diff --git a/AUTHORS b/AUTHORS
index daf447d0..face792d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -651,6 +651,7 @@
 Kui Tan <tk1061178@gmail.com>
 Kunal Thakar <kunalt@gmail.com>
 Kushal Pisavadia <kushi.p@gmail.com>
+Kwanghee Lee <ekwange@gmail.com>
 Kwangho Shin <k_h.shin@samsung.com>
 Kyle Nahrgang <kpn24@drexel.edu>
 Kyle Plumadore <kyle.plumadore@amd.com>
@@ -1248,6 +1249,7 @@
 Youngsun Suh <zard17@gmail.com>
 Yuan-Pin Yu <yjames@uber.com>
 Yuhong Sha <yuhong.sha@samsung.com>
+Yuki Osaki <yuki.osaki7@gmail.com>
 Yuki Tsuchiya <Yuki.Tsuchiya@sony.com>
 Yumikiyo Osanai <yumios.art@gmail.com>
 Yunchao He <yunchao.he@intel.com>
diff --git a/DEPS b/DEPS
index 3d5727e..224c063f 100644
--- a/DEPS
+++ b/DEPS
@@ -245,15 +245,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'f30a620d88f55fb8caafb03a9bd44fb486ab025e',
+  'skia_revision': '83a7369ca643825cf96ec4dac04b16424cddafe6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'ce6b3d1aa2e927431c9dc8d613ab9b86904738b0',
+  'v8_revision': 'de1922284d32d3f426da2300b5cfac2f336c5565',
   # 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': 'dca434bdcdb97f89cbfd75e7f7699b44561ab9ab',
+  'angle_revision': 'cb4f441c801f07e06b4b1e908c1ac3d471c96e75',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -276,7 +276,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling googletest
   # and whatever else without interference from each other.
-  'googletest_revision': '97a467571a0f615a4d96e79e4399c43221ca1232',
+  'googletest_revision': '9a32aee22d771387c494be2d8519fbdf46a713b2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lighttpd
   # and whatever else without interference from each other.
@@ -312,7 +312,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '97da6b1e1c9db574e28d63e89fe83b23bbb22cef',
+  'catapult_revision': '76c77ba0be42ad1bb71e001722132d2ecbc5b729',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -320,7 +320,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'cd90705e54fb479bd1144dc2d911253ab0e246b1',
+  'devtools_frontend_revision': 'b2da5bb3587caa9d9b6ba67a9094d6687a64a8f0',
   # 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.
@@ -360,7 +360,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': '36e86ee778ee8bd85a23cfbf10a4552cebf258a3',
+  'dawn_revision': '1fa386cc87bc7460123ef27432b3d0d328037b1f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -722,7 +722,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': 'VIopTUbBMZKsOBpN8Pq1YH1iKJa8bgbQI9gh9OqhJRcC',
+          'version': 'C-czXu-pxmAURdKch8StcG1uhXzNhsQ0F9RR9Z36X2QC',
         },
       ],
       'dep_type': 'cipd',
@@ -733,7 +733,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'e9DLUp4VJZFhTZ4zDsJ_ZnJK9tfLZtQwHWQbxt8y160C',
+          'version': '2RejVbrrCM1lYJ2MLFl1pOk2WEtQgb2ZcG7AcQhssn0C',
         },
       ],
       'dep_type': 'cipd',
@@ -744,7 +744,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/windows-amd64',
-          'version': 'rnznfneUyVfE8Wa2qmh433kAYViXt2xwrO069_lCZGgC',
+          'version': 'lrYaQb8DwD434vPhfLx_Vyf_5Ry2N2AhjvB7Ff8YtfgC',
         },
       ],
       'dep_type': 'cipd',
@@ -805,7 +805,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'xkgx7AMF4DNVvZHw7WxcsMH6nWaWr-bT2ygkaaCUsp8C',
+          'version': 'vmWZ1WUPgUEe8dpm2UVl-azbGg4DDN1As69u4IhVXpgC',
       },
     ],
     'condition': 'checkout_android',
@@ -1024,7 +1024,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '84f32e1f62f14040dabf0ec9175060a008e01842',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '38872ea469667a1a3fbb4a48fa8df3f76aaee0a3',
       'condition': 'checkout_chromeos',
   },
 
@@ -1096,7 +1096,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' + '@' + 'da47e8823754d12f1dd643bc911e80703b557ade',
+      'url': Var('chromium_git') + '/external/github.com/grpc/grpc.git' + '@' + '47f58f5b8d338a6861febc8ee1a602c23a5af70a',
   },
 
   'src/third_party/freetype/src':
@@ -1319,7 +1319,7 @@
     Var('chromium_git') + '/webm/libwebm.git' + '@' + 'e4fbea0c9751ae8aa86629b197a28d8276a2b0da',
 
   'src/third_party/libyuv':
-    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + 'fdc71956bdade0012b0628b5011b567c06c72308',
+    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '78625492cb0ff43faebbb6cb6db2209cd4ccb785',
 
   'src/third_party/lighttpd': {
       'url': Var('chromium_git') + '/chromium/deps/lighttpd.git' + '@' + Var('lighttpd_revision'),
@@ -1427,7 +1427,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'cefb3e0ec3a0580c996f801e854fe02963c03d5c',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '183e582aa736806e0d9dd6f16afbb7ce866123e3',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1648,7 +1648,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'c843f8d63c8c17acfbb7d48e09059a581ba779b9',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '977234879de72cb9df9b191adc9c7520c5c51c35',
+    Var('webrtc_git') + '/src.git' + '@' + 'c561d0abbb91bed88514927ac62750babfd5018f',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1730,15 +1730,26 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@2151e544b52ee350395e053051b8b31e6ca2c676',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@20c1c98375145935f9a87af9ff70c0cdf41bcfd0',
     'condition': 'checkout_src_internal',
   },
 
+  'src/ash/ambient/resources': {
+    'packages': [
+      {
+        'package': 'chromeos_internal/assistant/ambient',
+        'version': 'S8Sbm_BTIBcLqO_bt-UOT9sRnf1LLo9hTbqn5giOGsQC',
+      },
+    ],
+    'condition': 'checkout_chromeos and checkout_src_internal',
+    'dep_type': 'cipd',
+  },
+
   'src/ash/webui/eche_app_ui/resources/prod': {
     'packages': [
       {
         'package': 'chromeos_internal/apps/eche_app/app',
-        'version': 'WVI-jf6AkhBZ5jLCWhd2hq_PEjz-4exfcw5PuzXMoKwC',
+        'version': 'ugRcbrMmxieF8SVUc3ctVICZt5Y3ZMAKPxJNnTCMJJsC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1749,7 +1760,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': '4sRnYvRbVWePmLRR-Az6arAFrfyMkNlo5I91XAKsp0sC',
+        'version': 'LjmMzuPl7YyrorspNfj46P5WAmsbu-TWRF4kmQjTXgwC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1760,7 +1771,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'nk9g7zIFRz3dNcMrthrb5U48VG7EVXbVtKv4hn3qOdkC',
+        'version': 'o7aj4NazbpVH0oPzlh9jX3q8PBm5OHjxJDtp00kI8rMC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1771,7 +1782,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'x1kS0cEcNLFfZasaj0TFIsh7_DH8-9vJV_eGaTIlX-wC',
+        'version': 'zfwaRXzeP6zfUlAaW7yy7alhAgTbbZ1HPS8b1fTpjiIC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/WATCHLISTS b/WATCHLISTS
index d5e3604f..647d435 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -2564,12 +2564,12 @@
                    'marq+watch@chromium.org'],
     'ios_clean': ['ios-reviews+clean@chromium.org',
                   'marq+scrutinize@chromium.org'],
-    'ios_credential_provider': ['javierrobles+watch@chromium.org'],
+    'ios_credential_provider': ['rkgibson+watch@chromium.org'],
     'ios_flags': ['noyau+watch@chromium.org'],
     'ios_showcase': ['ios-reviews+showcase@chromium.org',
                      'marq+watch@chromium.org'],
     'ios_web': ['ios-reviews+web@chromium.org'],
-    'ios_widget_kit': ['javierrobles+watch@chromium.org'],
+    'ios_widget_kit': ['rkgibson+watch@chromium.org'],
     'language': ['language-reviews@chromium.org'],
     'libaom': ['fgalligan@chromium.org',
                'jianj@chromium.org',
diff --git a/android_webview/OWNERS b/android_webview/OWNERS
index 717beb6..3bc5a7f 100644
--- a/android_webview/OWNERS
+++ b/android_webview/OWNERS
@@ -4,6 +4,7 @@
 ntfschr@chromium.org
 torne@chromium.org
 nator@chromium.org
+peter@chromium.org
 
 # Documentation changes:
 per-file *.md=file://android_webview/docs/OWNERS
diff --git a/android_webview/browser/aw_browser_terminator.cc b/android_webview/browser/aw_browser_terminator.cc
index b507ed4..e09adfcf0 100644
--- a/android_webview/browser/aw_browser_terminator.cc
+++ b/android_webview/browser/aw_browser_terminator.cc
@@ -44,8 +44,10 @@
   kJavaException = 0,
   kCrashNotHandled = 1,
   kKillNotHandled = 2,
-  kAllWebViewsHandled = 3,
-  kMaxValue = kAllWebViewsHandled,
+  // kAllWebViewsHandled = 3, // Deprecated: use kCrashHandled/kKillHandled
+  kCrashHandled = 4,
+  kKillHandled = 5,
+  kMaxValue = kKillHandled,
 };
 
 void GetJavaWebContentsForRenderProcess(
@@ -121,8 +123,13 @@
     }
   }
   // If we reached this point, it means the crash was handled for all WebViews.
-  base::UmaHistogramEnumeration(kRenderProcessGoneHistogramName,
-                                RenderProcessGoneResult::kAllWebViewsHandled);
+  if (crashed) {
+    base::UmaHistogramEnumeration(kRenderProcessGoneHistogramName,
+                                  RenderProcessGoneResult::kCrashHandled);
+  } else {
+    base::UmaHistogramEnumeration(kRenderProcessGoneHistogramName,
+                                  RenderProcessGoneResult::kKillHandled);
+  }
 
   // By this point we have moved the minidump to the crash directory, so it can
   // now be copied and uploaded.
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
index 5e96eac..0ecfab6 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
@@ -239,7 +239,7 @@
     }
 
     private static void recordWebViewApiCall(@ApiCall int sample) {
-        RecordHistogram.recordEnumeratedHistogram("WebView.ApiCall", sample, ApiCall.COUNT);
+        RecordHistogram.recordEnumeratedHistogram("Android.WebView.ApiCall", sample, ApiCall.COUNT);
     }
 
     // This does not touch any global / non-threadsafe state, but note that
diff --git a/android_webview/tools/update_cts.py b/android_webview/tools/update_cts.py
index 3a7034ea..81e754cc 100755
--- a/android_webview/tools/update_cts.py
+++ b/android_webview/tools/update_cts.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env vpython
+#!/usr/bin/env vpython3
 # Copyright 2020 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index eab3df6..f25beff 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -159,7 +159,9 @@
     "ambient/model/ambient_backend_model.cc",
     "ambient/model/ambient_backend_model.h",
     "ambient/model/ambient_backend_model_observer.h",
+    "ambient/model/ambient_photo_config.cc",
     "ambient/model/ambient_photo_config.h",
+    "ambient/model/ambient_slideshow_photo_config.cc",
     "ambient/model/ambient_slideshow_photo_config.h",
     "ambient/ui/ambient_background_image_view.cc",
     "ambient/ui/ambient_background_image_view.h",
@@ -1578,6 +1580,8 @@
     "system/unified/user_chooser_view.h",
     "system/update/update_notification_controller.cc",
     "system/update/update_notification_controller.h",
+    "system/usb_peripheral/usb_peripheral_notification_controller.cc",
+    "system/usb_peripheral/usb_peripheral_notification_controller.h",
     "system/user/login_status.cc",
     "system/user/login_status.h",
     "system/virtual_keyboard/virtual_keyboard_observer.h",
@@ -2594,6 +2598,7 @@
     "system/unified/unified_system_tray_unittest.cc",
     "system/unified/user_chooser_detailed_view_controller_unittest.cc",
     "system/update/update_notification_controller_unittest.cc",
+    "system/usb_peripheral/usb_peripheral_notification_controller_unittest.cc",
     "system/virtual_keyboard/virtual_keyboard_tray_unittest.cc",
     "test/ash_test_helper_unittest.cc",
     "test/ash_unittests.cc",
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index 23d244e1..15b9f195 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -1397,7 +1397,28 @@
   EXPECT_FALSE(tray->IsBubbleShown());
 }
 
+TEST_F(AcceleratorControllerTest,
+       GlobalAcceleratorsToggleProductivityLauncher) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kProductivityLauncher);
+
+  // Search key opens the launcher.
+  EXPECT_TRUE(ProcessInController(
+      ui::Accelerator(ui::VKEY_BROWSER_SEARCH, ui::EF_NONE)));
+  base::RunLoop().RunUntilIdle();
+  GetAppListTestHelper()->CheckVisibility(true);
+
+  // Search key again closes the launcher.
+  EXPECT_TRUE(ProcessInController(
+      ui::Accelerator(ui::VKEY_BROWSER_SEARCH, ui::EF_NONE)));
+  base::RunLoop().RunUntilIdle();
+  GetAppListTestHelper()->CheckVisibility(false);
+}
+
 TEST_F(AcceleratorControllerTest, GlobalAcceleratorsToggleAppListFullscreen) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(features::kProductivityLauncher);
+
   base::HistogramTester histogram_tester;
 
   int toggle_count_total = 0;
diff --git a/ash/accessibility/accessibility_controller_impl.cc b/ash/accessibility/accessibility_controller_impl.cc
index d73d3b43..ccacd4b 100644
--- a/ash/accessibility/accessibility_controller_impl.cc
+++ b/ash/accessibility/accessibility_controller_impl.cc
@@ -2055,7 +2055,7 @@
     return;
 
   std::string pref_key = PrefKeyForSwitchAccessCommand(command);
-  const base::DictionaryValue* key_codes_pref =
+  const base::Value* key_codes_pref =
       active_user_prefs_->GetDictionary(pref_key);
   std::map<int, std::set<std::string>> key_codes;
   for (const auto v : key_codes_pref->DictItems()) {
diff --git a/ash/ambient/ambient_controller.cc b/ash/ambient/ambient_controller.cc
index ce73a93..5656fbe 100644
--- a/ash/ambient/ambient_controller.cc
+++ b/ash/ambient/ambient_controller.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "ash/ambient/model/ambient_backend_model_observer.h"
+#include "ash/ambient/model/ambient_slideshow_photo_config.h"
 #include "ash/ambient/ui/ambient_container_view.h"
 #include "ash/ambient/ui/ambient_view_delegate.h"
 #include "ash/ambient/util/ambient_util.h"
@@ -571,7 +572,14 @@
 
     DCHECK(AmbientClient::Get());
     ambient_photo_controller_ = std::make_unique<AmbientPhotoController>(
-        *AmbientClient::Get(), access_token_controller_);
+        *AmbientClient::Get(), access_token_controller_,
+        // TODO(esum): Read a setting here and initialize the photo controller
+        // with the slideshow config or the animation config based on that
+        // setting. When this setting is available,
+        // ambient_photo_controller_unittest.cc must be updated with tests for
+        // the animation use case; it is too difficult to add tests without that
+        // setting.
+        CreateAmbientSlideshowPhotoConfig());
 
     ambient_ui_model_observer_.Observe(&ambient_ui_model_);
 
diff --git a/ash/ambient/ambient_photo_controller.cc b/ash/ambient/ambient_photo_controller.cc
index 45b4768..40aee97fa 100644
--- a/ash/ambient/ambient_photo_controller.cc
+++ b/ash/ambient/ambient_photo_controller.cc
@@ -13,7 +13,6 @@
 #include "ash/ambient/ambient_controller.h"
 #include "ash/ambient/ambient_photo_cache.h"
 #include "ash/ambient/model/ambient_backend_model.h"
-#include "ash/ambient/model/ambient_slideshow_photo_config.h"
 #include "ash/public/cpp/ambient/ambient_backend_controller.h"
 #include "ash/public/cpp/ambient/ambient_client.h"
 #include "ash/public/cpp/ambient/proto/photo_cache_entry.pb.h"
@@ -127,8 +126,9 @@
 
 AmbientPhotoController::AmbientPhotoController(
     AmbientClient& ambient_client,
-    AmbientAccessTokenController& access_token_controller)
-    : ambient_backend_model_(kAmbientSlideshowPhotoConfig),
+    AmbientAccessTokenController& access_token_controller,
+    AmbientPhotoConfig photo_config)
+    : ambient_backend_model_(std::move(photo_config)),
       fetch_topic_retry_backoff_(&kFetchTopicRetryBackoffPolicy),
       resume_fetch_image_backoff_(&kResumeFetchImageBackoffPolicy),
       photo_cache_(AmbientPhotoCache::Create(
@@ -143,6 +143,7 @@
           access_token_controller)),
       task_runner_(
           base::ThreadPool::CreateSequencedTaskRunner(GetTaskTraits())) {
+  DCHECK(!ambient_backend_model_.photo_config().refresh_topic_markers.empty());
   ambient_backend_model_observation_.Observe(&ambient_backend_model_);
   ScheduleFetchBackupImages();
 }
@@ -150,15 +151,22 @@
 AmbientPhotoController::~AmbientPhotoController() = default;
 
 void AmbientPhotoController::Init() {
+  state_ = State::kPreparingInitialTopicSets;
   topic_index_ = 0;
-  image_refresh_started_ = false;
   retries_to_read_from_cache_ = kMaxNumberOfCachedImages;
   backup_retries_to_read_from_cache_ = GetBackupPhotoUrls().size();
+  num_topics_prepared_ = 0;
 }
 
 void AmbientPhotoController::StartScreenUpdate() {
+  if (state_ != State::kInactive) {
+    DVLOG(3) << "AmbientPhotoController is already active. Ignoring "
+                "StartScreenUpdate().";
+    return;
+  }
+
   Init();
-  FetchTopics();
+  FetchTopics(FetchTopicRequestType::kOnDemand);
   FetchWeather();
   weather_refresh_timer_.Start(
       FROM_HERE, kWeatherRefreshInterval,
@@ -173,7 +181,8 @@
 }
 
 void AmbientPhotoController::StopScreenUpdate() {
-  photo_refresh_timer_.Stop();
+  state_ = State::kInactive;
+  fetch_topic_timer_.Stop();
   weather_refresh_timer_.Stop();
   fetch_topic_retry_backoff_.Reset();
   resume_fetch_image_backoff_.Reset();
@@ -181,24 +190,62 @@
   weak_factory_.InvalidateWeakPtrs();
 }
 
-void AmbientPhotoController::OnTopicsChanged() {
-  if (ambient_backend_model_.topics().size() < kMaxNumberOfCachedImages)
-    ScheduleFetchTopics(/*backoff=*/false);
+void AmbientPhotoController::OnMarkerHit(AmbientPhotoConfig::Marker marker) {
+  if (!ambient_backend_model_.photo_config().refresh_topic_markers.contains(
+          marker)) {
+    DVLOG(3) << "UI event " << marker
+             << " does not trigger a topic refresh. Ignoring...";
+    return;
+  }
 
-  if (!image_refresh_started_) {
-    image_refresh_started_ = true;
-    ScheduleRefreshImage();
+  DVLOG(3) << "UI event " << marker << " triggering topic refresh";
+  switch (state_) {
+    case State::kInactive:
+    case State::kPreparingInitialTopicSets:
+      // In these states, the UI shouldn't be active, so it it's unexpected for
+      // the controller to receive a UI event.
+      LOG(ERROR) << "Received unexpected UI marker " << marker << " in state "
+                 << state_;
+      break;
+    case State::kWaitingForNextMarker:
+      state_ = State::kPreparingNextTopicSet;
+      num_topics_prepared_ = 0;
+      StartPreparingNextTopic();
+      break;
+    case State::kPreparingNextTopicSet:
+      // The controller is still in the middle of preparing a topic from the
+      // previous set (i.e. waiting on a callback or timer to fire). Resetting
+      // |num_topics_prepared_| to 0 is enough, and the topic currently being
+      // prepared will count towards the next set.
+      num_topics_prepared_ = 0;
+      break;
   }
 }
 
-void AmbientPhotoController::FetchTopics() {
+void AmbientPhotoController::OnTopicsChanged() {
+  DVLOG(3) << __func__;
+  if (!HasModelReachedMaxTopicCapacity())
+    ScheduleFetchTopics(/*backoff=*/false);
+
+  // Only call FetchPhotoRawData() for on-demand fetches. If a scheduled topic
+  // fetch happens to occur during the PREPARING_INITIAL_TOPIC_SETS or
+  // PREPARING_NEXT_TOPIC_SET state and FetchPhotoRawData() is called, not only
+  // is that unnecessary but it could also result in the controller decoding
+  // multiple topics simultaneously, which is currently not supported.
+  if (latest_fetch_topic_request_type_ == FetchTopicRequestType::kOnDemand) {
+    FetchPhotoRawData();
+  }
+}
+
+void AmbientPhotoController::FetchTopics(FetchTopicRequestType request_type) {
+  fetch_topic_timer_.Stop();
   Shell::Get()
       ->ambient_controller()
       ->ambient_backend_controller()
       ->FetchScreenUpdateInfo(
           kTopicsBatchSize,
           base::BindOnce(&AmbientPhotoController::OnScreenUpdateInfoFetched,
-                         weak_factory_.GetWeakPtr()));
+                         weak_factory_.GetWeakPtr(), request_type));
 }
 
 void AmbientPhotoController::FetchWeather() {
@@ -222,18 +269,10 @@
   const base::TimeDelta delay =
       backoff ? fetch_topic_retry_backoff_.GetTimeUntilRelease()
               : kTopicFetchInterval;
-  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&AmbientPhotoController::FetchTopics,
-                     weak_factory_.GetWeakPtr()),
-      delay);
-}
-
-void AmbientPhotoController::ScheduleRefreshImage() {
-  photo_refresh_timer_.Start(
-      FROM_HERE, ambient_backend_model_.GetPhotoRefreshInterval(),
-      base::BindOnce(&AmbientPhotoController::FetchPhotoRawData,
-                     weak_factory_.GetWeakPtr()));
+  fetch_topic_timer_.Start(FROM_HERE, delay,
+                           base::BindOnce(&AmbientPhotoController::FetchTopics,
+                                          weak_factory_.GetWeakPtr(),
+                                          FetchTopicRequestType::kScheduled));
 }
 
 void AmbientPhotoController::ScheduleFetchBackupImages() {
@@ -282,6 +321,7 @@
 }
 
 void AmbientPhotoController::OnScreenUpdateInfoFetched(
+    FetchTopicRequestType request_type,
     const ash::ScreenUpdate& screen_update) {
   // It is possible that |screen_update| is an empty instance if fatal errors
   // happened during the fetch.
@@ -290,13 +330,13 @@
 
     fetch_topic_retry_backoff_.InformOfRequest(/*succeeded=*/false);
     ScheduleFetchTopics(/*backoff=*/true);
-    if (!image_refresh_started_) {
-      image_refresh_started_ = true;
-      ScheduleRefreshImage();
-    }
+
+    if (request_type == FetchTopicRequestType::kOnDemand)
+      FetchPhotoRawData();
     return;
   }
   fetch_topic_retry_backoff_.InformOfRequest(/*succeeded=*/true);
+  latest_fetch_topic_request_type_ = request_type;
   ambient_backend_model_.AppendTopics(screen_update.next_topics);
   StartDownloadingWeatherConditionIcon(screen_update.weather_info);
 }
@@ -313,6 +353,7 @@
   ResetImageData();
 
   if (topic) {
+    DVLOG(3) << "Downloading topic photos";
     ambient::Photo* photo = cache_entry_.mutable_primary_photo();
     photo->set_details(topic->details);
     photo->set_is_portrait(topic->is_portrait);
@@ -361,7 +402,6 @@
       if (ambient_backend_model_.ImageLoadingFailed() ||
           topic_index_ == ambient_backend_model_.topics().size()) {
         LOG(WARNING) << "Not attempting image refresh";
-        image_refresh_started_ = false;
         return;
       }
 
@@ -370,7 +410,7 @@
           resume_fetch_image_backoff_.GetTimeUntilRelease();
       base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
           FROM_HERE,
-          base::BindOnce(&AmbientPhotoController::ScheduleRefreshImage,
+          base::BindOnce(&AmbientPhotoController::FetchPhotoRawData,
                          weak_factory_.GetWeakPtr()),
           delay);
       return;
@@ -419,6 +459,7 @@
 }
 
 void AmbientPhotoController::OnAllPhotoRawDataDownloaded() {
+  DVLOG(3) << __func__;
   OnAllPhotoRawDataAvailable(/*from_downloading=*/true);
 }
 
@@ -499,6 +540,7 @@
 
 void AmbientPhotoController::OnAllPhotoDecoded(bool from_downloading,
                                                const std::string& hash) {
+  DVLOG(3) << __func__;
   if (image_.isNull()) {
     LOG(WARNING) << "Image decoding failed";
     if (from_downloading)
@@ -530,9 +572,43 @@
 
   ResetImageData();
 
+  // AddNextImage() can call out to observers, who can synchronously interact
+  // with the controller within their observer notification methods. So the
+  // internal |state_| should be updated before calling AddNextImage() so that
+  // it is consistent with the model.
+  State previous_state = state_;
+  size_t target_num_topics_to_prepare = 0;
+  switch (state_) {
+    case State::kInactive:
+    case State::kWaitingForNextMarker:
+      LOG(ERROR) << "Topic prepared when controller should be idle in state "
+                 << state_;
+      return;
+    case State::kPreparingInitialTopicSets:
+      target_num_topics_to_prepare =
+          ambient_backend_model_.photo_config().GetNumDecodedTopicsToBuffer();
+      break;
+    case State::kPreparingNextTopicSet:
+      target_num_topics_to_prepare =
+          ambient_backend_model_.photo_config().topic_set_size;
+      break;
+  }
+
+  ++num_topics_prepared_;
+  if (num_topics_prepared_ == target_num_topics_to_prepare)
+    state_ = State::kWaitingForNextMarker;
+
   ambient_backend_model_.AddNextImage(std::move(detailed_photo));
 
-  ScheduleRefreshImage();
+  if (previous_state == State::kPreparingInitialTopicSets &&
+      state_ == State::kWaitingForNextMarker) {
+    DCHECK(ambient_backend_model_.ImagesReady());
+  }
+
+  if (state_ == State::kPreparingInitialTopicSets ||
+      state_ == State::kPreparingNextTopicSet) {
+    StartPreparingNextTopic();
+  }
 }
 
 void AmbientPhotoController::StartDownloadingWeatherConditionIcon(
@@ -578,7 +654,7 @@
 }
 
 void AmbientPhotoController::FetchTopicsForTesting() {
-  FetchTopics();
+  FetchTopics(FetchTopicRequestType::kOnDemand);
 }
 
 void AmbientPhotoController::FetchImageForTesting() {
@@ -589,4 +665,36 @@
   FetchBackupImages();
 }
 
+bool AmbientPhotoController::HasModelReachedMaxTopicCapacity() const {
+  return ambient_backend_model_.topics().size() >= kMaxNumberOfCachedImages;
+}
+
+bool AmbientPhotoController::HasExhaustedAllTopicsInModel() const {
+  return topic_index_ == ambient_backend_model_.topics().size();
+}
+
+void AmbientPhotoController::StartPreparingNextTopic() {
+  DCHECK(state_ == State::kPreparingInitialTopicSets ||
+         state_ == State::kPreparingNextTopicSet);
+  if (HasExhaustedAllTopicsInModel() && !HasModelReachedMaxTopicCapacity()) {
+    FetchTopics(FetchTopicRequestType::kOnDemand);
+  } else {
+    FetchPhotoRawData();
+  }
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         AmbientPhotoController::State state) {
+  switch (state) {
+    case AmbientPhotoController::State::kInactive:
+      return os << "INACTIVE";
+    case AmbientPhotoController::State::kPreparingInitialTopicSets:
+      return os << "PREPARING_INITIAL_TOPIC_SETS";
+    case AmbientPhotoController::State::kWaitingForNextMarker:
+      return os << "WAITING_FOR_NEXT_MARKER";
+    case AmbientPhotoController::State::kPreparingNextTopicSet:
+      return os << "PREPARING_NEXT_TOPIC_SET";
+  }
+}
+
 }  // namespace ash
diff --git a/ash/ambient/ambient_photo_controller.h b/ash/ambient/ambient_photo_controller.h
index a55e5f35..7a79d3e6 100644
--- a/ash/ambient/ambient_photo_controller.h
+++ b/ash/ambient/ambient_photo_controller.h
@@ -6,6 +6,7 @@
 #define ASH_AMBIENT_AMBIENT_PHOTO_CONTROLLER_H_
 
 #include <memory>
+#include <ostream>
 #include <string>
 #include <utility>
 
@@ -13,6 +14,8 @@
 #include "ash/ambient/ambient_photo_cache.h"
 #include "ash/ambient/model/ambient_backend_model.h"
 #include "ash/ambient/model/ambient_backend_model_observer.h"
+#include "ash/ambient/model/ambient_photo_config.h"
+#include "ash/ambient/ui/ambient_view_delegate.h"
 #include "ash/ash_export.h"
 #include "ash/public/cpp/ambient/ambient_backend_controller.h"
 #include "ash/public/cpp/ambient/proto/photo_cache_entry.pb.h"
@@ -35,10 +38,83 @@
 class AmbientAccessTokenController;
 
 // Class to handle photos in ambient mode.
-class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver {
+//
+// Terminology:
+//
+// Topic - A primary and optional related photo specified by the IMAX server.
+//
+// Fetch Topics - Request new topics from the IMAX server. After they're
+//                fetched, the controller just has urls for the primary/optional
+//                photos in each returned topic.
+//
+// Download Topic - Download the encoded primary/related photos from their
+//                  corresponding urls.
+//
+// Save Topic - Write the topic's encoded photos to disk for future re-use.
+//              Helpful in future cases where ambient mode starts and there's no
+//              internet.
+//
+// Load Topic - Read a previously saved topic's encoded photos from disk.
+//
+// Decode Topic - Decode the topic's photos and commit them to the
+//                AmbientBackendModel.
+//
+// Prepare Topic - A term that aggregates all of the steps above:
+// 1) Either a) fetch/download/save new topic or b) load existing topic
+// 2) Decode topic and commit to the model.
+//
+// Topic Set - A group of topics for the UI to display in one cycle, capped by
+// |AmbientPhotoConfig.topic_set_size|.
+//
+// The controller's state machine:
+//
+//        INACTIVE
+//           |
+//           |
+//           v
+// PREPARING_INITIAL_TOPIC_SETS
+//           |
+//           |
+//           v
+// WAITING_FOR_NEXT_MARKER <-----
+//           |                  |
+//           |                  |
+//           v                  |
+// PREPARING_NEXT_TOPIC_SET -----
+//
+// INACTIVE:
+// The controller is idle, and the model has no decoded topics in it. This is
+// the initial state when the controller is constructed. Although not
+// illustrated above, the controller can transition to this state from any of
+// the other states via a call to StopScreenUpdate().
+//
+// PREPARING_INITIAL_TOPIC_SETS:
+// At this point, the UI has not started rendering yet, and the controller is
+// preparing initial sets of topics. The AmbientPhotoConfig dictates how many
+// sets to prepare initially. This state is triggered by a call to
+// StartScreenUpdate(). It is complete when the desired number of initial topic
+// sets have been prepared.
+//
+// WAITING_FOR_NEXT_MARKER:
+// The UI is rendering the decoded topics currently in the model, and the
+// controller is idle. It's waiting for the right marker(s) to be hit in the UI
+// before it becomes active and starts preparing the next set of topics.
+//
+// PREPARING_NEXT_TOPIC_SET (a.k.a. "refreshing" the model's topics):
+// A target marker has been hit, and the controller immediately starts preparing
+// the next set of topics. Unlike the PREPARING_INITIAL_TOPIC_SETS state, there
+// is only ever 1 topic set prepared in this state, and the UI is rendering
+// while the topics are being prepared. After the topic set is completely
+// prepared, the controller goes back to WAITING_FOR_NEXT_MARKER. If another
+// target marker is received while the controller is still preparing a topic
+// set, the controller will simply reset its internal "counter" to 0 and start
+// preparing a brand new set.
+class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver,
+                                          public AmbientViewEventHandler {
  public:
   AmbientPhotoController(AmbientClient& ambient_client,
-                         AmbientAccessTokenController& access_token_controller);
+                         AmbientAccessTokenController& access_token_controller,
+                         AmbientPhotoConfig photo_config);
 
   AmbientPhotoController(const AmbientPhotoController&) = delete;
   AmbientPhotoController& operator=(const AmbientPhotoController&) = delete;
@@ -47,9 +123,7 @@
 
   // Start/stop updating the screen contents.
   // We need different logics to update photos and weather info because they
-  // have different refreshing intervals. Currently we only update weather info
-  // one time when entering ambient mode. Photos will be refreshed every
-  // |kPhotoRefreshInterval|.
+  // have different refreshing intervals.
   void StartScreenUpdate();
   void StopScreenUpdate();
 
@@ -57,10 +131,6 @@
     return &ambient_backend_model_;
   }
 
-  const base::OneShotTimer& photo_refresh_timer_for_testing() const {
-    return photo_refresh_timer_;
-  }
-
   base::OneShotTimer& backup_photo_refresh_timer_for_testing() {
     return backup_photo_refresh_timer_;
   }
@@ -68,24 +138,58 @@
   // AmbientBackendModelObserver:
   void OnTopicsChanged() override;
 
+  // AmbientViewEventHandler:
+  void OnMarkerHit(AmbientPhotoConfig::Marker marker) override;
+
   // Clear cache when Settings changes.
   void ClearCache();
 
  private:
+  enum class State {
+    kInactive,
+    kPreparingInitialTopicSets,
+    kWaitingForNextMarker,
+    kPreparingNextTopicSet
+  };
+
+  // Describes the 2 cases for when new topics are fetched from the IMAX sever.
+  enum class FetchTopicRequestType {
+    // The controller is in the PREPARING_INITIAL_TOPIC_SETS or
+    // PREPARING_NEXT_TOPIC_SET state and hence, there is an immediate demand to
+    // prepare more topics. If it has exhausted all of the existing topics in
+    // the model and the model has capacity to store more topics, the controller
+    // will fetch a new set of topics and will immediately download/save/decode
+    // topics from the new set afterwards.
+    kOnDemand,
+    // The controller can be in any state other than INACTIVE. It will fetch a
+    // new set of topics if the |kTopicFetchInterval| has elapsed since the last
+    // topic fetch completed, regardless of that fetch's request type. This is
+    // done to guarantee that a sufficient amount of topics are available in the
+    // model before the access token required to fetch new topics from the
+    // server expires. In other words, a topic fetch is guaranteed to occur at
+    // least every |kTopicFetchInterval| seconds until
+    // |kMaxNumberOfCachedImages| topics are available in the model.
+    //
+    // Unlike an on-demand fetch, when a scheduled topic fetch completes, the
+    // controller does *not* download/save/decode the topics from the new set
+    // immediately after, regardless of the its state. The fetched topics are
+    // only added to the model for future use.
+    kScheduled
+  };
+
   friend class AmbientAshTestBase;
   friend class AmbientPhotoControllerTest;
+  friend std::ostream& operator<<(std::ostream& os, State state);
 
   // Initialize variables.
   void Init();
 
-  void FetchTopics();
+  void FetchTopics(FetchTopicRequestType request_type);
 
   void FetchWeather();
 
   void ScheduleFetchTopics(bool backoff);
 
-  void ScheduleRefreshImage();
-
   void ScheduleFetchBackupImages();
 
   // Download backup cache images.
@@ -99,7 +203,8 @@
   // Return nullptr when need to read from disk cache.
   const AmbientModeTopic* GetNextTopic();
 
-  void OnScreenUpdateInfoFetched(const ash::ScreenUpdate& screen_update);
+  void OnScreenUpdateInfoFetched(FetchTopicRequestType request_type,
+                                 const ash::ScreenUpdate& screen_update);
 
   // Clear temporary image data to prepare next photos.
   void ResetImageData();
@@ -166,10 +271,18 @@
 
   void FetchBackupImagesForTesting();
 
-  AmbientBackendModel ambient_backend_model_;
+  // Whether or not the controller has iterated through all of the topics that
+  // are currently available in the model.
+  bool HasExhaustedAllTopicsInModel() const;
 
-  // The timer to refresh photos.
-  base::OneShotTimer photo_refresh_timer_;
+  // Whether or not the model has any more room to store new topics fetched
+  // from the server.
+  bool HasModelReachedMaxTopicCapacity() const;
+
+  // Kicks off preparation of the next topic.
+  void StartPreparingNextTopic();
+
+  AmbientBackendModel ambient_backend_model_;
 
   // The timer to refresh backup cache photos.
   base::OneShotTimer backup_photo_refresh_timer_;
@@ -177,6 +290,8 @@
   // The timer to refresh weather information.
   base::RepeatingTimer weather_refresh_timer_;
 
+  State state_ = State::kInactive;
+
   // The index of a topic to download.
   size_t topic_index_ = 0;
 
@@ -195,15 +310,14 @@
   // failures happen.
   int cache_index_for_store_ = 0;
 
-  // Whether the image refresh started or not.
-  bool image_refresh_started_ = false;
-
   // Cached image may not exist or valid. This is the max times of attempts to
   // read cached images.
   int retries_to_read_from_cache_ = kMaxNumberOfCachedImages;
 
   int backup_retries_to_read_from_cache_ = 0;
 
+  base::OneShotTimer fetch_topic_timer_;
+
   // Backoff for fetch topics retries.
   net::BackoffEntry fetch_topic_retry_backoff_;
 
@@ -223,6 +337,14 @@
   gfx::ImageSkia image_;
   gfx::ImageSkia related_image_;
 
+  // Tracks the number of topics that have been prepared since the controller
+  // last transitioned to either the PREPARING_INITIAL_TOPIC_SETS or
+  // PREPARING_NEXT_TOPIC_SET state.
+  size_t num_topics_prepared_ = 0;
+
+  // Transient variable. Type of the most recent successful topic fetch.
+  FetchTopicRequestType latest_fetch_topic_request_type_;
+
   base::WeakPtrFactory<AmbientPhotoController> weak_factory_{this};
 };
 
diff --git a/ash/ambient/ambient_photo_controller_unittest.cc b/ash/ambient/ambient_photo_controller_unittest.cc
index cef7ad6a..7eef69c9 100644
--- a/ash/ambient/ambient_photo_controller_unittest.cc
+++ b/ash/ambient/ambient_photo_controller_unittest.cc
@@ -30,6 +30,8 @@
 #include "base/scoped_observation.h"
 #include "base/system/sys_info.h"
 #include "base/test/bind.h"
+#include "base/test/scoped_run_loop_timeout.h"
+#include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/gfx/image/image_skia.h"
@@ -103,6 +105,44 @@
   }
 
   void Init() { photo_controller()->Init(); }
+
+  void RunUntilImagesReady() {
+    if (photo_controller()->ambient_backend_model()->ImagesReady())
+      return;
+
+    static constexpr base::TimeDelta kTimeout = base::Seconds(3);
+    base::test::ScopedRunLoopTimeout loop_timeout(FROM_HERE, kTimeout);
+    base::RunLoop loop;
+    base::RepeatingClosure quit_closure = loop.QuitClosure();
+    testing::NiceMock<MockAmbientBackendModelObserver> mock_backend_observer;
+    base::ScopedObservation<AmbientBackendModel, AmbientBackendModelObserver>
+        scoped_observation{&mock_backend_observer};
+    scoped_observation.Observe(photo_controller()->ambient_backend_model());
+    ON_CALL(mock_backend_observer, OnImagesReady)
+        .WillByDefault(
+            ::testing::Invoke([quit_closure]() { quit_closure.Run(); }));
+    loop.Run();
+  }
+
+  void RunUntilNextTopicsAdded(int num_expected_topics) {
+    static constexpr base::TimeDelta kTimeout = base::Seconds(3);
+    base::test::ScopedRunLoopTimeout loop_timeout(FROM_HERE, kTimeout);
+    base::RunLoop loop;
+    base::RepeatingClosure quit_closure = loop.QuitClosure();
+    int num_topics_added = 0;
+    testing::NiceMock<MockAmbientBackendModelObserver> mock_backend_observer;
+    base::ScopedObservation<AmbientBackendModel, AmbientBackendModelObserver>
+        scoped_observation{&mock_backend_observer};
+    scoped_observation.Observe(photo_controller()->ambient_backend_model());
+    ON_CALL(mock_backend_observer, OnImageAdded)
+        .WillByDefault(::testing::Invoke(
+            [quit_closure, num_expected_topics, &num_topics_added]() {
+              ++num_topics_added;
+              if (num_topics_added >= num_expected_topics)
+                quit_closure.Run();
+            }));
+    loop.Run();
+  }
 };
 
 // Test that topics are downloaded when starting screen update.
@@ -115,7 +155,7 @@
   topics = photo_controller()->ambient_backend_model()->topics();
   EXPECT_TRUE(topics.empty());
 
-  FastForwardToNextImage();
+  RunUntilImagesReady();
   topics = photo_controller()->ambient_backend_model()->topics();
   EXPECT_FALSE(topics.empty());
 
@@ -135,7 +175,7 @@
 
   // Start to refresh images.
   photo_controller()->StartScreenUpdate();
-  FastForwardToNextImage();
+  RunUntilImagesReady();
   photo_controller()->ambient_backend_model()->GetCurrentAndNextImages(
       /*current_image=*/nullptr,
       /*next_image=*/&image);
@@ -149,22 +189,23 @@
   EXPECT_TRUE(image.IsNull());
 }
 
-// Tests that photos are updated periodically when starting screen update.
-TEST_F(AmbientPhotoControllerTest, ShouldUpdatePhotoPeriodically) {
+// Tests that photos are updated when OnMarkerHit() is called.
+TEST_F(AmbientPhotoControllerTest, OnMarkerHitShouldUpdatePhoto) {
   PhotoWithDetails image1;
   PhotoWithDetails image2;
   PhotoWithDetails image3;
 
   // Start to refresh images.
   photo_controller()->StartScreenUpdate();
-  FastForwardToNextImage();
+  RunUntilImagesReady();
   photo_controller()->ambient_backend_model()->GetCurrentAndNextImages(
       /*current_image=*/nullptr,
       /*next_image=*/&image1);
   EXPECT_FALSE(image1.IsNull());
   EXPECT_TRUE(image2.IsNull());
 
-  FastForwardToNextImage();
+  photo_controller()->OnMarkerHit(AmbientPhotoConfig::Marker::kUiCycleEnded);
+  RunUntilNextTopicsAdded(/*num_expected_topics=*/1);
   photo_controller()->ambient_backend_model()->GetCurrentAndNextImages(
       /*current_image=*/nullptr,
       /*next_image=*/&image2);
@@ -172,7 +213,8 @@
   EXPECT_FALSE(image1.photo.BackedBySameObjectAs(image2.photo));
   EXPECT_TRUE(image3.IsNull());
 
-  FastForwardToNextImage();
+  photo_controller()->OnMarkerHit(AmbientPhotoConfig::Marker::kUiCycleEnded);
+  RunUntilNextTopicsAdded(/*num_expected_topics=*/1);
   photo_controller()->ambient_backend_model()->GetCurrentAndNextImages(
       /*current_image=*/nullptr,
       /*next_image=*/&image3);
@@ -184,12 +226,41 @@
   photo_controller()->StopScreenUpdate();
 }
 
+TEST_F(AmbientPhotoControllerTest,
+       ShouldLoadSavedTopicsFromDiskWithoutInternet) {
+  // Start ambient mode and run until ImagesReady(). At this point, the
+  // controller should have saved 2 topics to disk.
+  PhotoWithDetails image;
+  photo_controller()->StartScreenUpdate();
+  RunUntilImagesReady();
+  photo_controller()->ambient_backend_model()->GetCurrentAndNextImages(
+      /*current_image=*/nullptr,
+      /*next_image=*/&image);
+  ASSERT_FALSE(image.IsNull());
+
+  // Stop ambient mode. That should clear the decoded topics in the model but
+  // not clear the saved topics on disk.
+  photo_controller()->StopScreenUpdate();
+
+  // Simulate internet connection down.
+  backend_controller()->SetFetchScreenUpdateInfoResponseSize(
+      /*num_topics_to_return=*/0);
+
+  // Restart ambient mode, and it should load previously saved topics from disk.
+  photo_controller()->StartScreenUpdate();
+  RunUntilImagesReady();
+  photo_controller()->ambient_backend_model()->GetCurrentAndNextImages(
+      /*current_image=*/nullptr,
+      /*next_image=*/&image);
+  EXPECT_FALSE(image.IsNull());
+}
+
 // Tests that image details is correctly set.
 TEST_F(AmbientPhotoControllerTest, ShouldSetDetailsCorrectly) {
   SetPhotoOrientation(/*portrait=*/true);
   // Start to refresh images.
   photo_controller()->StartScreenUpdate();
-  FastForwardToNextImage();
+  RunUntilImagesReady();
   PhotoWithDetails image;
   photo_controller()->ambient_backend_model()->GetCurrentAndNextImages(
       /*current_image=*/nullptr,
@@ -207,9 +278,12 @@
 TEST_F(AmbientPhotoControllerTest, ShouldSaveImagesOnDisk) {
   // Start to refresh images. It will download two images immediately and write
   // them in |ambient_image_path|. It will also download one more image after
-  // fast forward. It will also download the related images and not cache them.
+  // OnMarkerHit(). It will also download the related images and not cache
+  // them.
   photo_controller()->StartScreenUpdate();
-  FastForwardToNextImage();
+  RunUntilImagesReady();
+  photo_controller()->OnMarkerHit(AmbientPhotoConfig::Marker::kUiCycleEnded);
+  RunUntilNextTopicsAdded(/*num_expected_topics=*/1);
 
   // Count number of writes to cache. There should be three cache writes during
   // this ambient mode session.
@@ -221,9 +295,12 @@
 TEST_F(AmbientPhotoControllerTest, ShouldNotDeleteImagesOnDisk) {
   // Start to refresh images. It will download two images immediately and write
   // them in |ambient_image_path|. It will also download one more image after
-  // fast forward. It will also download the related images and not cache them.
+  // OnMarkerHit(). It will also download the related images and not cache
+  // them.
   photo_controller()->StartScreenUpdate();
-  FastForwardToNextImage();
+  RunUntilImagesReady();
+  photo_controller()->OnMarkerHit(AmbientPhotoConfig::Marker::kUiCycleEnded);
+  RunUntilNextTopicsAdded(/*num_expected_topics=*/1);
 
   EXPECT_EQ(GetSavedCacheIndices().size(), 3u);
 
@@ -263,7 +340,7 @@
   // Reset variables in photo controller.
   Init();
   FetchImage();
-  FastForwardToNextImage();
+  RunUntilNextTopicsAdded(/*num_expected_topics=*/1);
   photo_controller()->ambient_backend_model()->GetCurrentAndNextImages(
       /*current_image=*/&image,
       /*next_image=*/nullptr);
@@ -290,7 +367,7 @@
   // Reset variables in photo controller.
   Init();
   FetchImage();
-  FastForwardToNextImage();
+  RunUntilNextTopicsAdded(/*num_expected_topics=*/1);
   photo_controller()->ambient_backend_model()->GetCurrentAndNextImages(
       /*current_image=*/&image,
       /*next_image=*/nullptr);
@@ -345,7 +422,7 @@
   // Reset variables in photo controller.
   Init();
   FetchImage();
-  FastForwardToNextImage();
+  RunUntilNextTopicsAdded(/*num_expected_topics=*/1);
   photo_controller()->ambient_backend_model()->GetCurrentAndNextImages(
       /*current_image=*/&image,
       /*next_image=*/nullptr);
@@ -355,6 +432,7 @@
 
 // Test that image is read from disk when image decoding failed.
 TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDecodingFailed) {
+  Init();
   SetDecodePhotoImage(gfx::ImageSkia());
   FetchTopics();
   // Forward a little bit time. FetchTopics() will succeed.
@@ -370,6 +448,7 @@
 
 // Test that image will refresh when have more topics.
 TEST_F(AmbientPhotoControllerTest, ShouldResumWhenHaveMoreTopics) {
+  Init();
   FetchImage();
   FastForwardToNextImage();
   // Topics is empty. Will read from cache, which is empty.
@@ -477,8 +556,7 @@
   SetDownloadPhotoData("image data");
 
   photo_controller()->StartScreenUpdate();
-  // Run the clock so the first photo is loaded.
-  FastForwardTiny();
+  RunUntilNextTopicsAdded(/*num_expected_topics=*/1);
 
   // Should contain hash of downloaded data.
   EXPECT_TRUE(photo_controller()->ambient_backend_model()->IsHashDuplicate(
@@ -489,7 +567,7 @@
   // Now expect a call because second image is loaded.
   EXPECT_CALL(mock_backend_observer, OnImagesReady).Times(1);
   SetDownloadPhotoData("image data 2");
-  FastForwardToNextImage();
+  RunUntilImagesReady();
 
   // Second image should have been loaded.
   EXPECT_TRUE(photo_controller()->ambient_backend_model()->IsHashDuplicate(
diff --git a/ash/ambient/ambient_view_delegate_impl.cc b/ash/ambient/ambient_view_delegate_impl.cc
index 569b199..bb69c02 100644
--- a/ash/ambient/ambient_view_delegate_impl.cc
+++ b/ash/ambient/ambient_view_delegate_impl.cc
@@ -7,6 +7,7 @@
 #include "ash/ambient/ambient_controller.h"
 #include "ash/ambient/model/ambient_backend_model.h"
 #include "base/bind.h"
+#include "base/check.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 
 namespace ash {
@@ -31,6 +32,13 @@
   return ambient_controller_->GetAmbientBackendModel();
 }
 
+AmbientViewEventHandler* AmbientViewDelegateImpl::GetAmbientViewEventHandler() {
+  AmbientViewEventHandler* event_handler =
+      ambient_controller_->ambient_photo_controller();
+  DCHECK(event_handler);
+  return event_handler;
+}
+
 void AmbientViewDelegateImpl::OnPhotoTransitionAnimationCompleted() {
   for (auto& observer : view_delegate_observers_)
     observer.OnPhotoTransitionAnimationCompleted();
diff --git a/ash/ambient/ambient_view_delegate_impl.h b/ash/ambient/ambient_view_delegate_impl.h
index a969cb1..6bef2a3 100644
--- a/ash/ambient/ambient_view_delegate_impl.h
+++ b/ash/ambient/ambient_view_delegate_impl.h
@@ -23,6 +23,7 @@
 
   // AmbientViewDelegate:
   AmbientBackendModel* GetAmbientBackendModel() override;
+  AmbientViewEventHandler* GetAmbientViewEventHandler() override;
   void OnPhotoTransitionAnimationCompleted() override;
   void AddObserver(AmbientViewDelegateObserver* observer) override;
   void RemoveObserver(AmbientViewDelegateObserver* observer) override;
diff --git a/ash/ambient/model/ambient_animation_photo_config.cc b/ash/ambient/model/ambient_animation_photo_config.cc
index b292a5f..f94f168 100644
--- a/ash/ambient/model/ambient_animation_photo_config.cc
+++ b/ash/ambient/model/ambient_animation_photo_config.cc
@@ -25,18 +25,24 @@
 
 ASH_EXPORT AmbientPhotoConfig CreateAmbientAnimationPhotoConfig(
     const cc::SkottieResourceMetadataMap& skottie_resource_metadata) {
-  return {
-      // Unlike the full screen slideshow screensaver, the animated screensaver
-      // has much smaller assets, and/so primary/related photos are never split
-      // within the same asset and are assigned to separate ones.
-      /*should_split_topics=*/true,
-      // Unlike the slideshow screensaver, the animated screensaver has
-      // motion/activity in it. So in the worst case scenario, we can repeat the
-      // animation cycle with the same set of image assets indefinitely and the
-      // screen won't burn. Hence, only 1 set of assets is required in the
-      // buffer.
-      /*num_decoded_topics_to_buffer=*/
-      GetNumDynamicAssetsInAnimation(skottie_resource_metadata)};
+  AmbientPhotoConfig config;
+  config.should_split_topics = true;
+  // Unlike the slideshow screensaver, the animated screensaver has
+  // motion/activity in it. So in the worst case scenario, we can repeat the
+  // animation cycle with the same set of image assets indefinitely and the
+  // screen won't burn. Hence, only 1 set of assets is required in the buffer.
+  config.num_topic_sets_to_buffer = 1;
+  config.topic_set_size =
+      GetNumDynamicAssetsInAnimation(skottie_resource_metadata);
+
+  // Once an animation cycle starts rendering (including the very first
+  // cycle), start preparing the next set of decoded topics for the next
+  // cycle. Unlike the slideshow view, this view waits until a new animation
+  // cycle starts, then pulls the most recent topics from the model at that
+  // time.
+  config.refresh_topic_markers = {AmbientPhotoConfig::Marker::kUiStartRendering,
+                                  AmbientPhotoConfig::Marker::kUiCycleEnded};
+  return config;
 }
 
 }  // namespace ash
diff --git a/ash/ambient/model/ambient_animation_photo_config_unittest.cc b/ash/ambient/model/ambient_animation_photo_config_unittest.cc
index a1c51446..6f232e0 100644
--- a/ash/ambient/model/ambient_animation_photo_config_unittest.cc
+++ b/ash/ambient/model/ambient_animation_photo_config_unittest.cc
@@ -13,22 +13,22 @@
 
 using ::testing::Eq;
 
-TEST(AmbientAnimationPhotoConfigTest, SetsNumDecodedTopicsToBuffer) {
+TEST(AmbientAnimationPhotoConfigTest, SetsTopicSetSize) {
   cc::SkottieResourceMetadataMap skottie_resource_metadata;
   EXPECT_THAT(CreateAmbientAnimationPhotoConfig(skottie_resource_metadata)
-                  .num_decoded_topics_to_buffer,
+                  .topic_set_size,
               Eq(0u));
   ASSERT_TRUE(skottie_resource_metadata.RegisterAsset(
       "test-resource-path", "test-resource-name-0",
       GenerateTestLottieDynamicAssetId(/*unique_id=*/0)));
   EXPECT_THAT(CreateAmbientAnimationPhotoConfig(skottie_resource_metadata)
-                  .num_decoded_topics_to_buffer,
+                  .topic_set_size,
               Eq(1u));
   ASSERT_TRUE(skottie_resource_metadata.RegisterAsset(
       "test-resource-path", "test-resource-name-1",
       GenerateTestLottieDynamicAssetId(/*unique_id=*/1)));
   EXPECT_THAT(CreateAmbientAnimationPhotoConfig(skottie_resource_metadata)
-                  .num_decoded_topics_to_buffer,
+                  .topic_set_size,
               Eq(2u));
 }
 
diff --git a/ash/ambient/model/ambient_backend_model.cc b/ash/ambient/model/ambient_backend_model.cc
index c1f4df8..b13bf75 100644
--- a/ash/ambient/model/ambient_backend_model.cc
+++ b/ash/ambient/model/ambient_backend_model.cc
@@ -132,7 +132,7 @@
 // AmbientBackendModel---------------------------------------------------------
 AmbientBackendModel::AmbientBackendModel(AmbientPhotoConfig photo_config)
     : photo_config_(std::move(photo_config)) {
-  DCHECK_GT(photo_config_.num_decoded_topics_to_buffer, 0u);
+  DCHECK_GT(photo_config_.GetNumDecodedTopicsToBuffer(), 0u);
 }
 
 AmbientBackendModel::~AmbientBackendModel() = default;
@@ -171,12 +171,12 @@
 
 bool AmbientBackendModel::ImagesReady() const {
   DCHECK_LE(all_decoded_topics_.size(),
-            photo_config_.num_decoded_topics_to_buffer);
+            photo_config_.GetNumDecodedTopicsToBuffer());
   // TODO(esum): Add a timeout (ex: 10 seconds) for the animated screensaver,
   // after which we just start the UI anyways if at least 1 topic is buffered
   // (duplicating that topic in the animation).
   return all_decoded_topics_.size() ==
-         photo_config_.num_decoded_topics_to_buffer;
+         photo_config_.GetNumDecodedTopicsToBuffer();
 }
 
 void AmbientBackendModel::AddNextImage(
@@ -191,7 +191,7 @@
 
   all_decoded_topics_.push_back(photo_with_details);
   while (all_decoded_topics_.size() >
-         photo_config_.num_decoded_topics_to_buffer) {
+         photo_config_.GetNumDecodedTopicsToBuffer()) {
     DCHECK(!all_decoded_topics_.empty());
     all_decoded_topics_.pop_front();
   }
diff --git a/ash/ambient/model/ambient_backend_model.h b/ash/ambient/model/ambient_backend_model.h
index f642124..89a3d3e 100644
--- a/ash/ambient/model/ambient_backend_model.h
+++ b/ash/ambient/model/ambient_backend_model.h
@@ -128,6 +128,8 @@
 
   bool show_celsius() const { return show_celsius_; }
 
+  const AmbientPhotoConfig& photo_config() const { return photo_config_; }
+
  private:
   friend class AmbientBackendModelTest;
   friend class AmbientAshTestBase;
diff --git a/ash/ambient/model/ambient_backend_model_unittest.cc b/ash/ambient/model/ambient_backend_model_unittest.cc
index b221cbf..7ea821b9 100644
--- a/ash/ambient/model/ambient_backend_model_unittest.cc
+++ b/ash/ambient/model/ambient_backend_model_unittest.cc
@@ -96,8 +96,8 @@
 
   void SetUp() override {
     AshTestBase::SetUp();
-    ambient_backend_model_ =
-        std::make_unique<AmbientBackendModel>(kAmbientSlideshowPhotoConfig);
+    ambient_backend_model_ = std::make_unique<AmbientBackendModel>(
+        CreateAmbientSlideshowPhotoConfig());
   }
 
   void TearDown() override {
diff --git a/ash/ambient/model/ambient_photo_config.cc b/ash/ambient/model/ambient_photo_config.cc
new file mode 100644
index 0000000..f1cb4a4
--- /dev/null
+++ b/ash/ambient/model/ambient_photo_config.cc
@@ -0,0 +1,28 @@
+// 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/ambient/model/ambient_photo_config.h"
+
+namespace ash {
+
+AmbientPhotoConfig::AmbientPhotoConfig() = default;
+
+AmbientPhotoConfig::AmbientPhotoConfig(const AmbientPhotoConfig& other) =
+    default;
+
+AmbientPhotoConfig& AmbientPhotoConfig::operator=(
+    const AmbientPhotoConfig& other) = default;
+
+AmbientPhotoConfig::~AmbientPhotoConfig() = default;
+
+std::ostream& operator<<(std::ostream& os, AmbientPhotoConfig::Marker marker) {
+  switch (marker) {
+    case AmbientPhotoConfig::Marker::kUiStartRendering:
+      return os << "UI_START_RENDERING";
+    case AmbientPhotoConfig::Marker::kUiCycleEnded:
+      return os << "UI_CYCLE_ENDED";
+  }
+}
+
+}  // namespace ash
diff --git a/ash/ambient/model/ambient_photo_config.h b/ash/ambient/model/ambient_photo_config.h
index 0d29f1e..3f8766af 100644
--- a/ash/ambient/model/ambient_photo_config.h
+++ b/ash/ambient/model/ambient_photo_config.h
@@ -6,14 +6,25 @@
 #define ASH_AMBIENT_MODEL_AMBIENT_PHOTO_CONFIG_H_
 
 #include <cstddef>
+#include <ostream>
 
 #include "ash/ash_export.h"
+#include "base/containers/flat_set.h"
 
 namespace ash {
 
 // Tells the photo model and controller requirements for how topics are handled
 // by the current UI.
 struct ASH_EXPORT AmbientPhotoConfig {
+  AmbientPhotoConfig();
+  AmbientPhotoConfig(const AmbientPhotoConfig& other);
+  AmbientPhotoConfig& operator=(const AmbientPhotoConfig& other);
+  ~AmbientPhotoConfig();
+
+  std::size_t GetNumDecodedTopicsToBuffer() const {
+    return num_topic_sets_to_buffer * topic_set_size;
+  }
+
   // If true, topics from the IMAX server containing a primary and related image
   // are always split into two topics, where the second topic's primary image
   // is set to the "related" image from the original paired topic. The client
@@ -23,9 +34,28 @@
   // If false, topics may contain a pair of primary and related photos.
   bool should_split_topics = false;
 
-  // How many decoded topics to keep buffered at any given time while rendering.
-  // Dictated by the number of topics the UI displays at any given time.
-  std::size_t num_decoded_topics_to_buffer = 0;
+  // How many sets of decoded topics to keep buffered at any given time while
+  // rendering, where the size of each set is |topic_set_size|.
+  std::size_t num_topic_sets_to_buffer = 0;
+
+  // The maximum number of topics that the UI will display at any given time.
+  std::size_t topic_set_size = 0;
+
+  // A marker is any time point of interest in the UI. Note all Ambient UIs have
+  // a time component to them. They are cyclic with a period T and progress
+  // linearly from time 0 to T. After a cycle completes, the UI loops back to
+  // time 0 again, repeating indefinitely.
+  enum class Marker {
+    // A one-time event when the UI first starts rendering (the start of the
+    // very first cycle).
+    kUiStartRendering,
+    // Marks the end of one UI cycle, and by extension, the start of another
+    // cycle.
+    kUiCycleEnded
+  };
+  // Markers at which a new set of topics should be decoded and written to the
+  // model, where the size of the new set is |topic_set_size|.
+  base::flat_set<Marker> refresh_topic_markers;
 
   // TODO(esum): Evaluate whether the following are needed:
   // * Max topics to cache - Since the animation photo config splits topics,
@@ -38,6 +68,8 @@
   //   single paired topic. It may need to prepare 2 topics at a time.
 };
 
+std::ostream& operator<<(std::ostream& os, AmbientPhotoConfig::Marker marker);
+
 }  // namespace ash
 
 #endif  // ASH_AMBIENT_MODEL_AMBIENT_PHOTO_CONFIG_H_
diff --git a/ash/ambient/model/ambient_slideshow_photo_config.cc b/ash/ambient/model/ambient_slideshow_photo_config.cc
new file mode 100644
index 0000000..b98fb66
--- /dev/null
+++ b/ash/ambient/model/ambient_slideshow_photo_config.cc
@@ -0,0 +1,32 @@
+// 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/ambient/model/ambient_slideshow_photo_config.h"
+
+namespace ash {
+
+AmbientPhotoConfig CreateAmbientSlideshowPhotoConfig() {
+  AmbientPhotoConfig config;
+  // The UI can render both the primary and related photo at the same time in
+  // certain cases (ex: 2 portrait photos displayed on the left and right
+  // halves of the screen).
+  config.should_split_topics = false;
+
+  // Always having 2 topics available prevents any chance of screen burn. In
+  // the worst case scenario that no more assets become available, the slideshow
+  // can alternate between the 2 topics indefinitely.
+  config.num_topic_sets_to_buffer = 2;
+  config.topic_set_size = 1;
+
+  // The view for this UI listens for when a new topic has been committed to
+  // the model and uses this as a signal to immediately update the UI. Thus,
+  // it only makes sense to refresh at the end of each cycle. Don't refresh
+  // at the UI_START_RENDERING mark, otherwise the UI will start rendering the
+  // initial topic, then immediately transition to another topic once the
+  // new one is prepared.
+  config.refresh_topic_markers = {AmbientPhotoConfig::Marker::kUiCycleEnded};
+  return config;
+}
+
+}  // namespace ash
diff --git a/ash/ambient/model/ambient_slideshow_photo_config.h b/ash/ambient/model/ambient_slideshow_photo_config.h
index 43accd4..13d69f1 100644
--- a/ash/ambient/model/ambient_slideshow_photo_config.h
+++ b/ash/ambient/model/ambient_slideshow_photo_config.h
@@ -6,21 +6,13 @@
 #define ASH_AMBIENT_MODEL_AMBIENT_SLIDESHOW_PHOTO_CONFIG_H_
 
 #include "ash/ambient/model/ambient_photo_config.h"
+#include "ash/ash_export.h"
 
 namespace ash {
 
 // For the UI that iterates through a slideshow of images and displays them at
 // full-screen resolution.
-constexpr AmbientPhotoConfig kAmbientSlideshowPhotoConfig = {
-    // The UI can render both the primary and related photo at the same time in
-    // certain cases (ex: 2 portrait photos displayed on the left and right
-    // halves of the screen).
-    /*should_split_topics=*/false,
-    // Always having 2 topics available prevents any chance of screen burn. In
-    // the worst case scenario that no more assets become available, the
-    // slideshow can alternate between the 2 topics indefinitely.
-    /*num_decoded_topics_to_buffer=*/2,
-};
+ASH_EXPORT AmbientPhotoConfig CreateAmbientSlideshowPhotoConfig();
 
 }  // namespace ash
 
diff --git a/ash/ambient/resources/.gitignore b/ash/ambient/resources/.gitignore
new file mode 100644
index 0000000..86884734
--- /dev/null
+++ b/ash/ambient/resources/.gitignore
@@ -0,0 +1,3 @@
+# This folder's resources is fetched from cipd via gclient, so they should not
+# be committed to the git repo.
+lottie
diff --git a/ash/ambient/resources/BUILD.gn b/ash/ambient/resources/BUILD.gn
new file mode 100644
index 0000000..33d14b6
--- /dev/null
+++ b/ash/ambient/resources/BUILD.gn
@@ -0,0 +1,45 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/cipd/cipd.gni")
+import("//build/config/chromeos/ui_mode.gni")
+import("//tools/grit/grit_rule.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+assert(is_chromeos_ash)
+
+lottie_resources_grd_file = "$target_gen_dir/lottie_resources.grd"
+
+generate_grd("build_lottie_grd") {
+  out_grd = lottie_resources_grd_file
+  grd_prefix = "ash_ambient_lottie"
+  input_files = [
+    "lottie/feel_the_breeze/animation.json",
+    "lottie/feel_the_breeze/bg.png",
+    "lottie/feel_the_breeze/clips_bottom.png",
+    "lottie/feel_the_breeze/clips_top.png",
+    "lottie/feel_the_breeze/shadow_1.png",
+    "lottie/feel_the_breeze/shadow_2.png",
+    "lottie/feel_the_breeze/shadow_3.png",
+    "lottie/feel_the_breeze/shadow_4.png",
+    "lottie/feel_the_breeze/shadow_5.png",
+    "lottie/feel_the_breeze/shadow_6.png",
+    "lottie/feel_the_breeze/time_weather.png",
+    "lottie/feel_the_breeze/tree_shadow.png",
+  ]
+  input_files_base_dir = rebase_path(".", "//")
+}
+
+grit("lottie_resources") {
+  # Required since the .grd file is generated at build time.
+  enable_input_discovery_for_gn_analyze = false
+  source = lottie_resources_grd_file
+  deps = [ ":build_lottie_grd" ]
+  outputs = [
+    "grit/ash_ambient_lottie_resources.h",
+    "grit/ash_ambient_lottie_resources_map.cc",
+    "grit/ash_ambient_lottie_resources_map.h",
+    "ash_ambient_lottie_resources.pak",
+  ]
+}
diff --git a/ash/ambient/resources/README b/ash/ambient/resources/README
new file mode 100644
index 0000000..c361ace
--- /dev/null
+++ b/ash/ambient/resources/README
@@ -0,0 +1,7 @@
+Contains ash resources for ambient mode. The main resources currently are Lottie
+animation files and their respective image assets for ash's animated
+screensaver. These resources reside in an internal CIPD package and are
+fetched via gclient only in chromium checkouts with chromeos/internal settings.
+
+The cipd.yaml file is not actually used at build time, but it is a checked in as
+a reference for when CIPD needs to be updated with new ambient resources.
diff --git a/ash/ambient/resources/cipd.yaml b/ash/ambient/resources/cipd.yaml
new file mode 100644
index 0000000..30294310
--- /dev/null
+++ b/ash/ambient/resources/cipd.yaml
@@ -0,0 +1,4 @@
+package: chromeos_internal/assistant/ambient
+description: Resources for ash ambient mode.
+data:
+  - dir: lottie
diff --git a/ash/ambient/resources/resources.gni b/ash/ambient/resources/resources.gni
new file mode 100644
index 0000000..28e5435
--- /dev/null
+++ b/ash/ambient/resources/resources.gni
@@ -0,0 +1,13 @@
+# 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.
+
+declare_args() {
+  # Temporary build flag to include resources for the animated screensaver in
+  # ash builds. The resources are still under development and there are plans to
+  # reduce their size since a few are pretty big. This flag prevents the main
+  # resource pak at HEAD from being impacted until the size issues are
+  # addressed, while still allowing developers to test this feature by
+  # overriding this flag to true.
+  include_ash_ambient_animation_resources = false
+}
diff --git a/ash/ambient/ui/ambient_view_delegate.h b/ash/ambient/ui/ambient_view_delegate.h
index 3047b50..1d08514 100644
--- a/ash/ambient/ui/ambient_view_delegate.h
+++ b/ash/ambient/ui/ambient_view_delegate.h
@@ -5,6 +5,7 @@
 #ifndef ASH_AMBIENT_UI_AMBIENT_VIEW_DELEGATE_H_
 #define ASH_AMBIENT_UI_AMBIENT_VIEW_DELEGATE_H_
 
+#include "ash/ambient/model/ambient_photo_config.h"
 #include "ash/ash_export.h"
 #include "base/observer_list_types.h"
 
@@ -18,6 +19,13 @@
   virtual void OnPhotoTransitionAnimationCompleted() = 0;
 };
 
+// Handles UI state changes from the currently rendering view. The events below
+// are common to all ambient UI modes.
+class AmbientViewEventHandler {
+ public:
+  virtual void OnMarkerHit(AmbientPhotoConfig::Marker marker) = 0;
+};
+
 class ASH_EXPORT AmbientViewDelegate {
  public:
   virtual ~AmbientViewDelegate() = default;
@@ -30,6 +38,8 @@
   // Ambient Mode.
   virtual AmbientBackendModel* GetAmbientBackendModel() = 0;
 
+  virtual AmbientViewEventHandler* GetAmbientViewEventHandler() = 0;
+
   // Invoked when the photo transition animation completed.
   virtual void OnPhotoTransitionAnimationCompleted() = 0;
 };
diff --git a/ash/ambient/ui/photo_view.cc b/ash/ambient/ui/photo_view.cc
index b2a0a5cb..66fe6af 100644
--- a/ash/ambient/ui/photo_view.cc
+++ b/ash/ambient/ui/photo_view.cc
@@ -13,6 +13,7 @@
 #include "ash/ambient/ui/ambient_background_image_view.h"
 #include "ash/ambient/ui/ambient_view_delegate.h"
 #include "ash/ambient/ui/ambient_view_ids.h"
+#include "ash/public/cpp/ambient/ambient_ui_model.h"
 #include "ash/public/cpp/metrics_util.h"
 #include "base/bind.h"
 #include "base/metrics/histogram_functions.h"
@@ -90,6 +91,8 @@
   model->GetCurrentAndNextImages(&current_image, &next_image);
   UpdateImage(current_image);
   UpdateImage(next_image);
+  delegate_->GetAmbientViewEventHandler()->OnMarkerHit(
+      AmbientPhotoConfig::Marker::kUiStartRendering);
 }
 
 void PhotoView::UpdateImage(const PhotoWithDetails& next_image) {
@@ -103,6 +106,14 @@
       ->UpdateImageDetails(base::UTF8ToUTF16(next_image.details),
                            base::UTF8ToUTF16(next_image.related_details));
   image_index_ = 1 - image_index_;
+  photo_refresh_timer_.Start(FROM_HERE,
+                             AmbientUiModel::Get()->photo_refresh_interval(),
+                             this, &PhotoView::OnImageCycleComplete);
+}
+
+void PhotoView::OnImageCycleComplete() {
+  delegate_->GetAmbientViewEventHandler()->OnMarkerHit(
+      AmbientPhotoConfig::Marker::kUiCycleEnded);
 }
 
 void PhotoView::StartTransitionAnimation() {
diff --git a/ash/ambient/ui/photo_view.h b/ash/ambient/ui/photo_view.h
index 51e9535..2811ab0 100644
--- a/ash/ambient/ui/photo_view.h
+++ b/ash/ambient/ui/photo_view.h
@@ -13,6 +13,7 @@
 #include "ash/ambient/ui/ambient_background_image_view.h"
 #include "ash/ash_export.h"
 #include "base/scoped_observation.h"
+#include "base/timer/timer.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/views/view.h"
 
@@ -50,6 +51,7 @@
   void Init();
 
   void UpdateImage(const PhotoWithDetails& image);
+  void OnImageCycleComplete();
 
   void StartTransitionAnimation();
 
@@ -68,6 +70,10 @@
   // The index of |image_views_| to update the next image.
   int image_index_ = 0;
 
+  // Fires when the next photo should be prepared, which ultimately leads to
+  // it being rendered in this view.
+  base::OneShotTimer photo_refresh_timer_;
+
   base::ScopedObservation<AmbientBackendModel, AmbientBackendModelObserver>
       scoped_backend_model_observer_{this};
 };
diff --git a/ash/ambient/ui/photo_view_unittest.cc b/ash/ambient/ui/photo_view_unittest.cc
index dbc1004d..d3eff77 100644
--- a/ash/ambient/ui/photo_view_unittest.cc
+++ b/ash/ambient/ui/photo_view_unittest.cc
@@ -13,12 +13,36 @@
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/test/display_manager_test_api.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/image/image_skia.h"
 #include "ui/views/controls/image_view.h"
 
 namespace ash {
 
 using AmbientPhotoViewTest = AmbientAshTestBase;
 
+// Test that a new topic s rendered every cycle.
+TEST_F(AmbientPhotoViewTest, ShouldRefreshImagesEveryCycle) {
+  UpdateDisplay("600x800");
+
+  ShowAmbientScreen();
+  gfx::ImageSkia image_1 = GetAmbientBackgroundImageView()->GetCurrentImage();
+  ASSERT_FALSE(image_1.isNull());
+
+  // It takes 2 cycles to refresh both AmbientBackgroundImageViews owned by the
+  // PhotoView, guaranteeing that the images for both should have changed.
+  FastForwardToNextImage();
+  FastForwardToNextImage();
+  gfx::ImageSkia image_2 = GetAmbientBackgroundImageView()->GetCurrentImage();
+  ASSERT_FALSE(image_2.isNull());
+  EXPECT_FALSE(image_2.BackedBySameObjectAs(image_1));
+
+  FastForwardToNextImage();
+  FastForwardToNextImage();
+  gfx::ImageSkia image_3 = GetAmbientBackgroundImageView()->GetCurrentImage();
+  ASSERT_FALSE(image_3.isNull());
+  EXPECT_FALSE(image_3.BackedBySameObjectAs(image_2));
+}
+
 // Test that image is scaled to fill screen width when image is portrait and
 // screen is portrait. The top and bottom of the image will be cut off, as
 // the height of the image is taller than the height of the screen.
diff --git a/ash/app_list/app_list_bubble_presenter.cc b/ash/app_list/app_list_bubble_presenter.cc
index 1278c09..49787e8 100644
--- a/ash/app_list/app_list_bubble_presenter.cc
+++ b/ash/app_list/app_list_bubble_presenter.cc
@@ -171,6 +171,7 @@
 
 void AppListBubblePresenter::Show(int64_t display_id) {
   DVLOG(1) << __PRETTY_FUNCTION__;
+  DCHECK(features::IsProductivityLauncherEnabled());
   if (is_target_visibility_show_)
     return;
 
@@ -261,6 +262,7 @@
 
 ShelfAction AppListBubblePresenter::Toggle(int64_t display_id) {
   DVLOG(1) << __PRETTY_FUNCTION__;
+  DCHECK(features::IsProductivityLauncherEnabled());
   if (is_target_visibility_show_) {
     Dismiss();
     return SHELF_ACTION_APP_LIST_DISMISSED;
@@ -271,6 +273,7 @@
 
 void AppListBubblePresenter::Dismiss() {
   DVLOG(1) << __PRETTY_FUNCTION__;
+  DCHECK(features::IsProductivityLauncherEnabled());
   if (!is_target_visibility_show_)
     return;
 
diff --git a/ash/app_list/app_list_color_provider_impl.cc b/ash/app_list/app_list_color_provider_impl.cc
index 1dcdce8..5f8b6e0 100644
--- a/ash/app_list/app_list_color_provider_impl.cc
+++ b/ash/app_list/app_list_color_provider_impl.cc
@@ -255,6 +255,15 @@
                      GetInkDropOpacity(GetSearchBoxBackgroundColor()) * 255);
 }
 
+SkColor AppListColorProviderImpl::GetTextColorURL() const {
+  // Use highlight colors when Dark Light mode is enabled.
+  if (ShouldUseDarkLightColors()) {
+    return AshColorProvider::Get()->GetContentLayerColor(
+        AshColorProvider::ContentLayerType::kTextColorURL);
+  }
+  return gfx::kGoogleBlue600;
+}
+
 bool AppListColorProviderImpl::ShouldUseDarkLightColors() const {
   return is_dark_light_mode_enabled_ || is_productivity_launcher_enabled_;
 }
diff --git a/ash/app_list/app_list_color_provider_impl.h b/ash/app_list/app_list_color_provider_impl.h
index d3d0581..1f9cb4cf 100644
--- a/ash/app_list/app_list_color_provider_impl.h
+++ b/ash/app_list/app_list_color_provider_impl.h
@@ -46,6 +46,7 @@
   float GetInkDropOpacity(
       SkColor bg_color = gfx::kPlaceholderColor) const override;
   SkColor GetSearchResultViewHighlightColor() const override;
+  SkColor GetTextColorURL() const override;
 
  private:
   bool ShouldUseDarkLightColors() const;
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index a013d75..2b10b42 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -271,10 +271,8 @@
 AppListControllerImpl::AppListControllerImpl()
     : model_provider_(std::make_unique<AppListModelProvider>()),
       fullscreen_presenter_(std::make_unique<AppListPresenterImpl>(this)),
+      bubble_presenter_(std::make_unique<AppListBubblePresenter>(this)),
       badge_controller_(std::make_unique<AppListBadgeController>()) {
-  if (features::IsProductivityLauncherEnabled())
-    bubble_presenter_ = std::make_unique<AppListBubblePresenter>(this);
-
   SessionControllerImpl* session_controller =
       Shell::Get()->session_controller();
   session_controller->AddObserver(this);
@@ -1083,8 +1081,10 @@
   keyboard_traversal_engaged_ = engaged;
 
   // No need to schedule paint for bubble presenter.
-  if (bubble_presenter_ && bubble_presenter_->IsShowing())
+  if (features::IsProductivityLauncherEnabled() &&
+      bubble_presenter_->IsShowing()) {
     return;
+  }
 
   AppListView* app_list_view = fullscreen_presenter_->GetView();
   // May be null in tests of bubble presenter.
@@ -1115,8 +1115,10 @@
 }
 
 bool AppListControllerImpl::IsShowingEmbeddedAssistantUI() const {
-  if (bubble_presenter_ && bubble_presenter_->IsShowingEmbeddedAssistantUI())
+  if (features::IsProductivityLauncherEnabled() &&
+      bubble_presenter_->IsShowingEmbeddedAssistantUI()) {
     return true;
+  }
   return fullscreen_presenter_->IsShowingEmbeddedAssistantUI();
 }
 
@@ -1549,6 +1551,10 @@
     client_->LoadIcon(profile_id_, app_id);
 }
 
+bool AppListControllerImpl::HasValidProfile() const {
+  return profile_id_ != kAppListInvalidProfileID;
+}
+
 void AppListControllerImpl::GetAppLaunchedMetricParams(
     AppLaunchedMetricParams* metric_params) {
   metric_params->app_list_view_state = GetAppListViewState();
@@ -1950,8 +1956,10 @@
   DCHECK(!is_shutdown_);
   is_shutdown_ = true;
 
-  if (bubble_presenter_)
-    bubble_presenter_->Shutdown();
+  // Always shutdown the bubble presenter, even if ProductivityLauncher is
+  // disabled, because tests might have temporarily enabled the feature and
+  // the widget needs to be closed.
+  bubble_presenter_->Shutdown();
 
   Shell* shell = Shell::Get();
   AssistantController::Get()->RemoveObserver(this);
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 7255b35f..d7f26ef 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -196,6 +196,7 @@
       bool was_animation_interrupted) override;
   int AdjustAppListViewScrollOffset(int offset, ui::EventType type) override;
   void LoadIcon(const std::string& app_id) override;
+  bool HasValidProfile() const override;
 
   void GetAppLaunchedMetricParams(
       AppLaunchedMetricParams* metric_params) override;
@@ -440,8 +441,9 @@
   // prevent a crash in destruction.
   std::unique_ptr<AppListPresenterImpl> fullscreen_presenter_;
 
-  // Manages the clamshell launcher bubble. Null when the feature AppListBubble
-  // is disabled.
+  // Manages the clamshell launcher bubble. Always exists, even when feature
+  // ProductivityLauncher is disabled, to allow unit tests to turn the feature
+  // on and off within the test body.
   std::unique_ptr<AppListBubblePresenter> bubble_presenter_;
 
   // Tracks the current page shown in the app list view (tracked for the
diff --git a/ash/app_list/app_list_test_view_delegate.cc b/ash/app_list/app_list_test_view_delegate.cc
index 29d59d7..3ff1cac 100644
--- a/ash/app_list/app_list_test_view_delegate.cc
+++ b/ash/app_list/app_list_test_view_delegate.cc
@@ -151,6 +151,10 @@
   return offset;
 }
 
+bool AppListTestViewDelegate::HasValidProfile() const {
+  return true;
+}
+
 void AppListTestViewDelegate::GetSearchResultContextMenuModel(
     const std::string& result_id,
     GetContextMenuModelCallback callback) {
diff --git a/ash/app_list/app_list_test_view_delegate.h b/ash/app_list/app_list_test_view_delegate.h
index 8b2d045f..7484df7 100644
--- a/ash/app_list/app_list_test_view_delegate.h
+++ b/ash/app_list/app_list_test_view_delegate.h
@@ -121,6 +121,7 @@
   AppListNotifier* GetNotifier() override;
   int AdjustAppListViewScrollOffset(int offset, ui::EventType type) override;
   void LoadIcon(const std::string& app_id) override {}
+  bool HasValidProfile() const override;
 
   // Do a bulk replacement of the items in the model.
   void ReplaceTestModel(int item_count);
diff --git a/ash/app_list/app_list_view_delegate.h b/ash/app_list/app_list_view_delegate.h
index af3c049..355ce10 100644
--- a/ash/app_list/app_list_view_delegate.h
+++ b/ash/app_list/app_list_view_delegate.h
@@ -208,6 +208,10 @@
 
   // Loads the icon of an app item identified by `app_id`.
   virtual void LoadIcon(const std::string& app_id) = 0;
+
+  // Whether the controller has a valid profile, and hence a valid data model.
+  // Returns false during startup and shutdown.
+  virtual bool HasValidProfile() const = 0;
 };
 
 }  // namespace ash
diff --git a/ash/app_list/views/search_result_page_view.cc b/ash/app_list/views/search_result_page_view.cc
index 8b49d52..0461d43 100644
--- a/ash/app_list/views/search_result_page_view.cc
+++ b/ash/app_list/views/search_result_page_view.cc
@@ -231,6 +231,8 @@
     AppListViewDelegate* view_delegate,
     AppListMainView* app_list_main_view,
     SearchBoxView* search_box_view) {
+  DCHECK(view_delegate);
+  view_delegate_ = view_delegate;
   dialog_controller_ = std::make_unique<SearchResultPageDialogController>(this);
 
   if (features::IsProductivityLauncherEnabled()) {
@@ -472,6 +474,10 @@
 }
 
 void SearchResultPageView::OnSearchResultContainerResultsChanged() {
+  // Skip updates during shutdown.
+  if (!view_delegate_->HasValidProfile())
+    return;
+
   // Result selection should be handled by |productivity_launcher_search_page_|.
   DCHECK(!features::IsProductivityLauncherEnabled());
   DCHECK(!result_container_views_.empty());
diff --git a/ash/app_list/views/search_result_page_view.h b/ash/app_list/views/search_result_page_view.h
index b0ad985..26c75fd 100644
--- a/ash/app_list/views/search_result_page_view.h
+++ b/ash/app_list/views/search_result_page_view.h
@@ -177,6 +177,8 @@
   void AddSearchResultContainerViewInternal(
       std::unique_ptr<SearchResultContainerView> result_container);
 
+  AppListViewDelegate* view_delegate_ = nullptr;
+
   // The SearchResultContainerViews that compose the search page. All owned by
   // the views hierarchy.
   std::vector<SearchResultContainerView*> result_container_views_;
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc
index 1332c06..e9f866b 100644
--- a/ash/app_list/views/search_result_view.cc
+++ b/ash/app_list/views/search_result_view.cc
@@ -363,8 +363,7 @@
   // Apply font color styles.
   label->SetEnabledColor(
       is_url
-          ? AshColorProvider::Get()->GetContentLayerColor(
-                AshColorProvider::ContentLayerType::kTextColorURL)
+          ? AppListColorProvider::Get()->GetTextColorURL()
           : is_title_label
                 ? AppListColorProvider::Get()->GetSearchBoxTextColor(
                       kDeprecatedSearchBoxTextDefaultColor)
diff --git a/ash/clipboard/clipboard_nudge_controller.cc b/ash/clipboard/clipboard_nudge_controller.cc
index 63236967..8407727 100644
--- a/ash/clipboard/clipboard_nudge_controller.cc
+++ b/ash/clipboard/clipboard_nudge_controller.cc
@@ -315,7 +315,7 @@
 }
 
 int ClipboardNudgeController::GetShownCount(PrefService* prefs) {
-  const base::DictionaryValue* dictionary =
+  const base::Value* dictionary =
       prefs->GetDictionary(prefs::kMultipasteNudges);
   if (!dictionary)
     return 0;
@@ -323,7 +323,7 @@
 }
 
 int ClipboardNudgeController::GetNewFeatureBadgeShownCount(PrefService* prefs) {
-  const base::DictionaryValue* dictionary =
+  const base::Value* dictionary =
       prefs->GetDictionary(prefs::kMultipasteNudges);
   if (!dictionary)
     return 0;
@@ -331,7 +331,7 @@
 }
 
 base::Time ClipboardNudgeController::GetLastShownTime(PrefService* prefs) {
-  const base::DictionaryValue* dictionary =
+  const base::Value* dictionary =
       prefs->GetDictionary(prefs::kMultipasteNudges);
   if (!dictionary)
     return base::Time();
diff --git a/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc b/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc
index 9bb9285..8f40d36 100644
--- a/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc
+++ b/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc
@@ -255,7 +255,7 @@
 }
 
 void ArcDataSnapshotdManager::Snapshot::Parse() {
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       local_state_->GetDictionary(arc::prefs::kArcSnapshotInfo);
   if (!dict)
     return;
diff --git a/ash/components/arc/metrics/stability_metrics_manager.cc b/ash/components/arc/metrics/stability_metrics_manager.cc
index c0bb20f..ace7f95 100644
--- a/ash/components/arc/metrics/stability_metrics_manager.cc
+++ b/ash/components/arc/metrics/stability_metrics_manager.cc
@@ -76,7 +76,7 @@
 
 absl::optional<bool> StabilityMetricsManager::GetArcEnabledState() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       local_state_->GetDictionary(prefs::kStabilityMetrics);
   return dict->FindBoolKey(kArcEnabledStateKey);
 }
@@ -90,7 +90,7 @@
 absl::optional<NativeBridgeType>
 StabilityMetricsManager::GetArcNativeBridgeType() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       local_state_->GetDictionary(prefs::kStabilityMetrics);
   absl::optional<int> native_bridge_type =
       dict->FindIntKey(kArcNativeBridgeTypeKey);
diff --git a/ash/components/arc/mojom/BUILD.gn b/ash/components/arc/mojom/BUILD.gn
index 7ed060e..14cf13f 100644
--- a/ash/components/arc/mojom/BUILD.gn
+++ b/ash/components/arc/mojom/BUILD.gn
@@ -54,6 +54,7 @@
     "power.mojom",
     "print_common.mojom",
     "print_spooler.mojom",
+    "privacy_items.mojom",
     "process.mojom",
     "property.mojom",
     "rotation_lock.mojom",
diff --git a/ash/components/arc/mojom/app_permissions.mojom b/ash/components/arc/mojom/app_permissions.mojom
index 0454b741..c44ef5df 100644
--- a/ash/components/arc/mojom/app_permissions.mojom
+++ b/ash/components/arc/mojom/app_permissions.mojom
@@ -18,6 +18,15 @@
   STORAGE         = 5, // android.manifest.WRITE_EXTERNAL_STORAGE and android.manifest.READ_EXTERNAL_STORAGE
 };
 
+// These permissions map to ones listed at
+// https://developer.android.com/reference/android/Manifest.permission_group
+[Extensible]
+enum AppPermissionGroup {
+  CAMERA          = 0, // android.permission-group.CAMERA
+  MICROPHONE      = 1, // android.permission-group.MICROPHONE
+  LOCATION        = 2, // android.permission-group.LOCATION
+};
+
 struct PermissionState {
   bool granted;          // If the permission has been granted
   bool managed;          // If the permission is managed by an enterprise policy
diff --git a/ash/components/arc/mojom/arc_bridge.mojom b/ash/components/arc/mojom/arc_bridge.mojom
index 76039ca..4b545097 100644
--- a/ash/components/arc/mojom/arc_bridge.mojom
+++ b/ash/components/arc/mojom/arc_bridge.mojom
@@ -47,6 +47,7 @@
 import "ash/components/arc/mojom/policy.mojom";
 import "ash/components/arc/mojom/power.mojom";
 import "ash/components/arc/mojom/print_spooler.mojom";
+import "ash/components/arc/mojom/privacy_items.mojom";
 import "ash/components/arc/mojom/process.mojom";
 import "ash/components/arc/mojom/property.mojom";
 import "ash/components/arc/mojom/rotation_lock.mojom";
@@ -64,9 +65,9 @@
 import "ash/components/arc/mojom/wallpaper.mojom";
 import "ash/components/arc/mojom/webapk.mojom";
 
-// Next MinVersion: 61
+// Next MinVersion: 62
 // Deprecated method IDs: 101, 105, 121
-// Next method ID: 166
+// Next method ID: 167
 interface ArcBridgeHost {
   // Keep the entries alphabetical. In order to do so without breaking
   // compatibility with the ARC instance, explicitly assign each interface a
@@ -242,6 +243,10 @@
   [MinVersion=45] OnPrintSpoolerInstanceReady@150(
       pending_remote<PrintSpoolerInstance> instance_remote);
 
+  // Notifies Chrome that the PrivacyItemsInstance interface is ready.
+  [MinVersion=61] OnPrivacyItemsInstanceReady@166(
+      pending_remote<PrivacyItemsInstance> instance_remote);
+
   // Notifies Chrome that the ProcessInstance interface is ready.
   OnProcessInstanceReady@104(pending_remote<ProcessInstance> instance_remote);
 
diff --git a/ash/components/arc/mojom/privacy_items.mojom b/ash/components/arc/mojom/privacy_items.mojom
new file mode 100644
index 0000000..78a2962
--- /dev/null
+++ b/ash/components/arc/mojom/privacy_items.mojom
@@ -0,0 +1,52 @@
+// 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.
+//
+// Next MinVersion: 1
+
+module arc.mojom;
+
+import "ash/components/arc/mojom/app_permissions.mojom";
+import "ash/components/arc/mojom/gfx.mojom";
+
+// Contains information about an application that is accessing resources
+// tracked by the privacy indicators.
+struct PrivacyApplication {
+  string package_name;
+  int32 uid;
+};
+
+// A record about an apps access to a resource tracked by the privacy
+// indicators.
+struct PrivacyItem {
+  // The type of resourced accessed by the application.
+  AppPermissionGroup permission_group;
+  // The application associated with this privacy item.
+  PrivacyApplication privacy_application;
+};
+
+// Interface for Android communicating to Ash.
+// Next Method ID: 3
+interface PrivacyItemsHost {
+  // Notifies Chrome that the privacy items accessed by applications have
+  // changed.
+  OnPrivacyItemsChanged@0(array<PrivacyItem> privacy_items);
+  // Notifies Chrome when the SystemUI flag that determines whether to show
+  // the privacy indicators for microphone + camera has changed.
+  // See frameworks/base/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+  OnMicCameraIndicatorRequirementChanged@1(bool flag);
+  // Notifies Chrome when the SystemUI flag that determines whether to show
+  // the privacy indicators for location has changed.
+  // See frameworks/base/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+  OnLocationIndicatorRequirementChanged@2(bool flag);
+};
+
+// Interface for communicating to Android.
+// Next Method ID: 2
+interface PrivacyItemsInstance {
+  // Establishes full-duplex communication with the host.
+  Init@0(pending_remote<PrivacyItemsHost> host_remote) => ();
+  // Tell android WM the maximum bounds of the indicators, regardless of
+  // whether they're being shown or not.
+  OnStaticPrivacyIndicatorBoundsChanged@1(int32 displayId, array<Rect> bounds);
+};
diff --git a/ash/components/arc/session/arc_bridge_host_impl.cc b/ash/components/arc/session/arc_bridge_host_impl.cc
index d9566cd..174a7e1 100644
--- a/ash/components/arc/session/arc_bridge_host_impl.cc
+++ b/ash/components/arc/session/arc_bridge_host_impl.cc
@@ -50,6 +50,7 @@
 #include "ash/components/arc/mojom/policy.mojom.h"
 #include "ash/components/arc/mojom/power.mojom.h"
 #include "ash/components/arc/mojom/print_spooler.mojom.h"
+#include "ash/components/arc/mojom/privacy_items.mojom.h"
 #include "ash/components/arc/mojom/process.mojom.h"
 #include "ash/components/arc/mojom/property.mojom.h"
 #include "ash/components/arc/mojom/rotation_lock.mojom.h"
@@ -350,6 +351,12 @@
                   std::move(print_spooler_remote));
 }
 
+void ArcBridgeHostImpl::OnPrivacyItemsInstanceReady(
+    mojo::PendingRemote<mojom::PrivacyItemsInstance> privacy_items_remote) {
+  OnInstanceReady(arc_bridge_service_->privacy_items(),
+                  std::move(privacy_items_remote));
+}
+
 void ArcBridgeHostImpl::OnProcessInstanceReady(
     mojo::PendingRemote<mojom::ProcessInstance> process_remote) {
   OnInstanceReady(arc_bridge_service_->process(), std::move(process_remote));
diff --git a/ash/components/arc/session/arc_bridge_host_impl.h b/ash/components/arc/session/arc_bridge_host_impl.h
index 9841119c..0a1c4ff 100644
--- a/ash/components/arc/session/arc_bridge_host_impl.h
+++ b/ash/components/arc/session/arc_bridge_host_impl.h
@@ -145,6 +145,9 @@
   void OnPrintSpoolerInstanceReady(
       mojo::PendingRemote<mojom::PrintSpoolerInstance> print_spooler_remote)
       override;
+  void OnPrivacyItemsInstanceReady(
+      mojo::PendingRemote<mojom::PrivacyItemsInstance> privacy_items_remote)
+      override;
   void OnProcessInstanceReady(
       mojo::PendingRemote<mojom::ProcessInstance> process_remote) override;
   void OnPropertyInstanceReady(
diff --git a/ash/components/arc/session/arc_bridge_service.cc b/ash/components/arc/session/arc_bridge_service.cc
index e28abd01..30ad3dce 100644
--- a/ash/components/arc/session/arc_bridge_service.cc
+++ b/ash/components/arc/session/arc_bridge_service.cc
@@ -45,6 +45,7 @@
 #include "ash/components/arc/mojom/policy.mojom.h"
 #include "ash/components/arc/mojom/power.mojom.h"
 #include "ash/components/arc/mojom/print_spooler.mojom.h"
+#include "ash/components/arc/mojom/privacy_items.mojom.h"
 #include "ash/components/arc/mojom/process.mojom.h"
 #include "ash/components/arc/mojom/property.mojom.h"
 #include "ash/components/arc/mojom/rotation_lock.mojom.h"
diff --git a/ash/components/arc/session/arc_bridge_service.h b/ash/components/arc/session/arc_bridge_service.h
index 0ae171f..e9385288 100644
--- a/ash/components/arc/session/arc_bridge_service.h
+++ b/ash/components/arc/session/arc_bridge_service.h
@@ -88,6 +88,8 @@
 class PowerInstance;
 class PrintSpoolerHost;
 class PrintSpoolerInstance;
+class PrivacyItemsHost;
+class PrivacyItemsInstance;
 class ProcessInstance;
 class PropertyInstance;
 class RotationLockInstance;
@@ -290,6 +292,10 @@
   print_spooler() {
     return &print_spooler_;
   }
+  ConnectionHolder<mojom::PrivacyItemsInstance, mojom::PrivacyItemsHost>*
+  privacy_items() {
+    return &privacy_items_;
+  }
   ConnectionHolder<mojom::ProcessInstance>* process() { return &process_; }
   ConnectionHolder<mojom::PropertyInstance>* property() { return &property_; }
   ConnectionHolder<mojom::RotationLockInstance>* rotation_lock() {
@@ -399,6 +405,8 @@
   ConnectionHolder<mojom::PowerInstance, mojom::PowerHost> power_;
   ConnectionHolder<mojom::PrintSpoolerInstance, mojom::PrintSpoolerHost>
       print_spooler_;
+  ConnectionHolder<mojom::PrivacyItemsInstance, mojom::PrivacyItemsHost>
+      privacy_items_;
   ConnectionHolder<mojom::ProcessInstance> process_;
   ConnectionHolder<mojom::PropertyInstance> property_;
   ConnectionHolder<mojom::RotationLockInstance> rotation_lock_;
diff --git a/ash/components/arc/test/fake_arc_bridge_host.cc b/ash/components/arc/test/fake_arc_bridge_host.cc
index 8182a21..039728c 100644
--- a/ash/components/arc/test/fake_arc_bridge_host.cc
+++ b/ash/components/arc/test/fake_arc_bridge_host.cc
@@ -45,6 +45,7 @@
 #include "ash/components/arc/mojom/policy.mojom.h"
 #include "ash/components/arc/mojom/power.mojom.h"
 #include "ash/components/arc/mojom/print_spooler.mojom.h"
+#include "ash/components/arc/mojom/privacy_items.mojom.h"
 #include "ash/components/arc/mojom/process.mojom.h"
 #include "ash/components/arc/mojom/property.mojom.h"
 #include "ash/components/arc/mojom/rotation_lock.mojom.h"
@@ -205,6 +206,9 @@
 void FakeArcBridgeHost::OnPrintSpoolerInstanceReady(
     mojo::PendingRemote<mojom::PrintSpoolerInstance> print_spooler_remote) {}
 
+void FakeArcBridgeHost::OnPrivacyItemsInstanceReady(
+    mojo::PendingRemote<mojom::PrivacyItemsInstance> privacy_items_remote) {}
+
 void FakeArcBridgeHost::OnProcessInstanceReady(
     mojo::PendingRemote<mojom::ProcessInstance> process_remote) {}
 
diff --git a/ash/components/arc/test/fake_arc_bridge_host.h b/ash/components/arc/test/fake_arc_bridge_host.h
index 95a37b6..f400913 100644
--- a/ash/components/arc/test/fake_arc_bridge_host.h
+++ b/ash/components/arc/test/fake_arc_bridge_host.h
@@ -123,6 +123,9 @@
   void OnPrintSpoolerInstanceReady(
       mojo::PendingRemote<mojom::PrintSpoolerInstance> print_spooler_remote)
       override;
+  void OnPrivacyItemsInstanceReady(
+      mojo::PendingRemote<mojom::PrivacyItemsInstance> privacy_items_remote)
+      override;
   void OnProcessInstanceReady(
       mojo::PendingRemote<mojom::ProcessInstance> process_remote) override;
   void OnPropertyInstanceReady(
diff --git a/ash/components/arc/video_accelerator/gpu_arc_video_frame_pool.cc b/ash/components/arc/video_accelerator/gpu_arc_video_frame_pool.cc
index 031ce9b..1343df1 100644
--- a/ash/components/arc/video_accelerator/gpu_arc_video_frame_pool.cc
+++ b/ash/components/arc/video_accelerator/gpu_arc_video_frame_pool.cc
@@ -11,9 +11,12 @@
 #include "base/bind.h"
 #include "base/posix/eintr_wrapper.h"
 #include "build/build_config.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/base/decode_status.h"
+#include "media/base/format_utils.h"
 #include "media/base/video_types.h"
 #include "media/gpu/macros.h"
+#include "ui/gfx/buffer_format_util.h"
 
 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
 #include "media/gpu/chromeos/platform_video_frame_utils.h"
@@ -178,8 +181,9 @@
     return;
   }
 
-  DmabufId dmabuf_id = media::DmabufVideoFramePool::GetDmabufId(*origin_frame);
-  auto it = dmabuf_to_video_frame_id_.emplace(dmabuf_id, video_frame->id);
+  const gfx::GpuMemoryBufferId buffer_id =
+      origin_frame->GetGpuMemoryBuffer()->GetId();
+  auto it = buffer_id_to_video_frame_id_.emplace(buffer_id, video_frame->id);
   DCHECK(it.second);
 
   // Wrap the video frame and attach a destruction observer so we're notified
@@ -227,9 +231,9 @@
 absl::optional<int32_t> GpuArcVideoFramePool::GetVideoFrameId(
     const media::VideoFrame* video_frame) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = dmabuf_to_video_frame_id_.find(
-      media::DmabufVideoFramePool::GetDmabufId(*video_frame));
-  return it != dmabuf_to_video_frame_id_.end()
+  auto it = buffer_id_to_video_frame_id_.find(
+      video_frame->GetGpuMemoryBuffer()->GetId());
+  return it != buffer_id_to_video_frame_id_.end()
              ? absl::optional<int32_t>(it->second)
              : absl::nullopt;
 }
@@ -239,10 +243,10 @@
   DVLOGF(4);
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  auto it = dmabuf_to_video_frame_id_.find(
-      media::DmabufVideoFramePool::GetDmabufId(*origin_frame));
-  DCHECK(it != dmabuf_to_video_frame_id_.end());
-  dmabuf_to_video_frame_id_.erase(it);
+  auto it = buffer_id_to_video_frame_id_.find(
+      origin_frame->GetGpuMemoryBuffer()->GetId());
+  DCHECK(it != buffer_id_to_video_frame_id_.end());
+  buffer_id_to_video_frame_id_.erase(it);
 }
 
 gfx::GpuMemoryBufferHandle GpuArcVideoFramePool::CreateGpuMemoryHandle(
@@ -282,19 +286,34 @@
     }
     gmb_handle = std::move(handle).value();
   }
+  gmb_handle.id = media::GetNextGpuMemoryBufferId();
+
   return gmb_handle;
 }
 
 scoped_refptr<media::VideoFrame> GpuArcVideoFramePool::CreateVideoFrame(
     gfx::GpuMemoryBufferHandle gmb_handle,
     media::VideoPixelFormat pixel_format) const {
-  std::vector<base::ScopedFD> fds;
-  for (auto& plane : gmb_handle.native_pixmap_handle.planes) {
-    fds.push_back(std::move(plane.fd));
+  auto buffer_format = media::VideoPixelFormatToGfxBufferFormat(pixel_format);
+  CHECK(buffer_format);
+  // Usage is SCANOUT_VDA_WRITE because we are just wrapping the dmabuf in a
+  // GpuMemoryBuffer. This buffer is just for decoding purposes, so having
+  // the dmabufs mmapped is not necessary.
+  std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
+      gpu::GpuMemoryBufferSupport().CreateGpuMemoryBufferImplFromHandle(
+          std::move(gmb_handle), coded_size_, *buffer_format,
+          gfx::BufferUsage::SCANOUT_VDA_WRITE, base::NullCallback());
+  if (!gpu_memory_buffer) {
+    VLOGF(1) << "Failed to create GpuMemoryBuffer. format: "
+             << gfx::BufferFormatToString(*buffer_format)
+             << ", coded_size: " << coded_size_.ToString();
+    return nullptr;
   }
-  return media::VideoFrame::WrapExternalDmabufs(
-      *video_frame_layout_, gfx::Rect(coded_size_), coded_size_, std::move(fds),
-      base::TimeDelta());
+
+  const gpu::MailboxHolder mailbox_holder[media::VideoFrame::kMaxPlanes] = {};
+  return media::VideoFrame::WrapExternalGpuMemoryBuffer(
+      gfx::Rect(coded_size_), coded_size_, std::move(gpu_memory_buffer),
+      mailbox_holder, base::NullCallback(), base::TimeDelta());
 }
 
 }  // namespace arc
diff --git a/ash/components/arc/video_accelerator/gpu_arc_video_frame_pool.h b/ash/components/arc/video_accelerator/gpu_arc_video_frame_pool.h
index 86739980..69a215c 100644
--- a/ash/components/arc/video_accelerator/gpu_arc_video_frame_pool.h
+++ b/ash/components/arc/video_accelerator/gpu_arc_video_frame_pool.h
@@ -76,8 +76,6 @@
   base::WeakPtr<GpuArcVideoFramePool> WeakThis() { return weak_this_; }
 
  private:
-  using DmabufId = media::DmabufVideoFramePool::DmabufId;
-
   // Create a GPU memory handle from the specified |fd|.
   gfx::GpuMemoryBufferHandle CreateGpuMemoryHandle(
       base::ScopedFD fd,
@@ -111,8 +109,8 @@
   // The current video frame layout.
   absl::optional<media::VideoFrameLayout> video_frame_layout_;
 
-  // Map of video frame DmabufIds to the associated video frame ids.
-  std::map<DmabufId, int32_t> dmabuf_to_video_frame_id_;
+  // Map of video frame buffer ids to the associated video frame ids.
+  std::map<gfx::GpuMemoryBufferId, int32_t> buffer_id_to_video_frame_id_;
 
   // The protected buffer manager, used when decoding an encrypted video.
   scoped_refptr<ProtectedBufferManager> protected_buffer_manager_;
diff --git a/ash/components/arc/volume_mounter/arc_volume_mounter_bridge.cc b/ash/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
index 594d50c1..c43075b 100644
--- a/ash/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
+++ b/ash/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
@@ -153,7 +153,7 @@
 
 bool ArcVolumeMounterBridge::IsVisibleToAndroidApps(
     const std::string& uuid) const {
-  const base::ListValue* uuid_list =
+  const base::Value* uuid_list =
       pref_service_->GetList(prefs::kArcVisibleExternalStorages);
   for (auto& value : uuid_list->GetList()) {
     if (value.is_string() && value.GetString() == uuid)
diff --git a/ash/components/audio/audio_devices_pref_handler_impl.cc b/ash/components/audio/audio_devices_pref_handler_impl.cc
index 4048b85..7b5eefe 100644
--- a/ash/components/audio/audio_devices_pref_handler_impl.cc
+++ b/ash/components/audio/audio_devices_pref_handler_impl.cc
@@ -14,6 +14,7 @@
 #include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/values.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -302,8 +303,8 @@
 }
 
 void AudioDevicesPrefHandlerImpl::LoadDevicesMutePref() {
-  const base::DictionaryValue* mute_prefs =
-      local_state_->GetDictionary(prefs::kAudioDevicesMute);
+  const base::DictionaryValue* mute_prefs = &base::Value::AsDictionaryValue(
+      *local_state_->GetDictionary(prefs::kAudioDevicesMute));
   if (mute_prefs)
     device_mute_settings_.reset(mute_prefs->DeepCopy());
 }
@@ -315,8 +316,8 @@
 }
 
 void AudioDevicesPrefHandlerImpl::LoadDevicesVolumePref() {
-  const base::DictionaryValue* volume_prefs =
-      local_state_->GetDictionary(prefs::kAudioDevicesVolumePercent);
+  const base::DictionaryValue* volume_prefs = &base::Value::AsDictionaryValue(
+      *local_state_->GetDictionary(prefs::kAudioDevicesVolumePercent));
   if (volume_prefs)
     device_volume_settings_.reset(volume_prefs->DeepCopy());
 }
@@ -329,8 +330,8 @@
 }
 
 void AudioDevicesPrefHandlerImpl::LoadDevicesGainPref() {
-  const base::DictionaryValue* gain_prefs =
-      local_state_->GetDictionary(prefs::kAudioDevicesGainPercent);
+  const base::DictionaryValue* gain_prefs = &base::Value::AsDictionaryValue(
+      *local_state_->GetDictionary(prefs::kAudioDevicesGainPercent));
   if (gain_prefs)
     device_gain_settings_.reset(gain_prefs->DeepCopy());
 }
@@ -343,8 +344,8 @@
 }
 
 void AudioDevicesPrefHandlerImpl::LoadDevicesStatePref() {
-  const base::DictionaryValue* state_prefs =
-      local_state_->GetDictionary(prefs::kAudioDevicesState);
+  const base::DictionaryValue* state_prefs = &base::Value::AsDictionaryValue(
+      *local_state_->GetDictionary(prefs::kAudioDevicesState));
   if (state_prefs)
     device_state_settings_.reset(state_prefs->DeepCopy());
 }
diff --git a/ash/components/login/auth/auth_attempt_state.cc b/ash/components/login/auth/auth_attempt_state.cc
index 67aa045..335b5492 100644
--- a/ash/components/login/auth/auth_attempt_state.cc
+++ b/ash/components/login/auth/auth_attempt_state.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-namespace chromeos {
+namespace ash {
 
 AuthAttemptState::AuthAttemptState(std::unique_ptr<UserContext> user_context)
     : user_context(std::move(user_context)) {
@@ -65,4 +65,4 @@
   return username_hash_obtained_;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/auth_attempt_state.h b/ash/components/login/auth/auth_attempt_state.h
index 489076b..533caae 100644
--- a/ash/components/login/auth/auth_attempt_state.h
+++ b/ash/components/login/auth/auth_attempt_state.h
@@ -15,7 +15,7 @@
 #include "google_apis/gaia/gaia_auth_consumer.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
-namespace chromeos {
+namespace ash {
 
 // Tracks the state associated with a single attempt to log in to chromium OS.
 // Enforces that methods are only called on the UI thread.
@@ -86,6 +86,6 @@
   bool username_hash_valid_ = true;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_AUTH_ATTEMPT_STATE_H_
diff --git a/ash/components/login/auth/auth_session_authenticator.cc b/ash/components/login/auth/auth_session_authenticator.cc
index 51d5f161..2738591 100644
--- a/ash/components/login/auth/auth_session_authenticator.cc
+++ b/ash/components/login/auth/auth_session_authenticator.cc
@@ -20,7 +20,7 @@
 #include "components/device_event_log/device_event_log.h"
 #include "components/user_manager/user_names.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -1059,4 +1059,4 @@
   UnmountGeneric(std::move(crasher), std::move(not_owner), std::move(context));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/auth_session_authenticator.h b/ash/components/login/auth/auth_session_authenticator.h
index b38b703..e4c51218 100644
--- a/ash/components/login/auth/auth_session_authenticator.h
+++ b/ash/components/login/auth/auth_session_authenticator.h
@@ -16,7 +16,7 @@
 
 class AuthFailure;
 
-namespace chromeos {
+namespace ash {
 
 class AuthStatusConsumer;
 
@@ -112,7 +112,7 @@
           const UserContext& context,
           user_data_auth::MountRequest request)>;
 
-  // Transforms chromeos::Key in UserContext to cryptohome::KeyDefinition
+  // Transforms ash::Key in UserContext to cryptohome::KeyDefinition
   // used in cryptohome requests.
   using TransformCryotohomeKeyCallback =
       base::OnceCallback<void(const UserContext& context,
@@ -277,6 +277,6 @@
   base::WeakPtrFactory<AuthSessionAuthenticator> weak_factory_{this};
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_AUTH_SESSION_AUTHENTICATOR_H_
diff --git a/ash/components/login/auth/auth_status_consumer.cc b/ash/components/login/auth/auth_status_consumer.cc
index c071c76..d430eee 100644
--- a/ash/components/login/auth/auth_status_consumer.cc
+++ b/ash/components/login/auth/auth_status_consumer.cc
@@ -6,7 +6,7 @@
 
 #include "base/notreached.h"
 
-namespace chromeos {
+namespace ash {
 
 void AuthStatusConsumer::OnPasswordChangeDetected(
     const UserContext& user_context) {
@@ -19,4 +19,4 @@
   NOTREACHED();
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/auth_status_consumer.h b/ash/components/login/auth/auth_status_consumer.h
index 45277bf44..6b3ad56 100644
--- a/ash/components/login/auth/auth_status_consumer.h
+++ b/ash/components/login/auth/auth_status_consumer.h
@@ -15,7 +15,7 @@
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "net/base/net_errors.h"
 
-namespace chromeos {
+namespace ash {
 
 class UserContext;
 
@@ -150,13 +150,6 @@
                                        bool has_incomplete_migration);
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove after //chrome/browser/chromeos
-// source migration is finished.
-namespace ash {
-using ::chromeos::AuthFailure;
-using ::chromeos::AuthStatusConsumer;
 }  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_AUTH_STATUS_CONSUMER_H_
diff --git a/ash/components/login/auth/authenticator.cc b/ash/components/login/auth/authenticator.cc
index de78867..f0b3162 100644
--- a/ash/components/login/auth/authenticator.cc
+++ b/ash/components/login/auth/authenticator.cc
@@ -4,7 +4,7 @@
 
 #include "ash/components/login/auth/authenticator.h"
 
-namespace chromeos {
+namespace ash {
 
 class AuthStatusConsumer;
 
@@ -17,4 +17,4 @@
   consumer_ = consumer;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/authenticator.h b/ash/components/login/auth/authenticator.h
index 9ebc624..67f80810 100644
--- a/ash/components/login/auth/authenticator.h
+++ b/ash/components/login/auth/authenticator.h
@@ -14,7 +14,7 @@
 
 class AccountId;
 
-namespace chromeos {
+namespace ash {
 
 class UserContext;
 
@@ -93,12 +93,6 @@
   friend class base::RefCountedThreadSafe<Authenticator>;
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source migration is finished.
-namespace ash {
-using ::chromeos::Authenticator;
-}
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_AUTHENTICATOR_H_
diff --git a/ash/components/login/auth/challenge_response/cert_utils.cc b/ash/components/login/auth/challenge_response/cert_utils.cc
index 7749c1f..62de729 100644
--- a/ash/components/login/auth/challenge_response/cert_utils.cc
+++ b/ash/components/login/auth/challenge_response/cert_utils.cc
@@ -13,7 +13,7 @@
 #include "net/cert/x509_util.h"
 #include "third_party/boringssl/src/include/openssl/ssl.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -69,4 +69,4 @@
   return true;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/challenge_response/cert_utils.h b/ash/components/login/auth/challenge_response/cert_utils.h
index ae82327..040f6db 100644
--- a/ash/components/login/auth/challenge_response/cert_utils.h
+++ b/ash/components/login/auth/challenge_response/cert_utils.h
@@ -16,7 +16,7 @@
 class X509Certificate;
 }  // namespace net
 
-namespace chromeos {
+namespace ash {
 
 // Maps from the TLS 1.3 SignatureScheme value into the challenge-response key
 // algorithm.
@@ -33,12 +33,6 @@
         signature_algorithms,
     ChallengeResponseKey* challenge_response_key);
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source migration is finished.
-namespace ash {
-using ::chromeos::GetChallengeResponseKeyAlgorithmFromSsl;
-}
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_CHALLENGE_RESPONSE_CERT_UTILS_H_
diff --git a/ash/components/login/auth/challenge_response/cert_utils_unittest.cc b/ash/components/login/auth/challenge_response/cert_utils_unittest.cc
index 8b2514d..6ee0ed342 100644
--- a/ash/components/login/auth/challenge_response/cert_utils_unittest.cc
+++ b/ash/components/login/auth/challenge_response/cert_utils_unittest.cc
@@ -16,7 +16,7 @@
 #include "net/test/test_data_directory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace chromeos {
+namespace ash {
 
 using KeySignatureAlgorithm = ChallengeResponseKey::SignatureAlgorithm;
 
@@ -59,4 +59,4 @@
       certificate(), {} /* signature_algorithms */, &challenge_response_key));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/challenge_response/key_label_utils.cc b/ash/components/login/auth/challenge_response/key_label_utils.cc
index 4f25897..8278c186 100644
--- a/ash/components/login/auth/challenge_response/key_label_utils.cc
+++ b/ash/components/login/auth/challenge_response/key_label_utils.cc
@@ -8,7 +8,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "crypto/sha2.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -37,4 +37,4 @@
              challenge_response_keys[0].public_key_spki_der());
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/challenge_response/key_label_utils.h b/ash/components/login/auth/challenge_response/key_label_utils.h
index 8bbbbb2..fd526a5c 100644
--- a/ash/components/login/auth/challenge_response/key_label_utils.h
+++ b/ash/components/login/auth/challenge_response/key_label_utils.h
@@ -10,7 +10,7 @@
 
 #include "ash/components/login/auth/challenge_response_key.h"
 
-namespace chromeos {
+namespace ash {
 
 // Generates the cryptohome user key label for the given challenge-response key
 // information. Currently the constraint is that |challenge_response_keys| must
@@ -18,6 +18,6 @@
 std::string GenerateChallengeResponseKeyLabel(
     const std::vector<ChallengeResponseKey>& challenge_response_keys);
 
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_CHALLENGE_RESPONSE_KEY_LABEL_UTILS_H_
diff --git a/ash/components/login/auth/challenge_response/known_user_pref_utils.cc b/ash/components/login/auth/challenge_response/known_user_pref_utils.cc
index 5aaf6e0e..71c5b83 100644
--- a/ash/components/login/auth/challenge_response/known_user_pref_utils.cc
+++ b/ash/components/login/auth/challenge_response/known_user_pref_utils.cc
@@ -10,7 +10,7 @@
 #include "base/logging.h"
 #include "base/values.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -92,4 +92,4 @@
   return !deserialized_challenge_response_keys->empty();
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/challenge_response/known_user_pref_utils.h b/ash/components/login/auth/challenge_response/known_user_pref_utils.h
index 3df3993..af323c3 100644
--- a/ash/components/login/auth/challenge_response/known_user_pref_utils.h
+++ b/ash/components/login/auth/challenge_response/known_user_pref_utils.h
@@ -15,7 +15,7 @@
 class Value;
 }  // namespace base
 
-namespace chromeos {
+namespace ash {
 
 // Builds the known_user value that contains information about the given
 // challenge-response keys that can be used by the user to authenticate.
@@ -35,12 +35,6 @@
         std::vector<DeserializedChallengeResponseKey>*
             deserialized_challenge_response_keys);
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source migration is finished.
-namespace ash {
-using ::chromeos::SerializeChallengeResponseKeysForKnownUser;
-}
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_CHALLENGE_RESPONSE_KNOWN_USER_PREF_UTILS_H_
diff --git a/ash/components/login/auth/challenge_response_key.cc b/ash/components/login/auth/challenge_response_key.cc
index 89c51f7..a95d063 100644
--- a/ash/components/login/auth/challenge_response_key.cc
+++ b/ash/components/login/auth/challenge_response_key.cc
@@ -4,7 +4,7 @@
 
 #include "ash/components/login/auth/challenge_response_key.h"
 
-namespace chromeos {
+namespace ash {
 
 ChallengeResponseKey::ChallengeResponseKey() = default;
 
@@ -22,4 +22,4 @@
   return !(*this == other);
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/challenge_response_key.h b/ash/components/login/auth/challenge_response_key.h
index aa4db5c..39467fdc 100644
--- a/ash/components/login/auth/challenge_response_key.h
+++ b/ash/components/login/auth/challenge_response_key.h
@@ -10,7 +10,7 @@
 
 #include "base/component_export.h"
 
-namespace chromeos {
+namespace ash {
 
 // This class contains information about a challenge-response key for user
 // authentication. This includes information about the public key of the
@@ -72,13 +72,11 @@
   std::string extension_id;
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source migration is finished.
-namespace ash {
-using ::chromeos::ChallengeResponseKey;
-using ::chromeos::DeserializedChallengeResponseKey;
 }  // namespace ash
 
+// TODO(https://crbug.com/1164001): remove when the migration is finished.
+namespace chromeos {
+using ::ash::ChallengeResponseKey;
+}  // namespace chromeos
+
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_CHALLENGE_RESPONSE_KEY_H_
diff --git a/ash/components/login/auth/cryptohome_authenticator.cc b/ash/components/login/auth/cryptohome_authenticator.cc
index ef65e93..032887a 100644
--- a/ash/components/login/auth/cryptohome_authenticator.cc
+++ b/ash/components/login/auth/cryptohome_authenticator.cc
@@ -36,7 +36,7 @@
 #include "components/user_manager/user_type.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -1023,4 +1023,4 @@
   user_can_login_ = check_result;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/cryptohome_authenticator.h b/ash/components/login/auth/cryptohome_authenticator.h
index f2ae81d6..fb9a1f0 100644
--- a/ash/components/login/auth/cryptohome_authenticator.h
+++ b/ash/components/login/auth/cryptohome_authenticator.h
@@ -24,11 +24,9 @@
 class AuthFailure;
 
 namespace ash {
-class CryptohomeAuthenticatorTest;
-}
 
-namespace chromeos {
 class AuthStatusConsumer;
+class CryptohomeAuthenticatorTest;
 
 // Authenticates a Chromium OS user against cryptohome.
 // Relies on the fact that online authentications has been already performed
@@ -175,7 +173,7 @@
   ~CryptohomeAuthenticator() override;
 
  private:
-  friend class ash::CryptohomeAuthenticatorTest;
+  friend class CryptohomeAuthenticatorTest;
   FRIEND_TEST_ALL_PREFIXES(CryptohomeAuthenticatorTest,
                            ResolveOwnerNeededDirectFailedMount);
   FRIEND_TEST_ALL_PREFIXES(CryptohomeAuthenticatorTest,
@@ -262,12 +260,6 @@
   AuthFailure delayed_login_failure_;
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source migration is finished.
-namespace ash {
-using ::chromeos::CryptohomeAuthenticator;
-}
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_CRYPTOHOME_AUTHENTICATOR_H_
diff --git a/ash/components/login/auth/cryptohome_key_constants.cc b/ash/components/login/auth/cryptohome_key_constants.cc
index fb1d6ec..fd63c868 100644
--- a/ash/components/login/auth/cryptohome_key_constants.cc
+++ b/ash/components/login/auth/cryptohome_key_constants.cc
@@ -4,7 +4,7 @@
 
 #include "ash/components/login/auth/cryptohome_key_constants.h"
 
-namespace chromeos {
+namespace ash {
 
 // The label used for the key derived from the user's GAIA credentials.
 //
@@ -23,4 +23,4 @@
 
 const char kCryptohomeWildcardLabel[] = "";
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/cryptohome_key_constants.h b/ash/components/login/auth/cryptohome_key_constants.h
index 340dd0f0..772964d 100644
--- a/ash/components/login/auth/cryptohome_key_constants.h
+++ b/ash/components/login/auth/cryptohome_key_constants.h
@@ -7,7 +7,7 @@
 
 #include "base/component_export.h"
 
-namespace chromeos {
+namespace ash {
 
 COMPONENT_EXPORT(ASH_LOGIN_AUTH)
 extern const char kCryptohomeGaiaKeyLabel[];
@@ -21,13 +21,11 @@
 COMPONENT_EXPORT(ASH_LOGIN_AUTH)
 extern const char kCryptohomeWildcardLabel[];
 
-}  // namespace chromeos
+}  // namespace ash
 
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source code migration is finished.
-namespace ash {
-using ::chromeos::kCryptohomeGaiaKeyLabel;
-using ::chromeos::kCryptohomePinLabel;
-}
+// TODO(https://crbug.com/1164001): remove when the migration is finished.
+namespace chromeos {
+using ::ash::kCryptohomeGaiaKeyLabel;
+}  // namespace chromeos
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_CRYPTOHOME_KEY_CONSTANTS_H_
diff --git a/ash/components/login/auth/cryptohome_parameter_utils.cc b/ash/components/login/auth/cryptohome_parameter_utils.cc
index f8b6ab5..06e61fa8 100644
--- a/ash/components/login/auth/cryptohome_parameter_utils.cc
+++ b/ash/components/login/auth/cryptohome_parameter_utils.cc
@@ -12,7 +12,7 @@
 
 using cryptohome::KeyDefinition;
 
-namespace chromeos {
+namespace ash {
 namespace cryptohome_parameter_utils {
 
 KeyDefinition CreateKeyDefFromUserContext(const UserContext& user_context) {
@@ -64,4 +64,4 @@
 }
 
 }  // namespace cryptohome_parameter_utils
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/cryptohome_parameter_utils.h b/ash/components/login/auth/cryptohome_parameter_utils.h
index 327ecba..6a42fe0 100644
--- a/ash/components/login/auth/cryptohome_parameter_utils.h
+++ b/ash/components/login/auth/cryptohome_parameter_utils.h
@@ -9,9 +9,9 @@
 
 namespace cryptohome {
 struct KeyDefinition;
-}
+}  // namespace cryptohome
 
-namespace chromeos {
+namespace ash {
 
 class UserContext;
 
@@ -33,6 +33,6 @@
     const UserContext& user_context);
 
 }  // namespace cryptohome_parameter_utils
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_CRYPTOHOME_PARAMETER_UTILS_H_
diff --git a/ash/components/login/auth/extended_authenticator.cc b/ash/components/login/auth/extended_authenticator.cc
index 404f0a6..ea7c79d 100644
--- a/ash/components/login/auth/extended_authenticator.cc
+++ b/ash/components/login/auth/extended_authenticator.cc
@@ -6,7 +6,7 @@
 
 #include "ash/components/login/auth/extended_authenticator_impl.h"
 
-namespace chromeos {
+namespace ash {
 
 // static
 scoped_refptr<ExtendedAuthenticator> ExtendedAuthenticator::Create(
@@ -18,4 +18,4 @@
 
 ExtendedAuthenticator::~ExtendedAuthenticator() = default;
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/extended_authenticator.h b/ash/components/login/auth/extended_authenticator.h
index 86eba1e4..7094b1b 100644
--- a/ash/components/login/auth/extended_authenticator.h
+++ b/ash/components/login/auth/extended_authenticator.h
@@ -15,7 +15,7 @@
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "chromeos/dbus/cryptohome/UserDataAuth.pb.h"
 
-namespace chromeos {
+namespace ash {
 
 class AuthStatusConsumer;
 class UserContext;
@@ -104,12 +104,6 @@
   friend class base::RefCountedThreadSafe<ExtendedAuthenticator>;
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source migration is finished.
-namespace ash {
-using ::chromeos::ExtendedAuthenticator;
-}
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_EXTENDED_AUTHENTICATOR_H_
diff --git a/ash/components/login/auth/extended_authenticator_impl.cc b/ash/components/login/auth/extended_authenticator_impl.cc
index 00b0e3c77..98f4c9e 100644
--- a/ash/components/login/auth/extended_authenticator_impl.cc
+++ b/ash/components/login/auth/extended_authenticator_impl.cc
@@ -23,7 +23,7 @@
 #include "crypto/sha2.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 
-namespace chromeos {
+namespace ash {
 
 // static
 scoped_refptr<ExtendedAuthenticatorImpl> ExtendedAuthenticatorImpl::Create(
@@ -257,4 +257,4 @@
   }
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/extended_authenticator_impl.h b/ash/components/login/auth/extended_authenticator_impl.h
index 1e7da2b..8fc202d1 100644
--- a/ash/components/login/auth/extended_authenticator_impl.h
+++ b/ash/components/login/auth/extended_authenticator_impl.h
@@ -16,7 +16,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
-namespace chromeos {
+namespace ash {
 
 class AuthStatusConsumer;
 class UserContext;
@@ -93,6 +93,6 @@
   AuthStatusConsumer* consumer_;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_EXTENDED_AUTHENTICATOR_IMPL_H_
diff --git a/ash/components/login/auth/fake_extended_authenticator.cc b/ash/components/login/auth/fake_extended_authenticator.cc
index aa4b400..9bed8fa 100644
--- a/ash/components/login/auth/fake_extended_authenticator.cc
+++ b/ash/components/login/auth/fake_extended_authenticator.cc
@@ -8,7 +8,7 @@
 #include "base/notreached.h"
 #include "components/account_id/account_id.h"
 
-namespace chromeos {
+namespace ash {
 
 FakeExtendedAuthenticator::FakeExtendedAuthenticator(
     AuthStatusConsumer* consumer,
@@ -87,4 +87,4 @@
     consumer_->OnAuthFailure(error);
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/fake_extended_authenticator.h b/ash/components/login/auth/fake_extended_authenticator.h
index 258a5747..4ce719b 100644
--- a/ash/components/login/auth/fake_extended_authenticator.h
+++ b/ash/components/login/auth/fake_extended_authenticator.h
@@ -9,7 +9,7 @@
 #include "ash/components/login/auth/user_context.h"
 #include "base/component_export.h"
 
-namespace chromeos {
+namespace ash {
 
 class AuthFailure;
 
@@ -56,12 +56,6 @@
   UserContext expected_user_context_;
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source migration is finished.
-namespace ash {
-using ::chromeos::FakeExtendedAuthenticator;
-}
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_FAKE_EXTENDED_AUTHENTICATOR_H_
diff --git a/ash/components/login/auth/key.cc b/ash/components/login/auth/key.cc
index d8a06c3..636d684 100644
--- a/ash/components/login/auth/key.cc
+++ b/ash/components/login/auth/key.cc
@@ -14,7 +14,7 @@
 #include "crypto/sha2.h"
 #include "crypto/symmetric_key.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -107,4 +107,4 @@
   salt_ = salt;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/key.h b/ash/components/login/auth/key.h
index 9cfe4126..3ad817c 100644
--- a/ash/components/login/auth/key.h
+++ b/ash/components/login/auth/key.h
@@ -9,7 +9,7 @@
 
 #include "base/component_export.h"
 
-namespace chromeos {
+namespace ash {
 
 // Key for user authentication. The class supports hashing of plain text
 // passwords to generate keys as well as the use of pre-hashed keys.
@@ -61,12 +61,11 @@
   std::string label_;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source migration is finished.
-namespace ash {
-using ::chromeos::Key;
-}
+// TODO(https://crbug.com/1164001): remove when the migration is finished.
+namespace chromeos {
+using ::ash::Key;
+}  // namespace chromeos
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_KEY_H_
diff --git a/ash/components/login/auth/key_unittest.cc b/ash/components/login/auth/key_unittest.cc
index 0ea0613..0d3f6a9 100644
--- a/ash/components/login/auth/key_unittest.cc
+++ b/ash/components/login/auth/key_unittest.cc
@@ -6,7 +6,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -63,4 +63,4 @@
   EXPECT_EQ(4, Key::KEY_TYPE_COUNT);
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/login_performer.cc b/ash/components/login/auth/login_performer.cc
index 39d1809..42739ac 100644
--- a/ash/components/login/auth/login_performer.cc
+++ b/ash/components/login/auth/login_performer.cc
@@ -22,7 +22,7 @@
 
 using base::UserMetricsAction;
 
-namespace chromeos {
+namespace ash {
 
 LoginPerformer::LoginPerformer(Delegate* delegate)
     : delegate_(delegate),
@@ -264,4 +264,4 @@
 void LoginPerformer::EnsureAuthenticator() {
   authenticator_ = CreateAuthenticator();
 }
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/login_performer.h b/ash/components/login/auth/login_performer.h
index 5cf4e6f..e126293 100644
--- a/ash/components/login/auth/login_performer.h
+++ b/ash/components/login/auth/login_performer.h
@@ -24,9 +24,9 @@
 
 namespace network {
 class SharedURLLoaderFactory;
-}
+}  // namespace network
 
-namespace chromeos {
+namespace ash {
 
 // This class encapsulates sign in operations.
 // Sign in is performed in a way that offline auth is executed first.
@@ -210,12 +210,6 @@
   base::WeakPtrFactory<LoginPerformer> weak_factory_{this};
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove when the //chrome/browser/chromeos
-// source code migration is finished.
-namespace ash {
-using ::chromeos::LoginPerformer;
-}
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_LOGIN_PERFORMER_H_
diff --git a/ash/components/login/auth/mock_auth_status_consumer.cc b/ash/components/login/auth/mock_auth_status_consumer.cc
index eb371447..0d469c3ec 100644
--- a/ash/components/login/auth/mock_auth_status_consumer.cc
+++ b/ash/components/login/auth/mock_auth_status_consumer.cc
@@ -8,7 +8,7 @@
 #include "google_apis/gaia/gaia_auth_consumer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace chromeos {
+namespace ash {
 
 MockAuthStatusConsumer::MockAuthStatusConsumer(base::OnceClosure quit_closure)
     : quit_closure_(std::move(quit_closure)) {}
@@ -63,4 +63,4 @@
   std::move(quit_closure_).Run();
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/mock_auth_status_consumer.h b/ash/components/login/auth/mock_auth_status_consumer.h
index 9ab1ed5d..680252f2 100644
--- a/ash/components/login/auth/mock_auth_status_consumer.h
+++ b/ash/components/login/auth/mock_auth_status_consumer.h
@@ -11,7 +11,7 @@
 #include "base/component_export.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
-namespace chromeos {
+namespace ash {
 
 class COMPONENT_EXPORT(ASH_LOGIN_AUTH) MockAuthStatusConsumer
     : public AuthStatusConsumer {
@@ -51,12 +51,6 @@
   base::OnceClosure quit_closure_;
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source code migration is finished.
-namespace ash {
-using ::chromeos::MockAuthStatusConsumer;
-}
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_MOCK_AUTH_STATUS_CONSUMER_H_
diff --git a/ash/components/login/auth/password_visibility_utils.cc b/ash/components/login/auth/password_visibility_utils.cc
index 92ded90..28b1a4b8 100644
--- a/ash/components/login/auth/password_visibility_utils.cc
+++ b/ash/components/login/auth/password_visibility_utils.cc
@@ -6,7 +6,7 @@
 
 #include "components/user_manager/known_user.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace password_visibility {
 
@@ -19,4 +19,4 @@
 
 }  // namespace password_visibility
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/password_visibility_utils.h b/ash/components/login/auth/password_visibility_utils.h
index 10c1eb9..5fd2d8c 100644
--- a/ash/components/login/auth/password_visibility_utils.h
+++ b/ash/components/login/auth/password_visibility_utils.h
@@ -9,7 +9,7 @@
 
 class AccountId;
 
-namespace chromeos {
+namespace ash {
 
 namespace password_visibility {
 
@@ -20,6 +20,6 @@
 
 }  // namespace password_visibility
 
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_PASSWORD_VISIBILITY_UTILS_H_
diff --git a/ash/components/login/auth/safe_mode_delegate.h b/ash/components/login/auth/safe_mode_delegate.h
index 555630e..3644f3cb 100644
--- a/ash/components/login/auth/safe_mode_delegate.h
+++ b/ash/components/login/auth/safe_mode_delegate.h
@@ -8,7 +8,7 @@
 #include "base/callback.h"
 #include "base/component_export.h"
 
-namespace chromeos {
+namespace ash {
 
 // In case when DeviceSettings get corrupted, it is not possible to rely
 // on them to determine if particular user can sign in. In that case
@@ -42,12 +42,6 @@
                                       IsOwnerCallback callback) = 0;
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove when the //chrome/browser/chromeos
-// source code migration is finished.
-namespace ash {
-using ::chromeos::SafeModeDelegate;
-}
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_SAFE_MODE_DELEGATE_H_
diff --git a/ash/components/login/auth/saml_password_attributes.cc b/ash/components/login/auth/saml_password_attributes.cc
index 021ca53..0b4ab6a 100644
--- a/ash/components/login/auth/saml_password_attributes.cc
+++ b/ash/components/login/auth/saml_password_attributes.cc
@@ -28,7 +28,7 @@
 
 }  // namespace
 
-namespace chromeos {
+namespace ash {
 
 SamlPasswordAttributes::SamlPasswordAttributes() {}
 
@@ -111,4 +111,4 @@
   empty.SaveToPrefs(prefs);
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/saml_password_attributes.h b/ash/components/login/auth/saml_password_attributes.h
index b39a80b..cf601d61 100644
--- a/ash/components/login/auth/saml_password_attributes.h
+++ b/ash/components/login/auth/saml_password_attributes.h
@@ -15,9 +15,9 @@
 
 namespace base {
 class DictionaryValue;
-}
+}  // namespace base
 
-namespace chromeos {
+namespace ash {
 
 // Struct which holds attributes about a user's password provided by the SAML
 // Identity Provider during SAML signin flow. These can be read or written to
@@ -71,12 +71,11 @@
   std::string password_change_url_;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source migration is finished.
-namespace ash {
-using ::chromeos::SamlPasswordAttributes;
-}
+// TODO(https://crbug.com/1164001): remove when the migration is finished.
+namespace chromeos {
+using ::ash::SamlPasswordAttributes;
+}  // namespace chromeos
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_SAML_PASSWORD_ATTRIBUTES_H_
diff --git a/ash/components/login/auth/saml_password_attributes_unittest.cc b/ash/components/login/auth/saml_password_attributes_unittest.cc
index 0faf21e..64dae2f 100644
--- a/ash/components/login/auth/saml_password_attributes_unittest.cc
+++ b/ash/components/login/auth/saml_password_attributes_unittest.cc
@@ -8,7 +8,7 @@
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -75,4 +75,4 @@
   ExpectEmpty(loaded);
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/stub_authenticator.cc b/ash/components/login/auth/stub_authenticator.cc
index ca8260e..4844d840 100644
--- a/ash/components/login/auth/stub_authenticator.cc
+++ b/ash/components/login/auth/stub_authenticator.cc
@@ -9,7 +9,7 @@
 #include "base/notreached.h"
 #include "base/threading/thread_task_runner_handle.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -189,4 +189,4 @@
                                      has_incomplete_encryption_migration_);
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/stub_authenticator.h b/ash/components/login/auth/stub_authenticator.h
index 82905b6..0ea0d53 100644
--- a/ash/components/login/auth/stub_authenticator.h
+++ b/ash/components/login/auth/stub_authenticator.h
@@ -16,7 +16,7 @@
 
 class AccountId;
 
-namespace chromeos {
+namespace ash {
 
 class AuthStatusConsumer;
 class StubAuthenticatorBuilder;
@@ -94,12 +94,6 @@
   AuthFailure::FailureReason failure_reason_ = AuthFailure::NONE;
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source migration is finished.
-namespace ash {
-using ::chromeos::StubAuthenticator;
-}
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_STUB_AUTHENTICATOR_H_
diff --git a/ash/components/login/auth/stub_authenticator_builder.cc b/ash/components/login/auth/stub_authenticator_builder.cc
index 6c2f129f..58b30ff 100644
--- a/ash/components/login/auth/stub_authenticator_builder.cc
+++ b/ash/components/login/auth/stub_authenticator_builder.cc
@@ -4,7 +4,7 @@
 
 #include "ash/components/login/auth/stub_authenticator_builder.h"
 
-namespace chromeos {
+namespace ash {
 
 StubAuthenticatorBuilder::StubAuthenticatorBuilder(
     const UserContext& expected_user_context)
@@ -51,4 +51,4 @@
   failure_reason_ = failure_reason;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/stub_authenticator_builder.h b/ash/components/login/auth/stub_authenticator_builder.h
index d8d519a..036dd68 100644
--- a/ash/components/login/auth/stub_authenticator_builder.h
+++ b/ash/components/login/auth/stub_authenticator_builder.h
@@ -13,7 +13,7 @@
 #include "base/component_export.h"
 #include "base/memory/ref_counted.h"
 
-namespace chromeos {
+namespace ash {
 
 // Helper class for creating a StubAuthenticator with certain configuration.
 // Useful in tests for injecting StubAuthenticators to be used during user
@@ -70,12 +70,6 @@
   AuthFailure::FailureReason failure_reason_ = AuthFailure::NONE;
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove after //chrome/browser/chromeos
-// source migration is finished.
-namespace ash {
-using ::chromeos::StubAuthenticatorBuilder;
-}
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_STUB_AUTHENTICATOR_BUILDER_H_
diff --git a/ash/components/login/auth/sync_trusted_vault_keys.cc b/ash/components/login/auth/sync_trusted_vault_keys.cc
index cedc043..68bfdb5 100644
--- a/ash/components/login/auth/sync_trusted_vault_keys.cc
+++ b/ash/components/login/auth/sync_trusted_vault_keys.cc
@@ -9,7 +9,7 @@
 #include "base/values.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -146,4 +146,4 @@
   return trusted_recovery_methods_;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/sync_trusted_vault_keys.h b/ash/components/login/auth/sync_trusted_vault_keys.h
index 334dd168..b0346fc 100644
--- a/ash/components/login/auth/sync_trusted_vault_keys.h
+++ b/ash/components/login/auth/sync_trusted_vault_keys.h
@@ -12,9 +12,9 @@
 
 namespace base {
 class DictionaryValue;
-}
+}  // namespace base
 
-namespace chromeos {
+namespace ash {
 
 // Struct which holds keys about a user's encryption keys during signin flow.
 class COMPONENT_EXPORT(ASH_LOGIN_AUTH) SyncTrustedVaultKeys {
@@ -54,12 +54,11 @@
   std::vector<TrustedRecoveryMethod> trusted_recovery_methods_;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source migration is finished.
-namespace ash {
-using ::chromeos::SyncTrustedVaultKeys;
-}
+// TODO(https://crbug.com/1164001): remove when the migration is finished.
+namespace chromeos {
+using ::ash::SyncTrustedVaultKeys;
+}  // namespace chromeos
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_SYNC_TRUSTED_VAULT_KEYS_H_
diff --git a/ash/components/login/auth/sync_trusted_vault_keys_unittest.cc b/ash/components/login/auth/sync_trusted_vault_keys_unittest.cc
index f6d9333..13a50c0 100644
--- a/ash/components/login/auth/sync_trusted_vault_keys_unittest.cc
+++ b/ash/components/login/auth/sync_trusted_vault_keys_unittest.cc
@@ -9,7 +9,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-namespace chromeos {
+namespace ash {
 namespace {
 
 using testing::ElementsAre;
@@ -144,4 +144,4 @@
                   MatchesRecoveryMethod(kPublicKeyMaterial2, kMethodType2)));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/test_attempt_state.cc b/ash/components/login/auth/test_attempt_state.cc
index 0ad09f8..16952fbb 100644
--- a/ash/components/login/auth/test_attempt_state.cc
+++ b/ash/components/login/auth/test_attempt_state.cc
@@ -9,7 +9,7 @@
 #include "components/user_manager/user_type.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
-namespace chromeos {
+namespace ash {
 
 TestAttemptState::TestAttemptState(std::unique_ptr<UserContext> credentials)
     : AuthAttemptState(std::move(credentials)) {}
@@ -38,4 +38,4 @@
   return cryptohome_code_;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/test_attempt_state.h b/ash/components/login/auth/test_attempt_state.h
index 2318f5e..23f16a4 100644
--- a/ash/components/login/auth/test_attempt_state.h
+++ b/ash/components/login/auth/test_attempt_state.h
@@ -13,7 +13,7 @@
 #include "base/component_export.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
 
-namespace chromeos {
+namespace ash {
 
 class UserContext;
 
@@ -39,12 +39,6 @@
   cryptohome::MountError cryptohome_code() override;
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source code migration is finished.
-namespace ash {
-using ::chromeos::TestAttemptState;
-}
+}  // namespace ash
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_TEST_ATTEMPT_STATE_H_
diff --git a/ash/components/login/auth/user_context.cc b/ash/components/login/auth/user_context.cc
index 296f457..1fcb71c 100644
--- a/ash/components/login/auth/user_context.cc
+++ b/ash/components/login/auth/user_context.cc
@@ -8,7 +8,7 @@
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_names.h"
 
-namespace chromeos {
+namespace ash {
 
 UserContext::UserContext() : account_id_(EmptyAccountId()) {}
 
@@ -299,4 +299,4 @@
   authsession_id_.clear();
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/login/auth/user_context.h b/ash/components/login/auth/user_context.h
index 732522e..72a81bb 100644
--- a/ash/components/login/auth/user_context.h
+++ b/ash/components/login/auth/user_context.h
@@ -21,9 +21,9 @@
 
 namespace user_manager {
 class User;
-}
+}  // namespace user_manager
 
-namespace chromeos {
+namespace ash {
 
 // Information that is passed around while authentication is in progress. The
 // credentials may consist of a |account_id_|, |key_| pair or a GAIA
@@ -190,12 +190,11 @@
   absl::optional<SyncTrustedVaultKeys> sync_trusted_vault_keys_;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-// TODO(https://crbug.com/1164001): remove when the //chrome/browser/chromeos
-// source code migration is finished.
-namespace ash {
-using ::chromeos::UserContext;
-}
+// TODO(https://crbug.com/1164001): remove when the migration is finished.
+namespace chromeos {
+using ::ash::UserContext;
+}  // namespace chromeos
 
 #endif  // ASH_COMPONENTS_LOGIN_AUTH_USER_CONTEXT_H_
diff --git a/ash/components/peripheral_notification/DEPS b/ash/components/peripheral_notification/DEPS
index 20e2454..e032b2f 100644
--- a/ash/components/peripheral_notification/DEPS
+++ b/ash/components/peripheral_notification/DEPS
@@ -7,6 +7,7 @@
   "+services/device/public",
   "+ash/test",
   "+ash/constants",
+  "+third_party/cros_system_api/dbus/typecd",
 ]
 
 specific_include_rules = {
diff --git a/ash/components/peripheral_notification/peripheral_notification_manager.cc b/ash/components/peripheral_notification/peripheral_notification_manager.cc
index 0b2b7dd..54c09ea 100644
--- a/ash/components/peripheral_notification/peripheral_notification_manager.cc
+++ b/ash/components/peripheral_notification/peripheral_notification_manager.cc
@@ -12,6 +12,7 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "services/device/public/mojom/usb_device.mojom.h"
+#include "third_party/cros_system_api/dbus/typecd/dbus-constants.h"
 
 namespace ash {
 
@@ -127,6 +128,18 @@
   RecordConnectivityMetric(PeripheralConnectivityResults::kPeripheralBlocked);
 }
 
+void PeripheralNotificationManager::OnCableWarning(
+    typecd::CableWarningType cable_warning_type) {
+  // Adding function as stub. Will be enabled with flag CL.
+  // TODO: Add a flag to block this function. And behind the flag, call
+  // NotifyInvalidDpCable if the kInvalidDpCable CableWarningType is received.
+}
+
+void PeripheralNotificationManager::NotifyInvalidDpCable() {
+  for (auto& observer : observer_list_)
+    observer.OnInvalidDpCableWarning();
+}
+
 void PeripheralNotificationManager::OnDeviceConnected(
     device::mojom::UsbDeviceInfo* device) {
   if (device->class_code == kBillboardDeviceClassCode) {
diff --git a/ash/components/peripheral_notification/peripheral_notification_manager.h b/ash/components/peripheral_notification/peripheral_notification_manager.h
index 37a0996c..17cce368 100644
--- a/ash/components/peripheral_notification/peripheral_notification_manager.h
+++ b/ash/components/peripheral_notification/peripheral_notification_manager.h
@@ -13,6 +13,7 @@
 #include "base/observer_list_types.h"
 #include "chromeos/dbus/pciguard/pciguard_client.h"
 #include "chromeos/dbus/typecd/typecd_client.h"
+#include "third_party/cros_system_api/dbus/typecd/dbus-constants.h"
 
 namespace device {
 namespace mojom {
@@ -53,6 +54,11 @@
     // recently plugged in Thunderbolt/USB4 device is a billboard device that is
     // not supported by the board.
     virtual void OnBillboardDeviceConnected() = 0;
+
+    // Called to notify user of possibly invalid dp cable. This signal will be
+    // sent by typecd when the partner meets the conditions for DP alternate
+    // mode, but the cable does not.
+    virtual void OnInvalidDpCableWarning() = 0;
   };
 
   // These values are persisted to logs. Entries should not be renumbered and
@@ -65,7 +71,8 @@
     kAltModeFallbackInGuestSession = 4,
     kPeripheralBlocked = 5,
     kBillboardDevice = 6,
-    kMaxValue = kBillboardDevice,
+    kInvalidDpCable = 7,
+    kMaxValue = kInvalidDpCable,
   };
 
   // Sets the global instance. Must be called before any calls to Get().
@@ -99,6 +106,7 @@
 
   // TypecdClient::Observer:
   void OnThunderboltDeviceConnected(bool is_thunderbolt_only) override;
+  void OnCableWarning(typecd::CableWarningType cable_warning_type) override;
 
   // PciguardClient::Observer:
   void OnBlockedThunderboltDeviceConnected(
@@ -109,6 +117,7 @@
   void NotifyGuestModeNotificationReceived(bool is_thunderbolt_only);
   void NotifyPeripheralBlockedReceived();
   void OnBillboardDeviceConnected(bool billboard_is_supported);
+  void NotifyInvalidDpCable();
 
   // Called by unit tests to set up root_prefix_ for simulating the existence
   // of a system folder.
diff --git a/ash/components/peripheral_notification/peripheral_notification_manager_unittest.cc b/ash/components/peripheral_notification/peripheral_notification_manager_unittest.cc
index 6c5ad71..259c90d 100644
--- a/ash/components/peripheral_notification/peripheral_notification_manager_unittest.cc
+++ b/ash/components/peripheral_notification/peripheral_notification_manager_unittest.cc
@@ -18,6 +18,7 @@
 #include "chromeos/dbus/typecd/typecd_client.h"
 #include "services/device/public/cpp/test/fake_usb_device_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/typecd/dbus-constants.h"
 
 namespace {
 const int kUsbConfigWithInterfaces = 1;
@@ -51,6 +52,10 @@
     return num_billboard_notification_calls_;
   }
 
+  size_t num_invalid_dp_cable_notification_calls() const {
+    return num_invalid_dp_cable_notification_calls_;
+  }
+
   bool is_current_guest_device_tbt_only() const {
     return is_current_guest_device_tbt_only_;
   }
@@ -73,11 +78,16 @@
     ++num_billboard_notification_calls_;
   }
 
+  void OnInvalidDpCableWarning() override {
+    ++num_invalid_dp_cable_notification_calls_;
+  }
+
  private:
   size_t num_limited_performance_notification_calls_ = 0u;
   size_t num_guest_notification_calls_ = 0u;
   size_t num_peripheral_blocked_notification_calls_ = 0u;
   size_t num_billboard_notification_calls_ = 0u;
+  size_t num_invalid_dp_cable_notification_calls_ = 0u;
   bool is_current_guest_device_tbt_only_ = false;
 };
 
@@ -149,6 +159,10 @@
     return fake_observer_.num_billboard_notification_calls();
   }
 
+  size_t GetInvalidDpCableNotificationObserverCalls() {
+    return fake_observer_.num_invalid_dp_cable_notification_calls();
+  }
+
   bool GetIsCurrentGuestDeviceTbtOnly() {
     return fake_observer_.is_current_guest_device_tbt_only();
   }
diff --git a/ash/components/phonehub/util/histogram_util.cc b/ash/components/phonehub/util/histogram_util.cc
index 479cc63c..578277e 100644
--- a/ash/components/phonehub/util/histogram_util.cc
+++ b/ash/components/phonehub/util/histogram_util.cc
@@ -74,6 +74,12 @@
   base::UmaHistogramEnumeration("PhoneHub.OptInEntryPoint", entry_point);
 }
 
+void LogCameraRollFeatureOptInEntryPoint(
+    CameraRollOptInEntryPoint entry_point) {
+  base::UmaHistogramEnumeration("PhoneHub.CameraRoll.OptInEntryPoint",
+                                entry_point);
+}
+
 void LogTetherConnectionResult(TetherConnectionResult result) {
   base::UmaHistogramEnumeration(
       "PhoneHub.TaskCompletion.TetherConnection.Result", result);
diff --git a/ash/components/phonehub/util/histogram_util.h b/ash/components/phonehub/util/histogram_util.h
index fe819635..e30895f 100644
--- a/ash/components/phonehub/util/histogram_util.h
+++ b/ash/components/phonehub/util/histogram_util.h
@@ -22,6 +22,17 @@
   kMaxValue = kSettings,
 };
 
+// Enumeration of possible opt-in entry points for Phone Hub Camera Roll
+// feature. Keep in  sync with corresponding enum in
+// tools/metrics/histograms/enums.xml. These  values are persisted to logs.
+// Entries should not be renumbered and numeric values should never be reused.
+enum class CameraRollOptInEntryPoint {
+  kSetupFlow = 0,
+  kOnboardingDialog = 1,
+  kSettings = 2,
+  kMaxValue = kSettings,
+};
+
 // Enumeration of results of a tethering connection attempt.
 enum class TetherConnectionResult {
   kAttemptConnection = 0,
@@ -41,6 +52,9 @@
 // Logs a given opt-in |entry_point| for the PhoneHub feature.
 void LogFeatureOptInEntryPoint(OptInEntryPoint entry_point);
 
+// Logs a given opt-in |entry_point| for the PhoneHub Camera Roll feature.
+void LogCameraRollFeatureOptInEntryPoint(CameraRollOptInEntryPoint entry_point);
+
 // Logs a given |result| of a tethering connection attempt.
 void LogTetherConnectionResult(TetherConnectionResult result);
 
diff --git a/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc b/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc
index c1b344b..d846deb 100644
--- a/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc
+++ b/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc
@@ -192,7 +192,8 @@
   }
 
   const base::DictionaryValue* all_user_prefs_dict =
-      local_state_->GetDictionary(prefs::kEasyUnlockLocalStateUserPrefs);
+      &base::Value::AsDictionaryValue(
+          *local_state_->GetDictionary(prefs::kEasyUnlockLocalStateUserPrefs));
   DCHECK(all_user_prefs_dict);
 
   const base::DictionaryValue* current_user_prefs;
diff --git a/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc b/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc
index 58bb791..9af9b30 100644
--- a/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc
+++ b/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc
@@ -218,7 +218,8 @@
 
 bool ProximityAuthProfilePrefManager::HasShownLoginDisabledMessage() const {
   const base::DictionaryValue* all_user_prefs_dict =
-      local_state_->GetDictionary(prefs::kEasyUnlockLocalStateUserPrefs);
+      &base::Value::AsDictionaryValue(
+          *local_state_->GetDictionary(prefs::kEasyUnlockLocalStateUserPrefs));
   const base::DictionaryValue* current_user_prefs;
   if (!all_user_prefs_dict ||
       !all_user_prefs_dict->GetDictionaryWithoutPathExpansion(
diff --git a/ash/components/tether/persistent_host_scan_cache_impl.cc b/ash/components/tether/persistent_host_scan_cache_impl.cc
index f3ef0dae..233483a7 100644
--- a/ash/components/tether/persistent_host_scan_cache_impl.cc
+++ b/ash/components/tether/persistent_host_scan_cache_impl.cc
@@ -103,7 +103,7 @@
 
 std::unordered_map<std::string, HostScanCacheEntry>
 PersistentHostScanCacheImpl::GetStoredCacheEntries() {
-  const base::ListValue* cache_entry_list =
+  const base::Value* cache_entry_list =
       pref_service_->GetList(prefs::kHostScanCache);
   DCHECK(cache_entry_list);
 
diff --git a/ash/components/tether/tether_host_response_recorder.cc b/ash/components/tether/tether_host_response_recorder.cc
index f91e114..793f8e0e 100644
--- a/ash/components/tether/tether_host_response_recorder.cc
+++ b/ash/components/tether/tether_host_response_recorder.cc
@@ -77,7 +77,7 @@
 bool TetherHostResponseRecorder::AddRecentResponse(
     const std::string& device_id,
     const std::string& pref_name) {
-  const base::ListValue* ids = pref_service_->GetList(pref_name);
+  const base::Value* ids = pref_service_->GetList(pref_name);
   base::Value::ConstListView ids_list = ids->GetList();
 
   std::string first_device_id_in_list;
@@ -112,7 +112,7 @@
     const std::string& pref_name) const {
   std::vector<std::string> device_ids;
 
-  const base::ListValue* ids = pref_service_->GetList(pref_name);
+  const base::Value* ids = pref_service_->GetList(pref_name);
   if (!ids)
     return device_ids;
 
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 29c368c0..b5187451 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -196,7 +196,7 @@
 
 // Enable Big GL when using Borealis.
 const base::Feature kBorealisBigGl{"BorealisBigGl",
-                                   base::FEATURE_DISABLED_BY_DEFAULT};
+                                   base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable experimental disk management changes for Borealis.
 const base::Feature kBorealisDiskManagement{"BorealisDiskManagement",
@@ -696,14 +696,14 @@
 // transfer or access it later.
 const base::Feature kHoldingSpaceInProgressDownloadsIntegration{
     "HoldingSpaceInProgressDownloadsIntegration",
-    base::FEATURE_ENABLED_BY_DEFAULT};
+    base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables in-progress downloads notification suppression with the productivity
 // feature that aims to reduce context switching by enabling users to collect
 // content and transfer or access it later.
 const base::Feature kHoldingSpaceInProgressDownloadsNotificationSuppression{
     "HoldingSpaceInProgressNotificationSuppression",
-    base::FEATURE_ENABLED_BY_DEFAULT};
+    base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables incognito profile integration with the productivity feature that
 // aims to reduce context switching by enabling users to collect content and
@@ -792,6 +792,11 @@
 const base::Feature kForceProfileMigrationCompletion{
     "ForceProfileMigrationCompletion", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable this to turn on profile migration for non-googlers. Currently the
+// feature is only limited to googlers only.
+const base::Feature kLacrosProfileMigrationForAnyUser{
+    "LacrosProfileMigrationForAnyUser", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables or disables Language Packs for Handwriting Recognition.
 // This feature turns on the download of language-specific Handwriting models
 // via DLC.
@@ -1270,9 +1275,9 @@
 const base::Feature kWakeOnWifiAllowed{"WakeOnWifiAllowed",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enable new wallpaper experience in WebUI inside system settings.
+// Enable new wallpaper experience SWA.
 const base::Feature kWallpaperWebUI{"WallpaperWebUI",
-                                    base::FEATURE_DISABLED_BY_DEFAULT};
+                                    base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable full screen wallpaper preview in new wallpaper experience. Requires
 // |kWallpaperWebUI| to also be enabled.
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 262e435..5626b749 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -298,6 +298,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kForceProfileMigrationCompletion;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kLacrosProfileMigrationForAnyUser;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kLanguagePacksHandwriting;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kLanguageSettingsUpdate2;
diff --git a/ash/constants/ash_switches.cc b/ash/constants/ash_switches.cc
index 40072fa..be2014c7 100644
--- a/ash/constants/ash_switches.cc
+++ b/ash/constants/ash_switches.cc
@@ -522,10 +522,9 @@
 // Forces first-run UI to be shown for every login.
 const char kForceFirstRunUI[] = "force-first-run-ui";
 
-// Forces Hardware ID check (happens during OOBE) to fail. Should be used only
-// for testing.
-const char kForceHWIDCheckFailureForTest[] =
-    "force-hwid-check-failure-for-test";
+// Forces Hardware ID check (happens during OOBE) to fail or succeed. Possible
+// values: "failure" or "success". Should be used only for testing.
+const char kForceHWIDCheckResultForTest[] = "force-hwid-check-result-for-test";
 
 // Force enables the Happiness Tracking System for the device. This ignores
 // user profile check and time limits and shows the notification every time
diff --git a/ash/constants/ash_switches.h b/ash/constants/ash_switches.h
index b6636ce..66a1b93 100644
--- a/ash/constants/ash_switches.h
+++ b/ash/constants/ash_switches.h
@@ -176,7 +176,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kForceDevToolsAvailable[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kForceFirstRunUI[];
 COMPONENT_EXPORT(ASH_CONSTANTS)
-extern const char kForceHWIDCheckFailureForTest[];
+extern const char kForceHWIDCheckResultForTest[];
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kForceHappinessTrackingSystem[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kForceLaunchBrowser[];
diff --git a/ash/detachable_base/detachable_base_handler.cc b/ash/detachable_base/detachable_base_handler.cc
index 5980efb..efa333b 100644
--- a/ash/detachable_base/detachable_base_handler.cc
+++ b/ash/detachable_base/detachable_base_handler.cc
@@ -213,7 +213,7 @@
   if (user.is_ephemeral)
     return "";
 
-  const base::DictionaryValue* detachable_base_info =
+  const base::Value* detachable_base_info =
       local_state_->GetDictionary(prefs::kDetachableBaseDevices);
   const base::Value* last_used = detachable_base_info->FindPathOfType(
       {GetKeyForPrefs(user.account_id), kLastUsedByUserPrefKey},
diff --git a/ash/display/display_prefs_unittest.cc b/ash/display/display_prefs_unittest.cc
index c27415e..c2ac2f29 100644
--- a/ash/display/display_prefs_unittest.cc
+++ b/ash/display/display_prefs_unittest.cc
@@ -241,8 +241,8 @@
   }
 
   const base::DictionaryValue* ReadPropertiesForDisplay(int64_t display_id) {
-    const base::DictionaryValue* properties =
-        local_state()->GetDictionary(prefs::kDisplayProperties);
+    const base::DictionaryValue* properties = &base::Value::AsDictionaryValue(
+        *local_state()->GetDictionary(prefs::kDisplayProperties));
     EXPECT_TRUE(properties);
     const base::DictionaryValue* property = nullptr;
     EXPECT_TRUE(
@@ -262,7 +262,7 @@
                     "mirroring_source_id="
                  << source_id << " and mirroring_destination_ids="
                  << base::JoinString(expected_dest_id_strs, ","));
-    const base::DictionaryValue* prefs =
+    const base::Value* prefs =
         local_state()->GetDictionary(prefs::kDisplayMixedMirrorModeParams);
     ASSERT_TRUE(prefs);
     EXPECT_THAT(prefs->FindStringKey("mirroring_source_id"),
@@ -448,8 +448,8 @@
   display_manager()->SetTouchCalibrationData(id2, point_pair_quad_2,
                                              touch_size_1, touchdevice_3);
 
-  const base::DictionaryValue* displays =
-      local_state()->GetDictionary(prefs::kSecondaryDisplays);
+  const base::DictionaryValue* displays = &base::Value::AsDictionaryValue(
+      *local_state()->GetDictionary(prefs::kSecondaryDisplays));
   const base::DictionaryValue* layout_value = nullptr;
   std::string key = base::NumberToString(id1) + "," + base::NumberToString(id2);
   std::string dummy_key =
@@ -465,12 +465,12 @@
   EXPECT_EQ(dummy_layout->placement_list[0].offset,
             stored_layout.placement_list[0].offset);
 
-  const base::ListValue* external_display_mirror_info =
+  const base::Value* external_display_mirror_info =
       local_state()->GetList(prefs::kExternalDisplayMirrorInfo);
   EXPECT_EQ(0U, external_display_mirror_info->GetList().size());
 
-  const base::DictionaryValue* properties =
-      local_state()->GetDictionary(prefs::kDisplayProperties);
+  const base::DictionaryValue* properties = &base::Value::AsDictionaryValue(
+      *local_state()->GetDictionary(prefs::kDisplayProperties));
   const base::DictionaryValue* property = nullptr;
   EXPECT_TRUE(properties->GetDictionary(base::NumberToString(id1), &property));
   EXPECT_EQ(1, property->FindIntKey("rotation"));
@@ -669,8 +669,8 @@
                       base::OnceClosure()));
   UpdateDisplay("500x400#500x400|400x300|300x200");
 
-  const base::DictionaryValue* properties =
-      local_state()->GetDictionary(prefs::kDisplayProperties);
+  const base::DictionaryValue* properties = &base::Value::AsDictionaryValue(
+      *local_state()->GetDictionary(prefs::kDisplayProperties));
   const base::DictionaryValue* property = nullptr;
   EXPECT_TRUE(properties->GetDictionary(base::NumberToString(id), &property));
   EXPECT_FALSE(property->FindIntKey("width"));
@@ -703,8 +703,8 @@
   ASSERT_EQ(id1, display_manager_test.GetSecondaryDisplay().id());
 
   std::string key = base::NumberToString(id1) + "," + base::NumberToString(id2);
-  const base::DictionaryValue* displays =
-      local_state()->GetDictionary(prefs::kSecondaryDisplays);
+  const base::DictionaryValue* displays = &base::Value::AsDictionaryValue(
+      *local_state()->GetDictionary(prefs::kSecondaryDisplays));
   // Initial saved value is swapped.
   {
     const base::DictionaryValue* new_value = nullptr;
@@ -981,8 +981,8 @@
   shell->screen_orientation_controller()->OnAccelerometerUpdated(update);
   EXPECT_EQ(display::Display::ROTATE_90, GetCurrentInternalDisplayRotation());
 
-  const base::DictionaryValue* properties =
-      local_state()->GetDictionary(prefs::kDisplayProperties);
+  const base::DictionaryValue* properties = &base::Value::AsDictionaryValue(
+      *local_state()->GetDictionary(prefs::kDisplayProperties));
   const base::DictionaryValue* property = nullptr;
   EXPECT_TRUE(properties->GetDictionary(
       base::NumberToString(display::Display::InternalDisplayId()), &property));
@@ -991,7 +991,8 @@
   // Trigger a save, the acceleration rotation should not be saved as the user
   // rotation.
   display_prefs()->MaybeStoreDisplayPrefs();
-  properties = local_state()->GetDictionary(prefs::kDisplayProperties);
+  properties = &base::Value::AsDictionaryValue(
+      *local_state()->GetDictionary(prefs::kDisplayProperties));
   property = nullptr;
   EXPECT_TRUE(properties->GetDictionary(
       base::NumberToString(display::Display::InternalDisplayId()), &property));
@@ -1009,8 +1010,8 @@
                                                     current_rotation_lock);
   EXPECT_TRUE(local_state()->HasPrefPath(prefs::kDisplayRotationLock));
 
-  const base::DictionaryValue* properties =
-      local_state()->GetDictionary(prefs::kDisplayRotationLock);
+  const base::DictionaryValue* properties = &base::Value::AsDictionaryValue(
+      *local_state()->GetDictionary(prefs::kDisplayRotationLock));
   absl::optional<bool> rotation_lock = properties->FindBoolKey("lock");
   ASSERT_TRUE(rotation_lock.has_value());
   EXPECT_EQ(current_rotation_lock, rotation_lock.value());
@@ -1032,7 +1033,7 @@
                                                     current_rotation_lock);
   EXPECT_TRUE(local_state()->HasPrefPath(prefs::kDisplayRotationLock));
 
-  const base::DictionaryValue* properties =
+  const base::Value* properties =
       local_state()->GetDictionary(prefs::kDisplayRotationLock);
   absl::optional<bool> rotation_lock = properties->FindBoolKey("lock");
   ASSERT_TRUE(rotation_lock.has_value());
@@ -1055,7 +1056,7 @@
                                                     current_rotation_lock);
   EXPECT_TRUE(local_state()->HasPrefPath(prefs::kDisplayRotationLock));
 
-  const base::DictionaryValue* properties =
+  const base::Value* properties =
       local_state()->GetDictionary(prefs::kDisplayRotationLock);
   absl::optional<bool> rotation_lock = properties->FindBoolKey("lock");
   ASSERT_TRUE(rotation_lock.has_value());
@@ -1128,7 +1129,7 @@
 
   EXPECT_TRUE(local_state()->HasPrefPath(prefs::kDisplayRotationLock));
 
-  const base::DictionaryValue* properties =
+  const base::Value* properties =
       local_state()->GetDictionary(prefs::kDisplayRotationLock);
   absl::optional<bool> rotation_lock = properties->FindBoolKey("lock");
   EXPECT_TRUE(rotation_lock.has_value());
@@ -1145,7 +1146,8 @@
       display::Screen::GetScreen()->GetPrimaryDisplay().size().ToString());
 
   const base::DictionaryValue* secondary_displays =
-      local_state()->GetDictionary(prefs::kSecondaryDisplays);
+      &base::Value::AsDictionaryValue(
+          *local_state()->GetDictionary(prefs::kSecondaryDisplays));
   const base::DictionaryValue* new_value = nullptr;
   EXPECT_TRUE(secondary_displays->GetDictionary(
       display::DisplayIdListToString(list), &new_value));
@@ -1154,8 +1156,8 @@
   EXPECT_TRUE(display::JsonToDisplayLayout(*new_value, &stored_layout));
   EXPECT_TRUE(stored_layout.default_unified);
 
-  const base::DictionaryValue* displays =
-      local_state()->GetDictionary(prefs::kDisplayProperties);
+  const base::DictionaryValue* displays = &base::Value::AsDictionaryValue(
+      *local_state()->GetDictionary(prefs::kDisplayProperties));
   int64_t unified_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
   EXPECT_FALSE(
       displays->GetDictionary(base::NumberToString(unified_id), &new_value));
@@ -1275,7 +1277,8 @@
   display_manager()->SetLayoutForCurrentDisplays(builder.Build());
 
   const base::DictionaryValue* secondary_displays =
-      local_state()->GetDictionary(prefs::kSecondaryDisplays);
+      &base::Value::AsDictionaryValue(
+          *local_state()->GetDictionary(prefs::kSecondaryDisplays));
   const base::DictionaryValue* new_value = nullptr;
   EXPECT_TRUE(secondary_displays->GetDictionary(
       display::DisplayIdListToString(list), &new_value));
@@ -1407,7 +1410,7 @@
   external_display_mirror_info.emplace(first_display_masked_id);
   StoreExternalDisplayMirrorInfo(external_display_mirror_info);
   LoadDisplayPreferences();
-  const base::ListValue* pref_external_display_mirror_info =
+  const base::Value* pref_external_display_mirror_info =
       local_state()->GetList(prefs::kExternalDisplayMirrorInfo);
   EXPECT_EQ(1U, pref_external_display_mirror_info->GetList().size());
   EXPECT_EQ(base::NumberToString(first_display_masked_id),
@@ -1555,8 +1558,8 @@
   EXPECT_EQ(first_display_id, destination_ids[0]);
 
   // Check the preferences.
-  const base::DictionaryValue* pref_data =
-      local_state()->GetDictionary(prefs::kDisplayMixedMirrorModeParams);
+  const base::DictionaryValue* pref_data = &base::Value::AsDictionaryValue(
+      *local_state()->GetDictionary(prefs::kDisplayMixedMirrorModeParams));
   EXPECT_EQ(base::NumberToString(internal_display_id),
             pref_data->FindKey("mirroring_source_id")->GetString());
   const base::Value* destination_ids_value =
@@ -1580,8 +1583,8 @@
   EXPECT_EQ(second_display_id, destination_ids[0]);
 
   // Check the preferences.
-  pref_data =
-      local_state()->GetDictionary(prefs::kDisplayMixedMirrorModeParams);
+  pref_data = &base::Value::AsDictionaryValue(
+      *local_state()->GetDictionary(prefs::kDisplayMixedMirrorModeParams));
   EXPECT_EQ(base::NumberToString(first_display_id),
             pref_data->FindKey("mirroring_source_id")->GetString());
   destination_ids_value = pref_data->FindKey("mirroring_destination_ids");
@@ -1594,8 +1597,8 @@
   EXPECT_FALSE(display_manager()->IsInMirrorMode());
 
   // Check the preferences.
-  pref_data =
-      local_state()->GetDictionary(prefs::kDisplayMixedMirrorModeParams);
+  pref_data = &base::Value::AsDictionaryValue(
+      *local_state()->GetDictionary(prefs::kDisplayMixedMirrorModeParams));
   EXPECT_TRUE(pref_data->DictEmpty());
 }
 
diff --git a/ash/keyboard/keyboard_controller_impl.cc b/ash/keyboard/keyboard_controller_impl.cc
index 42989eb4..a6767181 100644
--- a/ash/keyboard/keyboard_controller_impl.cc
+++ b/ash/keyboard/keyboard_controller_impl.cc
@@ -67,7 +67,7 @@
 bool GetVirtualKeyboardFeatureValue(PrefService* prefs,
                                     const std::string& feature_path) {
   DCHECK(prefs);
-  const base::DictionaryValue* features =
+  const base::Value* features =
       prefs->GetDictionary(prefs::kAccessibilityVirtualKeyboardFeatures);
 
   if (!features)
diff --git a/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.cc b/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.cc
index 9afa60d..e5c4d38 100644
--- a/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.cc
+++ b/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.cc
@@ -4,6 +4,7 @@
 
 #include "ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h"
 
+#include <algorithm>
 #include <array>
 #include <utility>
 
@@ -11,6 +12,7 @@
 #include "ash/public/cpp/ambient/common/ambient_settings.h"
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/check.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -86,7 +88,11 @@
     OnScreenUpdateInfoFetchedCallback callback) {
   ash::ScreenUpdate update;
 
-  for (int i = 0; i < num_topics; i++) {
+  int num_topics_to_return =
+      custom_num_topics_to_return_.has_value()
+          ? std::min(custom_num_topics_to_return_.value(), num_topics)
+          : num_topics;
+  for (int i = 0; i < num_topics_to_return; i++) {
     ash::AmbientModeTopic topic;
     topic.url = kFakeUrl;
     topic.details = kFakeDetails;
@@ -171,6 +177,12 @@
   }
 }
 
+void FakeAmbientBackendControllerImpl::SetFetchScreenUpdateInfoResponseSize(
+    int num_topics_to_return) {
+  DCHECK_GE(num_topics_to_return, 0);
+  custom_num_topics_to_return_.emplace(num_topics_to_return);
+}
+
 bool FakeAmbientBackendControllerImpl::IsFetchSettingsAndAlbumsPending() const {
   return !pending_fetch_settings_albums_callback_.is_null();
 }
diff --git a/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h b/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h
index baa85a7..fea5ae0 100644
--- a/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h
+++ b/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h
@@ -51,6 +51,12 @@
       bool success,
       const absl::optional<AmbientSettings>& settings = absl::nullopt);
 
+  // Simulates the reply for FetchScreenUpdateInfo(). All future calls to
+  // FetchScreenUpdateInfo() will return the number of topics specified by
+  // |num_topics_to_return|, bounded by the |num_topics| argument requested in
+  // FetchScreenUpdateInfo().
+  void SetFetchScreenUpdateInfoResponseSize(int num_topics_to_return);
+
   // Whether there is a pending FetchSettingsAndAlbums() request.
   bool IsFetchSettingsAndAlbumsPending() const;
 
@@ -80,6 +86,8 @@
   bool has_related_image_ = true;
 
   ::ambient::TopicType topic_type_ = ::ambient::TopicType::kCulturalInstitute;
+
+  absl::optional<int> custom_num_topics_to_return_;
 };
 
 }  // namespace ash
diff --git a/ash/public/cpp/app_list/app_list_color_provider.h b/ash/public/cpp/app_list/app_list_color_provider.h
index d698065..39564ef9 100644
--- a/ash/public/cpp/app_list/app_list_color_provider.h
+++ b/ash/public/cpp/app_list/app_list_color_provider.h
@@ -54,6 +54,7 @@
   virtual float GetInkDropOpacity(
       SkColor bg_color = gfx::kPlaceholderColor) const = 0;
   virtual SkColor GetSearchResultViewHighlightColor() const = 0;
+  virtual SkColor GetTextColorURL() const = 0;
 
  protected:
   AppListColorProvider();
diff --git a/ash/public/cpp/app_list/app_list_features.h b/ash/public/cpp/app_list/app_list_features.h
index f868b14..d7247dc 100644
--- a/ash/public/cpp/app_list/app_list_features.h
+++ b/ash/public/cpp/app_list/app_list_features.h
@@ -62,11 +62,6 @@
 // non empty queries.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableAggregatedMlSearchRanking;
 
-// Enables the new app dragging in the launcher. When the users drags an app
-// within the launcher, this flag will enable the new cardified state, where
-// apps grid pages are scaled down and shown a background card.
-ASH_PUBLIC_EXPORT extern const base::Feature kNewDragSpecInLauncher;
-
 // Enables normalization of search results in the launcher.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableLauncherSearchNormalization;
 
@@ -91,7 +86,6 @@
 ASH_PUBLIC_EXPORT bool IsForceShowContinueSectionEnabled();
 ASH_PUBLIC_EXPORT bool IsLauncherSettingsSearchEnabled();
 ASH_PUBLIC_EXPORT bool IsAggregatedMlSearchRankingEnabled();
-ASH_PUBLIC_EXPORT bool IsNewDragSpecInLauncherEnabled();
 ASH_PUBLIC_EXPORT bool IsLauncherSearchNormalizationEnabled();
 ASH_PUBLIC_EXPORT bool IsCategoricalSearchEnabled();
 
diff --git a/ash/public/cpp/shelf_prefs.cc b/ash/public/cpp/shelf_prefs.cc
index 3686dd5..6fef0204 100644
--- a/ash/public/cpp/shelf_prefs.cc
+++ b/ash/public/cpp/shelf_prefs.cc
@@ -55,8 +55,8 @@
   std::string pref_key = base::NumberToString(display_id);
   bool has_per_display_prefs = false;
   if (!pref_key.empty()) {
-    const base::DictionaryValue* shelf_prefs =
-        prefs->GetDictionary(prefs::kShelfPreferences);
+    const base::DictionaryValue* shelf_prefs = &base::Value::AsDictionaryValue(
+        *prefs->GetDictionary(prefs::kShelfPreferences));
     const base::DictionaryValue* display_pref = nullptr;
     std::string per_display_value;
     if (shelf_prefs->GetDictionary(pref_key, &display_pref) &&
@@ -95,7 +95,8 @@
 
   // Avoid DictionaryPrefUpdate's notifications for read but unmodified prefs.
   const base::DictionaryValue* current_shelf_prefs =
-      prefs->GetDictionary(prefs::kShelfPreferences);
+      &base::Value::AsDictionaryValue(
+          *prefs->GetDictionary(prefs::kShelfPreferences));
   DCHECK(current_shelf_prefs);
   std::string display_key = base::NumberToString(display_id);
   const base::DictionaryValue* current_display_prefs = nullptr;
diff --git a/ash/public/cpp/test/test_app_list_color_provider.cc b/ash/public/cpp/test/test_app_list_color_provider.cc
index 7b333a0..3683dce 100644
--- a/ash/public/cpp/test/test_app_list_color_provider.cc
+++ b/ash/public/cpp/test/test_app_list_color_provider.cc
@@ -123,4 +123,8 @@
   return SkColorSetA(SK_ColorWHITE, 0x0D);
 }
 
+SkColor TestAppListColorProvider::GetTextColorURL() const {
+  return gfx::kGoogleBlue600;
+}
+
 }  // namespace ash
diff --git a/ash/public/cpp/test/test_app_list_color_provider.h b/ash/public/cpp/test/test_app_list_color_provider.h
index 4b44b84..e48f335 100644
--- a/ash/public/cpp/test/test_app_list_color_provider.h
+++ b/ash/public/cpp/test/test_app_list_color_provider.h
@@ -46,6 +46,7 @@
   float GetInkDropOpacity(
       SkColor bg_color = gfx::kPlaceholderColor) const override;
   SkColor GetSearchResultViewHighlightColor() const override;
+  SkColor GetTextColorURL() const override;
 };
 
 }  // namespace ash
diff --git a/ash/session/fullscreen_controller.cc b/ash/session/fullscreen_controller.cc
index b8a461a..a6c7dfa 100644
--- a/ash/session/fullscreen_controller.cc
+++ b/ash/session/fullscreen_controller.cc
@@ -91,8 +91,8 @@
 
   // Check if the URL is exempt from the notification by user pref.
   auto* prefs = session_controller->GetPrimaryUserPrefService();
-  const base::ListValue* url_exempt_list =
-      prefs->GetList(prefs::kFullscreenNotificationUrlExemptList);
+  const base::ListValue* url_exempt_list = &base::Value::AsListValue(
+      *prefs->GetList(prefs::kFullscreenNotificationUrlExemptList));
   url_matcher::URLMatcher url_matcher;
   policy::url_util::AddAllowFilters(&url_matcher, url_exempt_list);
   if (!url_matcher.MatchURL(url).empty())
diff --git a/ash/shelf/home_button_unittest.cc b/ash/shelf/home_button_unittest.cc
index 2a44548..44a79df 100644
--- a/ash/shelf/home_button_unittest.cc
+++ b/ash/shelf/home_button_unittest.cc
@@ -276,22 +276,25 @@
       start, end, base::Milliseconds(100), 4 /* steps */);
   GetAppListTestHelper()->WaitUntilIdle();
   GetAppListTestHelper()->CheckVisibility(true);
-  GetAppListTestHelper()->CheckState(AppListViewState::kPeeking);
+  if (!features::IsProductivityLauncherEnabled()) {
+    // ProductivityLauncher does not have states like peeking.
+    GetAppListTestHelper()->CheckState(AppListViewState::kPeeking);
 
-  // Closing the app list.
-  GetAppListTestHelper()->DismissAndRunLoop();
-  GetAppListTestHelper()->CheckVisibility(false);
-  GetAppListTestHelper()->CheckState(AppListViewState::kClosed);
+    // Closing the app list.
+    GetAppListTestHelper()->DismissAndRunLoop();
+    GetAppListTestHelper()->CheckVisibility(false);
+    GetAppListTestHelper()->CheckState(AppListViewState::kClosed);
 
-  // Swiping above the threshold should trigger a fullscreen app list.
-  end.set_y(shelf->GetIdealBounds().bottom() -
-            AppListView::kDragSnapToPeekingThreshold - 10);
-  GetEventGenerator()->GestureScrollSequence(
-      start, end, base::Milliseconds(100), 4 /* steps */);
-  base::RunLoop().RunUntilIdle();
-  GetAppListTestHelper()->WaitUntilIdle();
-  GetAppListTestHelper()->CheckVisibility(true);
-  GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps);
+    // Swiping above the threshold should trigger a fullscreen app list.
+    end.set_y(shelf->GetIdealBounds().bottom() -
+              AppListView::kDragSnapToPeekingThreshold - 10);
+    GetEventGenerator()->GestureScrollSequence(
+        start, end, base::Milliseconds(100), 4 /* steps */);
+    base::RunLoop().RunUntilIdle();
+    GetAppListTestHelper()->WaitUntilIdle();
+    GetAppListTestHelper()->CheckVisibility(true);
+    GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps);
+  }
 }
 
 TEST_P(HomeButtonTest, ClickToOpenAppList) {
@@ -310,27 +313,31 @@
   GetEventGenerator()->ClickLeftButton();
   GetAppListTestHelper()->WaitUntilIdle();
   GetAppListTestHelper()->CheckVisibility(true);
-  GetAppListTestHelper()->CheckState(AppListViewState::kPeeking);
+  // ProductivityLauncher does not have states like peeking.
+  if (!features::IsProductivityLauncherEnabled())
+    GetAppListTestHelper()->CheckState(AppListViewState::kPeeking);
   GetEventGenerator()->ClickLeftButton();
   GetAppListTestHelper()->WaitUntilIdle();
   GetAppListTestHelper()->CheckVisibility(false);
-  GetAppListTestHelper()->CheckState(AppListViewState::kClosed);
+  if (!features::IsProductivityLauncherEnabled()) {
+    GetAppListTestHelper()->CheckState(AppListViewState::kClosed);
 
-  // Shift-click should open the app list in fullscreen.
-  GetEventGenerator()->set_flags(ui::EF_SHIFT_DOWN);
-  GetEventGenerator()->ClickLeftButton();
-  GetEventGenerator()->set_flags(0);
-  GetAppListTestHelper()->WaitUntilIdle();
-  GetAppListTestHelper()->CheckVisibility(true);
-  GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps);
+    // Shift-click should open the app list in fullscreen.
+    GetEventGenerator()->set_flags(ui::EF_SHIFT_DOWN);
+    GetEventGenerator()->ClickLeftButton();
+    GetEventGenerator()->set_flags(0);
+    GetAppListTestHelper()->WaitUntilIdle();
+    GetAppListTestHelper()->CheckVisibility(true);
+    GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps);
 
-  // Another shift-click should close the app list.
-  GetEventGenerator()->set_flags(ui::EF_SHIFT_DOWN);
-  GetEventGenerator()->ClickLeftButton();
-  GetEventGenerator()->set_flags(0);
-  GetAppListTestHelper()->WaitUntilIdle();
-  GetAppListTestHelper()->CheckVisibility(false);
-  GetAppListTestHelper()->CheckState(AppListViewState::kClosed);
+    // Another shift-click should close the app list.
+    GetEventGenerator()->set_flags(ui::EF_SHIFT_DOWN);
+    GetEventGenerator()->ClickLeftButton();
+    GetEventGenerator()->set_flags(0);
+    GetAppListTestHelper()->WaitUntilIdle();
+    GetAppListTestHelper()->CheckVisibility(false);
+    GetAppListTestHelper()->CheckState(AppListViewState::kClosed);
+  }
 }
 
 TEST_P(HomeButtonTest, ClickToOpenAppListInTabletMode) {
diff --git a/ash/shelf/launcher_nudge_controller.cc b/ash/shelf/launcher_nudge_controller.cc
index cb520d5..7e09aac 100644
--- a/ash/shelf/launcher_nudge_controller.cc
+++ b/ash/shelf/launcher_nudge_controller.cc
@@ -46,7 +46,7 @@
 
 // Gets the timestamp when the nudge was last shown.
 base::Time GetLastShownTime(PrefService* prefs) {
-  const base::DictionaryValue* dictionary =
+  const base::Value* dictionary =
       prefs->GetDictionary(prefs::kShelfLauncherNudge);
   if (!dictionary)
     return base::Time();
@@ -59,7 +59,7 @@
 // set if the user has logged in before the launcher nudge feature was
 // enabled.
 base::Time GetFirstLoginTime(PrefService* prefs) {
-  const base::DictionaryValue* dictionary =
+  const base::Value* dictionary =
       prefs->GetDictionary(prefs::kShelfLauncherNudge);
   if (!dictionary)
     return base::Time();
@@ -70,7 +70,7 @@
 
 // Returns true if the launcher has been shown before.
 bool WasLauncherShownPreviously(PrefService* prefs) {
-  const base::DictionaryValue* dictionary =
+  const base::Value* dictionary =
       prefs->GetDictionary(prefs::kShelfLauncherNudge);
   if (!dictionary)
     return false;
@@ -113,7 +113,7 @@
 
 // static
 int LauncherNudgeController::GetShownCount(PrefService* prefs) {
-  const base::DictionaryValue* dictionary =
+  const base::Value* dictionary =
       prefs->GetDictionary(prefs::kShelfLauncherNudge);
   if (!dictionary)
     return 0;
diff --git a/ash/shelf/shelf_app_button.cc b/ash/shelf/shelf_app_button.cc
index 17a61db..eb27d2b 100644
--- a/ash/shelf/shelf_app_button.cc
+++ b/ash/shelf/shelf_app_button.cc
@@ -851,14 +851,8 @@
 void ShelfAppButton::InkDropRippleAnimationEnded(views::InkDropState state) {
   // Notify the host view of the ink drop to be hidden at the end of ink drop
   // animation.
-  // TODO(https://crbug.com/1126491): Ideally ink drops should be hidden at the
-  // end. However, it may not be guaranteed in reality. Therefore decrease the
-  // ink drop count when `state` is DEACTIVATED. If this tentative fixing works,
-  // we should fix the code so that an ink drop's final state is HIDDEN.
-  if (state == views::InkDropState::HIDDEN ||
-      state == views::InkDropState::DEACTIVATED) {
+  if (state == views::InkDropState::HIDDEN)
     SetInkDropAnimationStarted(/*started=*/false);
-  }
 }
 
 void ShelfAppButton::UpdateState() {
diff --git a/ash/shell.cc b/ash/shell.cc
index 5efd0ff..c8f7451 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -139,6 +139,7 @@
 #include "ash/system/toast/toast_manager_impl.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/unified/hps_notify_controller.h"
+#include "ash/system/usb_peripheral/usb_peripheral_notification_controller.h"
 #include "ash/touch/ash_touch_transform_controller.h"
 #include "ash/touch/touch_devices_controller.h"
 #include "ash/tray_action/tray_action.h"
@@ -898,6 +899,8 @@
 
   pcie_peripheral_notification_controller_.reset();
 
+  usb_peripheral_notification_controller_.reset();
+
   message_center_ash_impl_.reset();
 
   // Destroys the MessageCenter singleton, so must happen late.
@@ -981,6 +984,10 @@
       std::make_unique<PciePeripheralNotificationController>(
           message_center::MessageCenter::Get());
 
+  usb_peripheral_notification_controller_ =
+      std::make_unique<UsbPeripheralNotificationController>(
+          message_center::MessageCenter::Get());
+
   accessibility_focus_ring_controller_ =
       std::make_unique<AccessibilityFocusRingControllerImpl>();
   accessibility_delegate_.reset(shell_delegate_->CreateAccessibilityDelegate());
diff --git a/ash/shell.h b/ash/shell.h
index 1ae942b4..c8f7061 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -163,6 +163,7 @@
 class ParentAccessController;
 class PartialMagnifierController;
 class PciePeripheralNotificationController;
+class UsbPeripheralNotificationController;
 class PeripheralBatteryListener;
 class PeripheralBatteryNotifier;
 class PersistentDesksBarController;
@@ -602,6 +603,12 @@
   pcie_peripheral_notification_controller() {
     return pcie_peripheral_notification_controller_.get();
   }
+
+  UsbPeripheralNotificationController*
+  usb_peripheral_notification_controller() {
+    return usb_peripheral_notification_controller_.get();
+  }
+
   OcclusionTrackerPauser* occlusion_tracker_pauser() {
     return occlusion_tracker_pauser_.get();
   }
@@ -767,6 +774,8 @@
   std::unique_ptr<ParentAccessController> parent_access_controller_;
   std::unique_ptr<PciePeripheralNotificationController>
       pcie_peripheral_notification_controller_;
+  std::unique_ptr<UsbPeripheralNotificationController>
+      usb_peripheral_notification_controller_;
   std::unique_ptr<PersistentDesksBarController>
       persistent_desks_bar_controller_;
   std::unique_ptr<ResizeShadowController> resize_shadow_controller_;
diff --git a/ash/strings/ash_strings_en-GB.xtb b/ash/strings/ash_strings_en-GB.xtb
index e04e7d7..e3181494 100644
--- a/ash/strings/ash_strings_en-GB.xtb
+++ b/ash/strings/ash_strings_en-GB.xtb
@@ -131,6 +131,7 @@
 <translation id="1768366657309696705">The <ph name="LAUNCHER_KEY_NAME" /> + full stop keyboard shortcut has changed. To use the insert key, press the <ph name="LAUNCHER_KEY_NAME" /> key + shift + backspace.</translation>
 <translation id="1770726142253415363">Moved to row <ph name="ROW_NUMBER" />, column <ph name="COLUMN_NUMBER" />.</translation>
 <translation id="1771761307086386028">Scroll right</translation>
+<translation id="1774796056689732716">Calendar, <ph name="CURRENT_MONTH_YEAR" />, currently <ph name="DATE" /> is selected.</translation>
 <translation id="1787955149152357925">Off</translation>
 <translation id="1804572139604454141">Recording ended due to critically low disk space</translation>
 <translation id="181103072419391116">Signal strength <ph name="SIGNAL_STRENGTH" />, managed by your administrator</translation>
@@ -630,6 +631,7 @@
 <translation id="5168181903108465623">Cast devices available</translation>
 <translation id="5170568018924773124">Show in folder</translation>
 <translation id="5176318573511391780">Record partial screen</translation>
+<translation id="5198413532174090167"><ph name="DATE" />, <ph name="NUMBER" /> events</translation>
 <translation id="5198715732953550718"><ph name="MOVED_APP_NAME" /> combined with <ph name="IN_PLACE_APP" /> to create new folder.</translation>
 <translation id="5206028654245650022"><ph name="APP_NAME" />, <ph name="NOTIFICATION_TITLE" />: <ph name="MESSAGE" />, <ph name="PHONE_NAME" /></translation>
 <translation id="5206057955438543357">{NUM_NOTIFICATIONS,plural, =1{1 other notification}other{# other notifications}}</translation>
@@ -803,6 +805,7 @@
 <translation id="643147933154517414">All finished</translation>
 <translation id="6431865393913628856">Screen record</translation>
 <translation id="6445835306623867477"><ph name="ROUTE_TITLE" /> on <ph name="RECEIVER_NAME" /></translation>
+<translation id="6447111710783417522"><ph name="DATE" />, <ph name="NUMBER" /> event</translation>
 <translation id="6452181791372256707">Reject</translation>
 <translation id="6453179446719226835">Language has been changed</translation>
 <translation id="6459472438155181876">Extending screen to <ph name="DISPLAY_NAME" /></translation>
@@ -866,6 +869,7 @@
 <translation id="6811454077060061666">Google Drive for desktop is unavailable</translation>
 <translation id="6818242057446442178">Back by word</translation>
 <translation id="6820676911989879663">Take a break!</translation>
+<translation id="6827049576281411231">Close event panel</translation>
 <translation id="6836499262298959512">Dangerous file</translation>
 <translation id="6837064795450991317">Hide deskbar</translation>
 <translation id="6852052252232534364">Click to activate</translation>
@@ -873,6 +877,7 @@
 <translation id="685782768769951078">{NUM_DIGITS,plural, =1{One digit remaining}other{# digits remaining}}</translation>
 <translation id="6867938213751067702">Download paused <ph name="FILENAME" /></translation>
 <translation id="6878400149835617132">Shortcut turned off</translation>
+<translation id="6884665277231944629">Go back to today</translation>
 <translation id="6886172995547742638">Your <ph name="DEVICE_TYPE" /> may experience lower performance. Use a certified <ph name="PREFERRED_MINIMUM_POWER" />W or higher USB-C power adaptor.</translation>
 <translation id="688631446150864480">Press the Down arrow key to switch windows</translation>
 <translation id="6896758677409633944">Copy</translation>
@@ -959,6 +964,7 @@
 <translation id="7497767806359279797">Choose language and keyboard</translation>
 <translation id="7509246181739783082">Verify your identity</translation>
 <translation id="7513622367902644023">Screenshot mode selected</translation>
+<translation id="7513922695575567867">Calendar, week of <ph name="DATE" />, <ph name="SELECTED_DATE" /> is currently selected.</translation>
 <translation id="7514365320538308">Download</translation>
 <translation id="7526573455193969409">Network may be monitored</translation>
 <translation id="7536035074519304529">IP address: <ph name="ADDRESS" /></translation>
@@ -1041,6 +1047,7 @@
 <translation id="8035152190676905274">Pen</translation>
 <translation id="8036504271468642248">Previous sentence</translation>
 <translation id="8042893070933512245">Open accessibility settings menu</translation>
+<translation id="8042925093898452104">Close detail information</translation>
 <translation id="8048123526339889627">Bluetooth settings</translation>
 <translation id="8051716679295756675">Template named <ph name="DESK_TEMPLATE_NAME" /> already exists</translation>
 <translation id="8052898407431791827">Copied to clipboard</translation>
diff --git a/ash/strings/ash_strings_es-419.xtb b/ash/strings/ash_strings_es-419.xtb
index d83c48e..38ee197 100644
--- a/ash/strings/ash_strings_es-419.xtb
+++ b/ash/strings/ash_strings_es-419.xtb
@@ -327,7 +327,7 @@
 <translation id="3217205077783620295">El volumen está activado. Si lo desactivas, se silenciará.</translation>
 <translation id="3226991577105957773"><ph name="COUNT" /> más</translation>
 <translation id="3227137524299004712">Micrófono</translation>
-<translation id="324366796737464147">Reducción del ruido</translation>
+<translation id="324366796737464147">Cancelación de ruido</translation>
 <translation id="3249513730522716925">La ventana <ph name="WINDOW_TITLE" /> se movió del escritorio <ph name="ACTIVE_DESK" /> al <ph name="TARGET_DESK" /></translation>
 <translation id="3252248118006571685">Para desbloquear la Chromebook, activa la conexión Bluetooth del teléfono</translation>
 <translation id="3255483164551725916">¿Qué puedes hacer?</translation>
diff --git a/ash/strings/ash_strings_lo.xtb b/ash/strings/ash_strings_lo.xtb
index b042135..7af267e 100644
--- a/ash/strings/ash_strings_lo.xtb
+++ b/ash/strings/ash_strings_lo.xtb
@@ -131,6 +131,7 @@
 <translation id="1768366657309696705">ປ່ຽນປຸ່ມລັດແປ້ນພິມ <ph name="LAUNCHER_KEY_NAME" /> + ຈ້ຳເມັດ ແລ້ວ. ເພື່ອໃຊ້ປຸ່ມ Insert, ກະລຸນາກົດປຸ່ມ <ph name="LAUNCHER_KEY_NAME" /> + Shift + Backspace.</translation>
 <translation id="1770726142253415363">ຍ້າຍໄປຫາແຖວ <ph name="ROW_NUMBER" />, ຖັນ <ph name="COLUMN_NUMBER" /> ແລ້ວ.</translation>
 <translation id="1771761307086386028">ເລື່ອນໄປຂວາ</translation>
+<translation id="1774796056689732716">ປະຕິທິນ, <ph name="CURRENT_MONTH_YEAR" />, ຕອນນີ້ເລືອກ <ph name="DATE" /> ຢູ່.</translation>
 <translation id="1787955149152357925">ປິດ</translation>
 <translation id="1804572139604454141">ສິ້ນສຸດການບັນທຶກແລ້ວເນື່ອງຈາກພື້ນທີ່ດິສເຫຼືອໜ້ອຍຫຼາຍ</translation>
 <translation id="181103072419391116">ຄວາມແຮງສັນຍານ <ph name="SIGNAL_STRENGTH" />, ຈັດການໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ</translation>
@@ -620,6 +621,7 @@
 <translation id="5075554201838155866">ເລີ່ມຄຳບັນຍາຍ</translation>
 <translation id="5078796286268621944">PIN ບໍ່ຖືກຕ້ອງ</translation>
 <translation id="5083553833479578423">ປົດລັອກຄຸນສົມບັດເພີ່ມເຕີມຂອງຜູ້ຊ່ວຍ.</translation>
+<translation id="5090752371472782287">ສົ່ງສັນຍານໄປຫາອຸປະກອນ <ph name="MANAGER" /></translation>
 <translation id="5106223312672646208">ປັບແຕ່ງສ່ວນຕົວ</translation>
 <translation id="5117590920725113268">ສະ​ແດງ​ເດືອນ​ຕໍ່​ໄປ</translation>
 <translation id="5136175204352732067">ເຊື່ອມຕໍ່ແປ້ນພິມອື່ນແລ້ວ</translation>
@@ -629,6 +631,7 @@
 <translation id="5168181903108465623">ອຸປະກອນຄາສທ໌ທີ່ມີໃຫ້</translation>
 <translation id="5170568018924773124">ສະ​ແດງ​ຢູ່​ໃນ​ໂຟລ​ເດີ</translation>
 <translation id="5176318573511391780">ບັນທຶກໜ້າຈໍບາງສ່ວນ</translation>
+<translation id="5198413532174090167"><ph name="DATE" />, <ph name="NUMBER" /> ນັດໝາຍ</translation>
 <translation id="5198715732953550718"><ph name="MOVED_APP_NAME" /> ຮວມກັນກັບ <ph name="IN_PLACE_APP" /> ເພື່ອສ້າງໂຟນເດີໃໝ່.</translation>
 <translation id="5206028654245650022"><ph name="APP_NAME" />, <ph name="NOTIFICATION_TITLE" />: <ph name="MESSAGE" />, <ph name="PHONE_NAME" /></translation>
 <translation id="5206057955438543357">{NUM_NOTIFICATIONS,plural, =1{ການແຈ້ງເຕືອນອື່ນໆ 1 ລາຍການ}other{ການແຈ້ງເຕືອນອື່ນໆ # ລາຍການ}}</translation>
@@ -802,6 +805,7 @@
 <translation id="643147933154517414">ສໍາເລັດແລ້ວ</translation>
 <translation id="6431865393913628856">ບັນທຶກໜ້າຈໍ</translation>
 <translation id="6445835306623867477"><ph name="ROUTE_TITLE" /> ໃນ <ph name="RECEIVER_NAME" /></translation>
+<translation id="6447111710783417522"><ph name="DATE" />, <ph name="NUMBER" /> ນັດໝາຍ</translation>
 <translation id="6452181791372256707">ປະ​ຕິ​ເສດ</translation>
 <translation id="6453179446719226835">ປ່ຽນພາສາແລ້ວ</translation>
 <translation id="6459472438155181876">ກໍາລັງ​ຂະ​ຫຍາຍ​ຫນ້າ​ຈໍ​ເພື່ອ <ph name="DISPLAY_NAME" /></translation>
@@ -865,6 +869,7 @@
 <translation id="6811454077060061666">ບໍ່ສາມາດໃຊ້ Google Drive ສຳລັບເດັສທັອບໄດ້</translation>
 <translation id="6818242057446442178">ຖອຍກັບເທື່ອລະຄຳ</translation>
 <translation id="6820676911989879663">ຢຸດພັກ!</translation>
+<translation id="6827049576281411231">ປິດແຜງນັດໝາຍ</translation>
 <translation id="6836499262298959512">ໄຟລ໌ອັນຕະລາຍ</translation>
 <translation id="6837064795450991317">ເຊື່ອງເດັສບາ</translation>
 <translation id="6852052252232534364">ຄລິກເພື່ອເປີດນຳໃຊ້</translation>
@@ -872,6 +877,7 @@
 <translation id="685782768769951078">{NUM_DIGITS,plural, =1{ຍັງເຫຼືອໜຶ່ງຕົວເລກ}other{ຍັງເຫຼືອ # ຕົວເລກ}}</translation>
 <translation id="6867938213751067702">ຢຸດດາວໂຫຼດ <ph name="FILENAME" /> ຊົ່ວຄາວແລ້ວ</translation>
 <translation id="6878400149835617132">ປິດທາງລັດແລ້ວ</translation>
+<translation id="6884665277231944629">ກັບໄປຫາມື້ນີ້</translation>
 <translation id="6886172995547742638"><ph name="DEVICE_TYPE" /> ຂອງທ່ານອາດຈະປະສົບກັບປະສິດທິພາບຕໍ່າ. ໃຊ້ອະແດັບເຕີ <ph name="PREFERRED_MINIMUM_POWER" />W ທີ່ມີການຮັບຮອງ ຫຼື ອະແດັບເຕີໄຟ USB-C ທີ່ສູງກວ່າ.</translation>
 <translation id="688631446150864480">ກົດປຸ່ມລູກສອນລົງເພື່ອປ່ຽນໜ້າຈໍ</translation>
 <translation id="6896758677409633944">ກັອບປີ້</translation>
@@ -958,6 +964,7 @@
 <translation id="7497767806359279797">ເລືອກພາສາ ແລະ ແປ້ນພິມ</translation>
 <translation id="7509246181739783082">ຢັ້ງຢືນຕົວຕົນຂອງທ່ານ</translation>
 <translation id="7513622367902644023">ເລືອກໂໝດຮູບໜ້າຈໍແລ້ວ</translation>
+<translation id="7513922695575567867">ປະຕິທິນ, ອາທິດຂອງ <ph name="DATE" />, ຕອນນີ້ເລືອກ <ph name="SELECTED_DATE" /> ຢູ່.</translation>
 <translation id="7514365320538308">ດາວ​ໂຫລດ</translation>
 <translation id="7526573455193969409">ອາດມີການຕິດຕາມເບິ່ງເຄືອຂ່າຍ</translation>
 <translation id="7536035074519304529">ທີ່ຢູ່ IP: <ph name="ADDRESS" /></translation>
@@ -1040,6 +1047,7 @@
 <translation id="8035152190676905274">ບິກ</translation>
 <translation id="8036504271468642248">ປະໂຫຍກກ່ອນໜ້າ</translation>
 <translation id="8042893070933512245">ເປີດເມນູການຕັ້ງຄ່າການຊ່ວຍເຂົ້າເຖິງ</translation>
+<translation id="8042925093898452104">ປິດຂໍ້ມູນລາຍລະອຽດ</translation>
 <translation id="8048123526339889627">ການຕັ້ງຄ່າ Bluetooth</translation>
 <translation id="8051716679295756675">ມີແມ່ແບບຊື່ <ph name="DESK_TEMPLATE_NAME" /> ຢູ່ກ່ອນແລ້ວ</translation>
 <translation id="8052898407431791827">ສຳເນົາໃສ່ຄລິບບອດແລ້ວ</translation>
diff --git a/ash/strings/ash_strings_nl.xtb b/ash/strings/ash_strings_nl.xtb
index 32db9f6d..a3e9a37 100644
--- a/ash/strings/ash_strings_nl.xtb
+++ b/ash/strings/ash_strings_nl.xtb
@@ -235,7 +235,7 @@
 <translation id="256712445991462162">het vastgezette vergrootglas</translation>
 <translation id="2570734079541893434">Instellingen beheren</translation>
 <translation id="2573588302192866788">Kan <ph name="NAME" /> niet koppelen</translation>
-<translation id="2575685495496069081">Toegang tot meerdere accounts staat uit</translation>
+<translation id="2575685495496069081">Toegang tot meerdere accounts is uitgezet</translation>
 <translation id="2582112259361606227">Opnieuw starten om updates uit te voeren</translation>
 <translation id="2595239820337756193">5 km in mijl</translation>
 <translation id="2596078834055697711">Een screenshot van het venster maken</translation>
diff --git a/ash/strings/ash_strings_sw.xtb b/ash/strings/ash_strings_sw.xtb
index b92e848..05798f1 100644
--- a/ash/strings/ash_strings_sw.xtb
+++ b/ash/strings/ash_strings_sw.xtb
@@ -620,6 +620,7 @@
 <translation id="5075554201838155866">Anzisha manukuu</translation>
 <translation id="5078796286268621944">PIN isiyo sahihi</translation>
 <translation id="5083553833479578423">Fungua vipengele zaidi vya programu yako ya Mratibu.</translation>
+<translation id="5090752371472782287">Tuma kwenye kifaa cha <ph name="MANAGER" /></translation>
 <translation id="5106223312672646208">Weka mapendeleo</translation>
 <translation id="5117590920725113268">Onyesha mwezi unaofuata</translation>
 <translation id="5136175204352732067">Kibodi tofauti imeunganishwa</translation>
diff --git a/ash/strings/ash_strings_uz.xtb b/ash/strings/ash_strings_uz.xtb
index 280dbf4..c147ff4 100644
--- a/ash/strings/ash_strings_uz.xtb
+++ b/ash/strings/ash_strings_uz.xtb
@@ -131,6 +131,7 @@
 <translation id="1768366657309696705"><ph name="LAUNCHER_KEY_NAME" /> + Nuqta tezkor tugmasi oʻzgardi. Insert tugmasidan foydalanish uchun <ph name="LAUNCHER_KEY_NAME" /> tugmasi + Shift + Backspace tugmalarini bosing.</translation>
 <translation id="1770726142253415363"><ph name="ROW_NUMBER" />-qator, <ph name="COLUMN_NUMBER" />-ustunga oʻtdi.</translation>
 <translation id="1771761307086386028">Oʻngga aylantirish</translation>
+<translation id="1774796056689732716">Taqvim, <ph name="CURRENT_MONTH_YEAR" />, hozir <ph name="DATE" /> tanlangan.</translation>
 <translation id="1787955149152357925">Yoqilmagan</translation>
 <translation id="1804572139604454141">Diskdan juda kam joy qolgani uchun yozib olish tugatildi</translation>
 <translation id="181103072419391116">Signal darajasi: <ph name="SIGNAL_STRENGTH" />, Administrator boshqaruvida</translation>
@@ -629,6 +630,7 @@
 <translation id="5168181903108465623">Translatsiya qurilmalari mavjud</translation>
 <translation id="5170568018924773124">&amp;Jildda ko‘rsatish</translation>
 <translation id="5176318573511391780">Ekran qismini yozib olish</translation>
+<translation id="5198413532174090167"><ph name="DATE" />, <ph name="NUMBER" /> ta tadbir</translation>
 <translation id="5198715732953550718"><ph name="MOVED_APP_NAME" /> va <ph name="IN_PLACE_APP" /> ilovalari yangi jildga birlashtirildi.</translation>
 <translation id="5206028654245650022"><ph name="APP_NAME" />, <ph name="NOTIFICATION_TITLE" />: <ph name="MESSAGE" />, <ph name="PHONE_NAME" /></translation>
 <translation id="5206057955438543357">{NUM_NOTIFICATIONS,plural, =1{Yana 1 ta bildirishnoma}other{Yana # ta bildirishnoma}}</translation>
@@ -802,6 +804,7 @@
 <translation id="643147933154517414">Bajarildi</translation>
 <translation id="6431865393913628856">Ekran yozuvi</translation>
 <translation id="6445835306623867477"><ph name="ROUTE_TITLE" /> <ph name="RECEIVER_NAME" /> qurilmasiga translatsiya qilinmoqda</translation>
+<translation id="6447111710783417522"><ph name="DATE" />, <ph name="NUMBER" /> ta tadbir</translation>
 <translation id="6452181791372256707">Rad etish</translation>
 <translation id="6453179446719226835">Til sozlamalari o‘zgardi</translation>
 <translation id="6459472438155181876">Ekranni kengaytirish: <ph name="DISPLAY_NAME" /></translation>
@@ -865,6 +868,7 @@
 <translation id="6811454077060061666">Kompyuter uchun Google Drive hozir ishlamaydi</translation>
 <translation id="6818242057446442178">Bitta soʻz orqaga</translation>
 <translation id="6820676911989879663">Tanaffus qiling</translation>
+<translation id="6827049576281411231">Tadbir panelini yopish</translation>
 <translation id="6836499262298959512">Xavfli fayl</translation>
 <translation id="6837064795450991317">Ish panelini yopish</translation>
 <translation id="6852052252232534364">Faollashtirish</translation>
@@ -872,6 +876,7 @@
 <translation id="685782768769951078">{NUM_DIGITS,plural, =1{Bitta raqam kiritilmadi}other{# ta raqam kiritilmadi}}</translation>
 <translation id="6867938213751067702">Yuklab olish pauza qilindi: <ph name="FILENAME" /></translation>
 <translation id="6878400149835617132">Yorliq yoqilmagan</translation>
+<translation id="6884665277231944629">Bugunga qaytish</translation>
 <translation id="6886172995547742638"><ph name="DEVICE_TYPE" /> ishlashi sekinlashishi mumkin. <ph name="PREFERRED_MINIMUM_POWER" />W sertifikatiga ega yoki kuchliroq USB-C quvvat adapteridan foydalaning.</translation>
 <translation id="688631446150864480">Oynalarni almashtirish uchun pastga strelkani bosing</translation>
 <translation id="6896758677409633944">Nusxa olish</translation>
@@ -958,6 +963,7 @@
 <translation id="7497767806359279797">Til va klaviaturani tanlang</translation>
 <translation id="7509246181739783082">Shaxsni tasdiqlash</translation>
 <translation id="7513622367902644023">Skrinshot rejimi tanlandi</translation>
+<translation id="7513922695575567867">Taqvim, <ph name="DATE" /> haftasi, hozir <ph name="SELECTED_DATE" /> tanlangan.</translation>
 <translation id="7514365320538308">Yuklab olish</translation>
 <translation id="7526573455193969409">Tarmoq kuzatilishi mumkin</translation>
 <translation id="7536035074519304529">IP manzil: <ph name="ADDRESS" /></translation>
@@ -1040,6 +1046,7 @@
 <translation id="8035152190676905274">Pero</translation>
 <translation id="8036504271468642248">Avvalgi gap</translation>
 <translation id="8042893070933512245">Maxsus imkoniyatlar sozlamalari menyusini ochish</translation>
+<translation id="8042925093898452104">Batafsil axborotni yopish</translation>
 <translation id="8048123526339889627">Bluetooth sozlamalari</translation>
 <translation id="8051716679295756675"><ph name="DESK_TEMPLATE_NAME" /> nomli andoza bor</translation>
 <translation id="8052898407431791827">Vaqtinchalik xotiraga nusxalandi</translation>
diff --git a/ash/system/pcie_peripheral/pcie_peripheral_notification_controller.h b/ash/system/pcie_peripheral/pcie_peripheral_notification_controller.h
index 832eee2..263d9da 100644
--- a/ash/system/pcie_peripheral/pcie_peripheral_notification_controller.h
+++ b/ash/system/pcie_peripheral/pcie_peripheral_notification_controller.h
@@ -61,6 +61,9 @@
   // is not supported by the board.
   void NotifyBillboardDevice();
 
+  // Stubs from usb peripheral notification controller
+  void OnInvalidDpCableWarning() override {}
+
  private:
   friend class PciePeripheralNotificationControllerTest;
 
diff --git a/ash/system/phonehub/camera_roll_opt_in_view.cc b/ash/system/phonehub/camera_roll_opt_in_view.cc
index bf83628..986e5041 100644
--- a/ash/system/phonehub/camera_roll_opt_in_view.cc
+++ b/ash/system/phonehub/camera_roll_opt_in_view.cc
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "ash/components/phonehub/camera_roll_manager.h"
+#include "ash/components/phonehub/util/histogram_util.h"
 #include "ash/public/cpp/new_window_delegate.h"
 #include "ash/system/phonehub/phone_hub_view_ids.h"
 #include "chromeos/components/multidevice/logging/logging.h"
@@ -26,6 +27,8 @@
 
 void CameraRollOptInView::SetUpButtonPressed() {
   camera_roll_manager_->EnableCameraRollFeatureInSystemSetting();
+  phonehub::util::LogCameraRollFeatureOptInEntryPoint(
+      phonehub::util::CameraRollOptInEntryPoint::kOnboardingDialog);
 }
 
 void CameraRollOptInView::DismissButtonPressed() {
diff --git a/ash/system/power/power_prefs.cc b/ash/system/power/power_prefs.cc
index 304af08..c4e896c0 100644
--- a/ash/system/power/power_prefs.cc
+++ b/ash/system/power/power_prefs.cc
@@ -319,7 +319,8 @@
           prefs::kPowerPeakShiftBatteryThreshold) &&
       local_state_->IsManagedPreference(prefs::kPowerPeakShiftDayConfig)) {
     const base::DictionaryValue* configs_value =
-        local_state_->GetDictionary(prefs::kPowerPeakShiftDayConfig);
+        &base::Value::AsDictionaryValue(
+            *local_state_->GetDictionary(prefs::kPowerPeakShiftDayConfig));
     DCHECK(configs_value);
     std::vector<PeakShiftDayConfig> configs;
     if (chromeos::PowerPolicyController::GetPeakShiftDayConfigs(*configs_value,
@@ -339,12 +340,12 @@
           prefs::kAdvancedBatteryChargeModeEnabled) &&
       local_state_->IsManagedPreference(
           prefs::kAdvancedBatteryChargeModeDayConfig)) {
-    const base::DictionaryValue* configs_value =
+    const base::Value* configs_value =
         local_state_->GetDictionary(prefs::kAdvancedBatteryChargeModeDayConfig);
     DCHECK(configs_value);
     std::vector<AdvancedBatteryChargeModeDayConfig> configs;
     if (chromeos::PowerPolicyController::GetAdvancedBatteryChargeModeDayConfigs(
-            *configs_value, &configs)) {
+            base::Value::AsDictionaryValue(*configs_value), &configs)) {
       values.advanced_battery_charge_mode_enabled = true;
       values.advanced_battery_charge_mode_day_configs = std::move(configs);
     } else {
diff --git a/ash/system/power/power_prefs_unittest.cc b/ash/system/power/power_prefs_unittest.cc
index 5081c2e..8a25ba4 100644
--- a/ash/system/power/power_prefs_unittest.cc
+++ b/ash/system/power/power_prefs_unittest.cc
@@ -137,7 +137,9 @@
 
   std::vector<power_manager::PowerManagementPolicy::PeakShiftDayConfig> configs;
   EXPECT_TRUE(chromeos::PowerPolicyController::GetPeakShiftDayConfigs(
-      *prefs->GetDictionary(prefs::kPowerPeakShiftDayConfig), &configs));
+      base::Value::AsDictionaryValue(
+          *prefs->GetDictionary(prefs::kPowerPeakShiftDayConfig)),
+      &configs));
 
   power_manager::PowerManagementPolicy expected_policy;
   expected_policy.set_peak_shift_battery_percent_threshold(
@@ -158,7 +160,8 @@
       configs;
   EXPECT_TRUE(
       chromeos::PowerPolicyController::GetAdvancedBatteryChargeModeDayConfigs(
-          *prefs->GetDictionary(prefs::kAdvancedBatteryChargeModeDayConfig),
+          base::Value::AsDictionaryValue(*prefs->GetDictionary(
+              prefs::kAdvancedBatteryChargeModeDayConfig)),
           &configs));
 
   power_manager::PowerManagementPolicy expected_policy;
diff --git a/ash/system/usb_peripheral/usb_peripheral_notification_controller.cc b/ash/system/usb_peripheral/usb_peripheral_notification_controller.cc
new file mode 100644
index 0000000..9ce71d8
--- /dev/null
+++ b/ash/system/usb_peripheral/usb_peripheral_notification_controller.cc
@@ -0,0 +1,82 @@
+// 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/system/usb_peripheral/usb_peripheral_notification_controller.h"
+
+#include "ash/public/cpp/new_window_delegate.h"
+#include "ash/public/cpp/notification_utils.h"
+#include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "third_party/cros_system_api/dbus/typecd/dbus-constants.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/public/cpp/notification.h"
+#include "ui/message_center/public/cpp/notification_types.h"
+#include "url/gurl.h"
+
+namespace ash {
+
+namespace {
+const char kNotifierUsbPeripheral[] = "ash.usb_peripheral";
+const char kUsbPeripheralInvalidDpCableNotificationId[] =
+    "cros_usb_peripheral_invalid_dp_cable_notification_id";
+const char kNotificationLandingPageUrl[] =
+    "https://support.google.com/chromebook?p=cable_notification";
+
+bool ShouldDisplayNotification() {
+  return Shell::Get()->session_controller()->GetSessionState() ==
+             session_manager::SessionState::ACTIVE &&
+         !Shell::Get()->session_controller()->IsUserSessionBlocked();
+}
+
+void OnInvalidDpCableNotificationClicked() {
+  NewWindowDelegate::GetInstance()->OpenUrl(GURL(kNotificationLandingPageUrl),
+                                            /*from_user_interaction=*/true);
+  message_center::MessageCenter::Get()->RemoveNotification(
+      kUsbPeripheralInvalidDpCableNotificationId, /*from_user=*/true);
+}
+
+}  // namespace
+
+UsbPeripheralNotificationController::UsbPeripheralNotificationController(
+    message_center::MessageCenter* message_center)
+    : message_center_(message_center) {
+  DCHECK(message_center_);
+}
+
+UsbPeripheralNotificationController::~UsbPeripheralNotificationController() {
+  if (ash::PeripheralNotificationManager::IsInitialized())
+    ash::PeripheralNotificationManager::Get()->RemoveObserver(this);
+}
+
+void UsbPeripheralNotificationController::
+    OnPeripheralNotificationManagerInitialized() {
+  DCHECK(ash::PeripheralNotificationManager::IsInitialized());
+  ash::PeripheralNotificationManager::Get()->AddObserver(this);
+}
+
+// Notify the user that the current cable may not support dp alt mode.
+void UsbPeripheralNotificationController::OnInvalidDpCableWarning() {
+  if (!ShouldDisplayNotification())
+    return;
+
+  std::unique_ptr<message_center::Notification> notification =
+      CreateSystemNotification(
+          message_center::NOTIFICATION_TYPE_SIMPLE,
+          kUsbPeripheralInvalidDpCableNotificationId, u"USB Type-C Warning.",
+          u"This cable may not support dp alternate mode.",
+          /*display_source=*/std::u16string(), GURL(),
+          message_center::NotifierId(
+              message_center::NotifierType::SYSTEM_COMPONENT,
+              kNotifierUsbPeripheral),
+          message_center::RichNotificationData(),
+          base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
+              base::BindRepeating(&OnInvalidDpCableNotificationClicked)),
+          kSettingsIcon,
+          message_center::SystemNotificationWarningLevel::WARNING);
+
+  message_center_->AddNotification(std::move(notification));
+}
+
+}  // namespace ash
diff --git a/ash/system/usb_peripheral/usb_peripheral_notification_controller.h b/ash/system/usb_peripheral/usb_peripheral_notification_controller.h
new file mode 100644
index 0000000..b73b567
--- /dev/null
+++ b/ash/system/usb_peripheral/usb_peripheral_notification_controller.h
@@ -0,0 +1,46 @@
+// 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_SYSTEM_USB_PERIPHERAL_USB_PERIPHERAL_NOTIFICATION_CONTROLLER_H_
+#define ASH_SYSTEM_USB_PERIPHERAL_USB_PERIPHERAL_NOTIFICATION_CONTROLLER_H_
+
+#include "ash/ash_export.h"
+#include "ash/components/peripheral_notification/peripheral_notification_manager.h"
+
+namespace message_center {
+class MessageCenter;
+}  // namespace message_center
+
+namespace ash {
+
+class ASH_EXPORT UsbPeripheralNotificationController
+    : public PeripheralNotificationManager::Observer {
+ public:
+  explicit UsbPeripheralNotificationController(
+      message_center::MessageCenter* message_center);
+  UsbPeripheralNotificationController(
+      const UsbPeripheralNotificationController&) = delete;
+  UsbPeripheralNotificationController& operator=(
+      const UsbPeripheralNotificationController&) = delete;
+  ~UsbPeripheralNotificationController() override;
+
+  // Called after parent class is initialized.
+  void OnPeripheralNotificationManagerInitialized();
+
+  // PeripheralNotificationManager::Observer:
+  void OnInvalidDpCableWarning() override;
+
+  // Stubs from PCIE Notification controller
+  void OnLimitedPerformancePeripheralReceived() override {}
+  void OnGuestModeNotificationReceived(bool is_thunderbolt_only) override {}
+  void OnPeripheralBlockedReceived() override {}
+  void OnBillboardDeviceConnected() override {}
+
+ private:
+  message_center::MessageCenter* const message_center_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_USB_PERIPHERAL_USB_PERIPHERAL_NOTIFICATION_CONTROLLER_H_
diff --git a/ash/system/usb_peripheral/usb_peripheral_notification_controller_unittest.cc b/ash/system/usb_peripheral/usb_peripheral_notification_controller_unittest.cc
new file mode 100644
index 0000000..f7c0627
--- /dev/null
+++ b/ash/system/usb_peripheral/usb_peripheral_notification_controller_unittest.cc
@@ -0,0 +1,54 @@
+// 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/system/usb_peripheral/usb_peripheral_notification_controller.h"
+
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ui/message_center/message_center.h"
+
+using message_center::MessageCenter;
+
+namespace ash {
+
+namespace {
+
+const char kUsbPeripheralInvalidDpCableNotificationId[] =
+    "cros_usb_peripheral_invalid_dp_cable_notification_id";
+
+}  // namespace
+
+class UsbPeripheralNotificationControllerTest : public AshTestBase {
+ public:
+  UsbPeripheralNotificationControllerTest() {}
+  UsbPeripheralNotificationControllerTest(
+      const UsbPeripheralNotificationControllerTest&) = delete;
+  UsbPeripheralNotificationControllerTest& operator=(
+      const UsbPeripheralNotificationControllerTest&) = delete;
+  ~UsbPeripheralNotificationControllerTest() override = default;
+
+  UsbPeripheralNotificationController* controller() {
+    return Shell::Get()->usb_peripheral_notification_controller();
+  }
+
+  message_center::Notification* GetInvalidDpCableNotification() {
+    return MessageCenter::Get()->FindVisibleNotificationById(
+        kUsbPeripheralInvalidDpCableNotificationId);
+  }
+};
+
+TEST_F(UsbPeripheralNotificationControllerTest, InvalidDpCableNotification) {
+  EXPECT_EQ(MessageCenter::Get()->NotificationCount(), 0u);
+  controller()->OnInvalidDpCableWarning();
+  EXPECT_EQ(MessageCenter::Get()->NotificationCount(), 1u);
+
+  message_center::Notification* notification = GetInvalidDpCableNotification();
+  ASSERT_TRUE(notification);
+
+  EXPECT_EQ(notification->buttons().size(), 0u);
+  controller()->OnInvalidDpCableWarning();
+  EXPECT_EQ(MessageCenter::Get()->NotificationCount(), 1u);
+}
+
+}  // namespace ash
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc
index 6c6a4e3..3c29228 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -2432,8 +2432,9 @@
   absl::optional<std::vector<SkColor>> cached_colors_out;
   const base::ListValue* prominent_colors = nullptr;
   if (!local_state_ ||
-      !local_state_->GetDictionary(prefs::kWallpaperColors)
-           ->GetListWithoutPathExpansion(current_location, &prominent_colors)) {
+      !base::Value::AsDictionaryValue(
+           *local_state_->GetDictionary(prefs::kWallpaperColors))
+           .GetListWithoutPathExpansion(current_location, &prominent_colors)) {
     return cached_colors_out;
   }
   cached_colors_out = std::vector<SkColor>();
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index 5529a97..5ab6cb5d 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -331,8 +331,9 @@
                                 AccountId account_id,
                                 WallpaperInfo info) {
   const base::DictionaryValue* stored_info_dict;
-  pref_service->GetDictionary(pref_name)->GetDictionaryWithoutPathExpansion(
-      account_id.GetUserEmail(), &stored_info_dict);
+  base::Value::AsDictionaryValue(*pref_service->GetDictionary(pref_name))
+      .GetDictionaryWithoutPathExpansion(account_id.GetUserEmail(),
+                                         &stored_info_dict);
   auto expected_info_dict = CreateWallpaperInfoDict(info);
   EXPECT_EQ(*expected_info_dict.get(), *stored_info_dict);
 }
diff --git a/ash/webui/BUILD.gn b/ash/webui/BUILD.gn
index 5998f17..70f331d3 100644
--- a/ash/webui/BUILD.gn
+++ b/ash/webui/BUILD.gn
@@ -75,9 +75,6 @@
   ]
 
   if (!is_official_build) {
-    deps += [
-      "//ash/webui/demo_mode_app_ui:closure_compile",
-      "//ash/webui/sample_system_web_app_ui:closure_compile",
-    ]
+    deps += [ "//ash/webui/demo_mode_app_ui:closure_compile" ]
   }
 }
diff --git a/ash/webui/camera_app_ui/resources/css/main.css b/ash/webui/camera_app_ui/resources/css/main.css
index 6f5f319..3429134e 100644
--- a/ash/webui/camera_app_ui/resources/css/main.css
+++ b/ash/webui/camera_app_ui/resources/css/main.css
@@ -257,7 +257,8 @@
 
 body:not(.streaming) #modes-group,
 body.taking #modes-group,
-body.taking:not(.video) #shutters-group {
+body.taking:not(.video) #shutters-group,
+body.snapshotting #video-snapshot {
   opacity: 0.38;
 }
 
@@ -609,11 +610,21 @@
 
 #gallery-enter {
   background-color: rgba(24, 24, 24, 1);
-  background-size: cover;
   height: var(--big-icon);
+  overflow: hidden;
   width: var(--big-icon);
 }
 
+#gallery-enter>img {
+  height: 100%;
+  object-fit: cover;
+  width: 100%;
+}
+
+#gallery-enter>img:not(.draggable) {
+  pointer-events: none;
+}
+
 body.should-handle-intent-result #gallery-enter {
   display: none;
 }
diff --git a/ash/webui/camera_app_ui/resources/js/expert.js b/ash/webui/camera_app_ui/resources/js/expert.ts
similarity index 76%
rename from ash/webui/camera_app_ui/resources/js/expert.js
rename to ash/webui/camera_app_ui/resources/js/expert.ts
index ffe80c2..ef07611c 100644
--- a/ash/webui/camera_app_ui/resources/js/expert.js
+++ b/ash/webui/camera_app_ui/resources/js/expert.ts
@@ -7,9 +7,9 @@
 
 /**
  * Enables or disables expert mode.
- * @param {boolean} enable Whether to enable or disable expert mode
+ * @param enable Whether to enable or disable expert mode
  */
-export function setExpertMode(enable) {
+export function setExpertMode(enable: boolean): void {
   state.set(state.State.EXPERT, enable);
   localStorage.set('expert', enable);
 }
@@ -17,7 +17,7 @@
 /**
  * Toggles expert mode.
  */
-export function toggleExpertMode() {
+export function toggleExpertMode(): void {
   const newState = !state.get(state.State.EXPERT);
   setExpertMode(newState);
 }
diff --git a/ash/webui/camera_app_ui/resources/js/gallerybutton.js b/ash/webui/camera_app_ui/resources/js/gallerybutton.js
index 470d6de..78f7898 100644
--- a/ash/webui/camera_app_ui/resources/js/gallerybutton.js
+++ b/ash/webui/camera_app_ui/resources/js/gallerybutton.js
@@ -14,29 +14,26 @@
 import {ResultSaver} from './models/result_saver.js';
 import {VideoSaver} from './models/video_saver.js';
 import {ChromeHelper} from './mojo/chrome_helper.js';
-import {scale} from './thumbnailer.js';
+import {extractImageFromBlob} from './thumbnailer.js';
 import {
   ErrorLevel,
   ErrorType,
+  MimeType,
   VideoType,
 } from './type.js';
 
 /**
- * Width of thumbnail used by cover photo of gallery button.
- * @type {number}
- */
-const THUMBNAIL_WIDTH = 240;
-
-/**
  * Cover photo of gallery button.
  */
 class CoverPhoto {
   /**
    * @param {!FileAccessEntry} file File entry of cover photo.
-   * @param {?string} thumbnailUrl Url to its thumbnail. Might be null if the
-   *     thumbnail is failed to load.
+   * @param {?string} url Url to its cover photo. Might be null if the cover is
+   *     failed to load.
+   * @param {boolean} draggable If the file type support share by dragg/drop
+   *     cover photo.
    */
-  constructor(file, thumbnailUrl) {
+  constructor(file, url, draggable) {
     /**
      * @type {!FileAccessEntry}
      * @const
@@ -47,7 +44,12 @@
      * @type {?string}
      * @const
      */
-    this.thumbnailUrl = thumbnailUrl;
+    this.url = url;
+
+    /**
+     * @const {boolean}
+     */
+    this.draggable = draggable;
   }
 
   /**
@@ -62,8 +64,8 @@
    * Releases resources used by this cover photo.
    */
   release() {
-    if (this.thumbnailUrl !== null) {
-      URL.revokeObjectURL(this.thumbnailUrl);
+    if (this.url !== null) {
+      URL.revokeObjectURL(this.url);
     }
   }
 
@@ -84,13 +86,14 @@
     }
 
     try {
-      const thumbnail = await scale(blob, THUMBNAIL_WIDTH);
-      return new CoverPhoto(file, URL.createObjectURL(thumbnail));
+      const cover = await extractImageFromBlob(blob);
+      const draggable = blob.type !== MimeType.MP4;
+      return new CoverPhoto(file, URL.createObjectURL(cover), draggable);
     } catch (e) {
       reportError(
           ErrorType.BROKEN_THUMBNAIL, ErrorLevel.ERROR,
           assertInstanceof(e, Error));
-      return new CoverPhoto(file, null);
+      return new CoverPhoto(file, null, false);
     }
   }
 }
@@ -118,6 +121,12 @@
     this.button_ = dom.get('#gallery-enter', HTMLButtonElement);
 
     /**
+     * @type {!HTMLImageElement}
+     * @private
+     */
+    this.coverPhoto_ = dom.getFrom(this.button_, 'img', HTMLImageElement);
+
+    /**
      * Directory holding saved pictures showing in gallery.
      * @type {?DirectoryAccessEntry}
      * @private
@@ -158,10 +167,8 @@
     this.cover_ = cover;
 
     this.button_.hidden = cover === null;
-    this.button_.style.backgroundImage =
-        cover !== null && cover.thumbnailUrl !== null ?
-        `url("${cover.thumbnailUrl}")` :
-        'none';
+    this.coverPhoto_.classList.toggle('draggable', cover?.draggable ?? false);
+    this.coverPhoto_.src = cover?.url ?? '';
 
     if (cover !== null) {
       ChromeHelper.getInstance().monitorFileDeletion(file.name, () => {
diff --git a/ash/webui/camera_app_ui/resources/js/js.gni b/ash/webui/camera_app_ui/resources/js/js.gni
index b2f1d687..305ffd6 100644
--- a/ash/webui/camera_app_ui/resources/js/js.gni
+++ b/ash/webui/camera_app_ui/resources/js/js.gni
@@ -16,7 +16,7 @@
   "device/stream_manager.js",
   "dom.ts",
   "error.ts",
-  "expert.js",
+  "expert.ts",
   "face.js",
   "flag.ts",
   "focus_ring.js",
diff --git a/ash/webui/camera_app_ui/resources/js/state.js b/ash/webui/camera_app_ui/resources/js/state.js
index c4ec702..af8abfef 100644
--- a/ash/webui/camera_app_ui/resources/js/state.js
+++ b/ash/webui/camera_app_ui/resources/js/state.js
@@ -60,9 +60,11 @@
   SAVE_METADATA: 'save-metadata',
   SCREEN_OFF_AUTO: 'screen-off-auto',
   SHOULD_HANDLE_INTENT_RESULT: 'should-handle-intent-result',
+  SHOW_GIF_RECORDING_OPTION: 'show-gif-recording-option',
   SHOW_METADATA: 'show-metadata',
   SHOW_SCAN_MODE: 'show-scan-mode',
   SHUTTER_PROGRESSING: 'shutter-progressing',
+  SNAPSHOTTING: 'snapshotting',
   STREAMING: 'streaming',
   SUSPEND: 'suspend',
   TABLET: 'tablet',
diff --git a/ash/webui/camera_app_ui/resources/js/thumbnailer.js b/ash/webui/camera_app_ui/resources/js/thumbnailer.js
index 3fc791a..6c759b0 100644
--- a/ash/webui/camera_app_ui/resources/js/thumbnailer.js
+++ b/ash/webui/camera_app_ui/resources/js/thumbnailer.js
@@ -130,7 +130,7 @@
  * @param {number=} height Target height. Preserve the aspect ratio if not set.
  * @return {!Promise<!Blob>} Promise of the thumbnail as a jpeg blob.
  */
-async function scaleImage(blob, width, height = undefined) {
+export async function scaleImage(blob, width, height = undefined) {
   const el = await loadImageBlob(blob);
   if (height === undefined) {
     height = Math.round(width * el.naturalHeight / el.naturalWidth);
@@ -215,18 +215,6 @@
 }
 
 /**
- * Creates a thumbnail of image in pdf by scaling it to the target size.
- * @param {!Blob} blob Blob of pdf.
- * @param {number} width Target width.
- * @param {number=} height Target height. Preserve the aspect ratio if not set.
- * @return {!Promise<!Blob>} Promise of the thumbnail as a jpeg blob.
- */
-async function scalePdfImage(blob, width, height = undefined) {
-  blob = await getImageFromPdf(blob);
-  return scaleImage(blob, width, height);
-}
-
-/**
  * Throws when the input blob type is not supported by thumbnailer.
  */
 class InvalidBlobTypeError extends Error {
@@ -241,22 +229,26 @@
 }
 
 /**
- * Creates a thumbnail from specific format blob by scaling it to the target
- * size.
- * @param {!Blob} blob
- * @param {number} width Target width.
- * @param {number=} height Target height. Preserve the aspect ratio if not set.
- * @return {!Promise<!Blob>} Promise of the thumbnail as a jpeg blob.
+ * For non-video type cover, keeps the original size as possible to support drag
+ * drop share. Scales video type which don't support drag drop share.
+ * @type {number}
  */
-export function scale(blob, width, height = undefined) {
+const VIDEO_COVER_WIDTH = 240;
+
+/**
+ * Extracts image blob from an arbitrary type of blob.
+ * @param {!Blob} blob
+ * @return {!Promise<!Blob>} Resolved to the image blob.
+ */
+export async function extractImageFromBlob(blob) {
   switch (blob.type) {
     case MimeType.GIF:
     case MimeType.JPEG:
-      return scaleImage(blob, width, height);
+      return blob;
     case MimeType.MP4:
-      return scaleVideo(blob, width, height);
+      return scaleVideo(blob, VIDEO_COVER_WIDTH);
     case MimeType.PDF:
-      return scalePdfImage(blob, width, height);
+      return getImageFromPdf(blob);
     default:
       throw new InvalidBlobTypeError(blob.type);
   }
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js
index a3808335..01bdbb7 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js
@@ -7,7 +7,6 @@
   assertInstanceof,
   assertNotReached,
 } from '../../../assert.js';
-import {AsyncJobQueue} from '../../../async_job_queue.js';
 // eslint-disable-next-line no-unused-vars
 import {StreamConstraints} from '../../../device/stream_constraints.js';
 import {
@@ -323,11 +322,11 @@
     this.recordingType_ = RecordType.NORMAL;
 
     /**
-     * Queueing all taking video snapshot jobs requested in a single recording.
-     * @type {!AsyncJobQueue}
+     * The ongoing video snapshot.
+     * @type {?Promise<void>}
      * @private
      */
-    this.snapshots_ = new AsyncJobQueue();
+    this.snapshotting_ = null;
 
     /**
      * Promise for process of toggling video pause/resume. Sets to null if CCA
@@ -392,32 +391,40 @@
    * Takes a video snapshot during recording.
    * @return {!Promise} Promise resolved when video snapshot is finished.
    */
-  takeSnapshot() {
-    const doSnapshot = async () => {
-      let blob;
-      if (await this.isBlobVideoSnapshotEnabled()) {
-        const photoSettings = /** @type {!PhotoSettings} */ ({
-          imageWidth: this.snapshotResolution_.width,
-          imageHeight: this.snapshotResolution_.height,
-        });
-        const results = await this.crosImageCapture_.takePhoto(photoSettings);
-        blob = await results[0];
-      } else {
-        blob = await this.crosImageCapture_.grabJpegFrame();
-      }
+  async takeSnapshot() {
+    if (this.snapshotting_ !== null) {
+      return;
+    }
+    state.set(state.State.SNAPSHOTTING, true);
+    this.snapshotting_ = (async () => {
+      try {
+        let blob;
+        if (await this.isBlobVideoSnapshotEnabled()) {
+          const photoSettings = /** @type {!PhotoSettings} */ ({
+            imageWidth: this.snapshotResolution_.width,
+            imageHeight: this.snapshotResolution_.height,
+          });
+          const results = await this.crosImageCapture_.takePhoto(photoSettings);
+          blob = await results[0];
+        } else {
+          blob = await this.crosImageCapture_.grabJpegFrame();
+        }
 
-      this.handler_.playShutterEffect();
-      const imageName = (new Filenamer()).newImageName();
-      await this.handler_.handleResultPhoto(
-          {
-            resolution: this.captureResolution_,
-            blob,
-            isVideoSnapshot: true,
-          },
-          imageName);
-    };
-    this.snapshots_.push(doSnapshot);
-    return this.snapshots_.flush();
+        this.handler_.playShutterEffect();
+        const imageName = (new Filenamer()).newImageName();
+        await this.handler_.handleResultPhoto(
+            {
+              resolution: this.captureResolution_,
+              blob,
+              isVideoSnapshot: true,
+            },
+            imageName);
+      } finally {
+        state.set(state.State.SNAPSHOTTING, false);
+        this.snapshotting_ = null;
+      }
+    })();
+    return this.snapshotting_;
   }
 
   /**
@@ -514,7 +521,7 @@
    * @override
    */
   async start_() {
-    this.snapshots_ = new AsyncJobQueue();
+    assert(this.snapshotting_ === null);
     this.togglePaused_ = null;
     this.everPaused_ = false;
 
@@ -602,7 +609,7 @@
         } finally {
           this.recordTime_.stop({pause: false});
           sound.play(dom.get('#sound-rec-end', HTMLAudioElement));
-          await this.snapshots_.flush();
+          await this.snapshotting_;
         }
       } catch (e) {
         // Tolerates the error if it is due to the very short duration. Reports
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera_intent.js b/ash/webui/camera_app_ui/resources/js/views/camera_intent.js
index 8a2b652..93a7c9f 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera_intent.js
+++ b/ash/webui/camera_app_ui/resources/js/views/camera_intent.js
@@ -22,7 +22,7 @@
 // eslint-disable-next-line no-unused-vars
 import {PerfLogger} from '../perf.js';
 import * as state from '../state.js';
-import {scale} from '../thumbnailer.js';
+import {scaleImage} from '../thumbnailer.js';
 import * as toast from '../toast.js';
 // eslint-disable-next-line no-unused-vars
 import {Mode} from '../type.js';
@@ -61,7 +61,7 @@
           const image = await util.blobToImage(blob);
           const ratio = Math.sqrt(
               DOWNSCALE_INTENT_MAX_PIXEL_NUM / (image.width * image.height));
-          blob = await scale(
+          blob = await scaleImage(
               blob, Math.floor(image.width * ratio),
               Math.floor(image.height * ratio));
         }
diff --git a/ash/webui/camera_app_ui/resources/views/main.html b/ash/webui/camera_app_ui/resources/views/main.html
index 7d834e8..6e7b86b 100644
--- a/ash/webui/camera_app_ui/resources/views/main.html
+++ b/ash/webui/camera_app_ui/resources/views/main.html
@@ -151,7 +151,9 @@
       </div>
       <div class="bottom-stripe right-stripe buttons circle">
         <button id="gallery-enter" tabindex="0"
-                i18n-label="gallery_button" hidden></button>
+                i18n-label="gallery_button" hidden>
+          <img>
+        </button>
       </div>
       <div id="mode-selector" class="bottom-stripe">
         <div id="modes-group" class="buttons" role="radiogroup"
diff --git a/ash/webui/common/resources/keyboard_diagram.html b/ash/webui/common/resources/keyboard_diagram.html
index 996629a..80081b3ea 100644
--- a/ash/webui/common/resources/keyboard_diagram.html
+++ b/ash/webui/common/resources/keyboard_diagram.html
@@ -224,20 +224,11 @@
       data-show-assistant-key$="[[showAssistantKey]]">
     <div id="topRow">
       <keyboard-key main-glyph="esc"></keyboard-key>
-      <!-- TODO(crbug.com/1207678): Populate the top row from Vivaldi data. -->
-      <keyboard-key icon="keyboard:back"></keyboard-key>
-      <keyboard-key icon="keyboard:refresh"></keyboard-key>
-      <keyboard-key icon="keyboard:fullscreen"></keyboard-key>
-      <keyboard-key icon="keyboard:overview"></keyboard-key>
-      <keyboard-key icon="keyboard:screenshot"></keyboard-key>
-      <keyboard-key icon="keyboard:display-brightness-down"></keyboard-key>
-      <keyboard-key icon="keyboard:display-brightness-up"></keyboard-key>
-      <keyboard-key icon="keyboard:volume-mute"></keyboard-key>
-      <keyboard-key icon="keyboard:volume-down"></keyboard-key>
-      <keyboard-key icon="keyboard:volume-up"></keyboard-key>
-      <template is="dom-if" if="[[isEqual_(physicalLayout, 'dell-enterprise')]]">
-        <keyboard-key id="dellDeleteKey" main-glyph="delete"></keyboard-key>
-      </template>
+      <dom-repeat items="[[topRowKeys]]" as="key">
+        <template>
+          <keyboard-key icon="[[key.icon]]" main-glyph="[[key.text]]"></keyboard-key>
+        </template>
+      </dom-repeat>
       <keyboard-key icon="keyboard:power"></keyboard-key>
     </div>
 
diff --git a/ash/webui/common/resources/keyboard_diagram.js b/ash/webui/common/resources/keyboard_diagram.js
index b596ed8..91935a99 100644
--- a/ash/webui/common/resources/keyboard_diagram.js
+++ b/ash/webui/common/resources/keyboard_diagram.js
@@ -34,6 +34,36 @@
   kChromeOSDellEnterprise: 'dell-enterprise',
 };
 
+/**
+ * Enum of action keys to be shown on the top row.
+ * @enum {!Object<string, !{icon: ?string, text: ?string}>}
+ */
+export const TopRowKey = {
+  kNone: {},
+  kBack: {icon: 'keyboard:back'},
+  kForward: {icon: 'keyboard:forward'},
+  kRefresh: {icon: 'keyboard:refresh'},
+  kFullscreen: {icon: 'keyboard:fullscreen'},
+  kOverview: {icon: 'keyboard:overview'},
+  kScreenshot: {icon: 'keyboard:screenshot'},
+  kScreenBrightnessDown: {icon: 'keyboard:display-brightness-down'},
+  kScreenBrightnessUp: {icon: 'keyboard:display-brightness-up'},
+  kPrivacyScreenToggle: {icon: 'keyboard:electronic-privacy-screen'},
+  kVolumeMute: {icon: 'keyboard:volume-mute'},
+  kVolumeDown: {icon: 'keyboard:volume-down'},
+  kVolumeUp: {icon: 'keyboard:volume-up'},
+  kKeyboardBacklightDown: {icon: 'keyboard:keyboard-brightness-down'},
+  kKeyboardBacklightUp: {icon: 'keyboard:keyboard-brightness-up'},
+  kNextTrack: {icon: 'keyboard:next-track'},
+  kPreviousTrack: {icon: 'keyboard:last-track'},
+  kPlayPause: {icon: 'keyboard:play-pause'},
+  kScreenMirror: {icon: 'keyboard:screen-mirror'},
+  // TODO(crbug.com/1207678): work out the localization scheme for keys like
+  // delete and unknown.
+  kDelete: {text: 'delete'},
+  kUnknown: {text: 'unknown'},
+};
+
 export class KeyboardDiagramElement extends PolymerElement {
   static get is() {
     return 'keyboard-diagram';
@@ -66,6 +96,15 @@
         type: Boolean,
         observer: 'updateHeight_',
       },
+
+      /**
+       * The keys to display on the top row.
+       * @type {!Array<!TopRowKey>}
+       */
+      topRowKeys: {
+        type: Array,
+        value: [],
+      },
     };
   }
 
diff --git a/ash/webui/common/resources/keyboard_icons.html b/ash/webui/common/resources/keyboard_icons.html
index 96e47eb..9724acc 100644
--- a/ash/webui/common/resources/keyboard_icons.html
+++ b/ash/webui/common/resources/keyboard_icons.html
@@ -51,6 +51,7 @@
       </g>
       <g id="power"><path d="M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42C17.99 7.86 19 9.81 19 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.19 1.01-4.14 2.58-5.42L6.17 5.17C4.23 6.82 3 9.26 3 12c0 4.97 4.03 9 9 9s9-4.03 9-9c0-2.74-1.23-5.18-3.17-6.83z"></g>
       <g id="refresh"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"></g>
+      <g id="screen-mirror" viewbox="0 0 20 20"><path fill-rule="evenodd" clip-rule="evenodd" d="M17.1327 12.0716H15.0166V10.5716H16.6327V5.28516H9.57471V5.97091H8.07471V4.78516C8.07471 4.23287 8.52242 3.78516 9.07471 3.78516H17.1327C17.685 3.78516 18.1327 4.23287 18.1327 4.78516V11.0716C18.1327 11.6239 17.685 12.0716 17.1327 12.0716ZM14.228 15.0129H13.5443V7.49229C13.5443 7.30349 13.4678 7.13257 13.344 7.00884C13.2203 6.88512 13.0494 6.80859 12.8606 6.80859H3.28888C2.91128 6.80859 2.60518 7.11469 2.60518 7.49229V15.0129H1.92149C1.54389 15.0129 1.23779 15.319 1.23779 15.6966C1.23779 16.0742 1.54389 16.3803 1.92149 16.3803H14.228C14.6056 16.3803 14.9117 16.0742 14.9117 15.6966C14.9117 15.319 14.6056 15.0129 14.228 15.0129ZM12.1769 8.17598V12.9618H3.97257V8.17598H12.1769ZM6.70735 15.0129V14.3292H9.44213V15.0129H6.70735Z"></path></g>
       <g id="screenshot" viewbox="0 0 20 20"><path fill-rule="evenodd" d="M15.211 13.9537V6.04687H4.47659V13.9537H15.211ZM2.6875 4.30469H17V15.6958H2.6875V4.30469ZM10.8731 8.79554C10.5634 8.49395 10.1433 8.32451 9.70535 8.32451C9.26736 8.32451 8.84734 8.49395 8.53763 8.79554C8.22793 9.09713 8.05394 9.50617 8.05394 9.93268C8.05394 10.3592 8.22793 10.7682 8.53763 11.0698C8.84734 11.3714 9.26736 11.5408 9.70535 11.5408C10.1433 11.5408 10.5634 11.3714 10.8731 11.0698C11.1828 10.7682 11.3568 10.3592 11.3568 9.93268C11.3568 9.50617 11.1828 9.09713 10.8731 8.79554ZM8.02328 7.48126C8.52117 7.1573 9.10654 6.98438 9.70535 6.98438C10.5083 6.98438 11.2784 7.29501 11.8462 7.84792C12.414 8.40083 12.733 9.15074 12.733 9.93268C12.733 10.5158 12.5554 11.0858 12.2228 11.5707C11.8901 12.0555 11.4172 12.4334 10.864 12.6566C10.3108 12.8797 9.70199 12.9381 9.11469 12.8243C8.52739 12.7106 7.98792 12.4298 7.5645 12.0174C7.14107 11.6051 6.85274 11.0798 6.73591 10.5079C6.61909 9.93595 6.67904 9.34315 6.90819 8.80442C7.13734 8.26568 7.52539 7.80523 8.02328 7.48126Z"></g>
       <g id="volume-down"><path d="M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z"></g>
       <g id="volume-mute"><path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"></g>
diff --git a/ash/webui/diagnostics_ui/resources/keyboard_tester.html b/ash/webui/diagnostics_ui/resources/keyboard_tester.html
index ba5ac0dd..7a9685b 100644
--- a/ash/webui/diagnostics_ui/resources/keyboard_tester.html
+++ b/ash/webui/diagnostics_ui/resources/keyboard_tester.html
@@ -17,7 +17,8 @@
           mechanical-layout="[[diagramMechanicalLayout_]]"
           physical-layout="[[diagramPhysicalLayout_]]"
           show-assistant-key="[[keyboard.hasAssistantKey]]"
-          show-number-pad="[[showNumberPad_]]">
+          show-number-pad="[[showNumberPad_]]"
+          top-row-keys="[[topRowKeys_]]">
       </keyboard-diagram>
     </template>
   </div>
diff --git a/ash/webui/diagnostics_ui/resources/keyboard_tester.js b/ash/webui/diagnostics_ui/resources/keyboard_tester.js
index 116600e..e519faa 100644
--- a/ash/webui/diagnostics_ui/resources/keyboard_tester.js
+++ b/ash/webui/diagnostics_ui/resources/keyboard_tester.js
@@ -4,16 +4,44 @@
 
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
 
-import {MechanicalLayout as DiagramMechanicalLayout, PhysicalLayout as DiagramPhysicalLayout} from 'chrome://resources/ash/common/keyboard_diagram.js';
+import {MechanicalLayout as DiagramMechanicalLayout, PhysicalLayout as DiagramPhysicalLayout, TopRowKey as DiagramTopRowKey} from 'chrome://resources/ash/common/keyboard_diagram.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {KeyboardInfo, MechanicalLayout, NumberPadPresence, PhysicalLayout} from './diagnostics_types.js';
+import {KeyboardInfo, MechanicalLayout, NumberPadPresence, PhysicalLayout, TopRowKey} from './diagnostics_types.js';
 
 /**
  * @fileoverview
  * 'keyboard-tester' displays a tester UI for a keyboard.
  */
 
+/**
+ * Map from Mojo TopRowKey constants to keyboard diagram top row key
+ * definitions.
+ */
+const topRowKeyMap = {
+  [TopRowKey.kNone]: DiagramTopRowKey.kNone,
+  [TopRowKey.kBack]: DiagramTopRowKey.kBack,
+  [TopRowKey.kForward]: DiagramTopRowKey.kForward,
+  [TopRowKey.kRefresh]: DiagramTopRowKey.kRefresh,
+  [TopRowKey.kFullscreen]: DiagramTopRowKey.kFullscreen,
+  [TopRowKey.kOverview]: DiagramTopRowKey.kOverview,
+  [TopRowKey.kScreenshot]: DiagramTopRowKey.kScreenshot,
+  [TopRowKey.kScreenBrightnessDown]: DiagramTopRowKey.kScreenBrightnessDown,
+  [TopRowKey.kScreenBrightnessUp]: DiagramTopRowKey.kScreenBrightnessUp,
+  [TopRowKey.kPrivacyScreenToggle]: DiagramTopRowKey.kPrivacyScreenToggle,
+  [TopRowKey.kVolumeMute]: DiagramTopRowKey.kVolumeMute,
+  [TopRowKey.kVolumeDown]: DiagramTopRowKey.kVolumeDown,
+  [TopRowKey.kVolumeUp]: DiagramTopRowKey.kVolumeUp,
+  [TopRowKey.kKeyboardBacklightDown]: DiagramTopRowKey.kKeyboardBacklightDown,
+  [TopRowKey.kKeyboardBacklightUp]: DiagramTopRowKey.kKeyboardBacklightUp,
+  [TopRowKey.kNextTrack]: DiagramTopRowKey.kNextTrack,
+  [TopRowKey.kPreviousTrack]: DiagramTopRowKey.kPreviousTrack,
+  [TopRowKey.kPlayPause]: DiagramTopRowKey.kPlayPause,
+  [TopRowKey.kScreenMirror]: DiagramTopRowKey.kScreenMirror,
+  [TopRowKey.kDelete]: DiagramTopRowKey.kDelete,
+  [TopRowKey.kUnknown]: DiagramTopRowKey.kUnknown,
+};
+
 Polymer({
   is: 'keyboard-tester',
 
@@ -53,6 +81,14 @@
       type: Boolean,
       computed: 'computeShowNumberPad_(keyboard)',
     },
+
+    // TODO(crbug.com/1257138): use the proper type annotation instead of
+    // Object.
+    /** @private {!Array<!Object>} */
+    topRowKeys_: {
+      type: Array,
+      computed: 'computeTopRowKeys_(keyboard)',
+    },
   },
 
   /**
@@ -116,6 +152,19 @@
         keyboard.numberPadPresent === NumberPadPresence.kPresent;
   },
 
+
+  /**
+   * @param {?KeyboardInfo} keyboard
+   * @return {!Array<!Object>}
+   * @private
+   */
+  computeTopRowKeys_(keyboard) {
+    if (!keyboard) {
+      return [];
+    }
+    return keyboard.topRowKeys.map((keyId) => topRowKeyMap[keyId]);
+  },
+
   /** Shows the tester's dialog. */
   show() {
     this.$.dialog.showModal();
diff --git a/ash/webui/personalization_app/resources/BUILD.gn b/ash/webui/personalization_app/resources/BUILD.gn
index 7ef30e7b..6af45b90 100644
--- a/ash/webui/personalization_app/resources/BUILD.gn
+++ b/ash/webui/personalization_app/resources/BUILD.gn
@@ -30,7 +30,7 @@
   "trusted/personalization_test_api.ts",
   "trusted/utils.ts",
   "trusted/wallpaper/wallpaper_actions.ts",
-  "trusted/wallpaper/untrusted_message_handler.js",
+  "trusted/wallpaper/untrusted_message_handler.ts",
   "trusted/wallpaper/wallpaper_controller.ts",
   "trusted/wallpaper/wallpaper_interface_provider.ts",
   "trusted/wallpaper/wallpaper_reducers.ts",
@@ -55,7 +55,7 @@
   "trusted/wallpaper/google_photos_zero_state_element.js",
   "trusted/wallpaper/local_images_element.ts",
   "trusted/wallpaper/styles.ts",
-  "trusted/wallpaper/wallpaper_collections_element.js",
+  "trusted/wallpaper/wallpaper_collections_element.ts",
   "trusted/wallpaper/wallpaper_error_element.ts",
   "trusted/wallpaper/wallpaper_fullscreen_element.ts",
   "trusted/wallpaper/wallpaper_grid_item_element.ts",
diff --git a/ash/webui/personalization_app/resources/common/constants.ts b/ash/webui/personalization_app/resources/common/constants.ts
index d1c73ce..3545de55 100644
--- a/ash/webui/personalization_app/resources/common/constants.ts
+++ b/ash/webui/personalization_app/resources/common/constants.ts
@@ -67,7 +67,7 @@
 
 export type SendImageCountsEvent = {
   type: EventType.SEND_IMAGE_COUNTS,
-  counts: {[key: string]: number},
+  counts: {[key: string]: number|null},
 };
 
 /**
diff --git a/ash/webui/personalization_app/resources/trusted/iframe_api.ts b/ash/webui/personalization_app/resources/trusted/iframe_api.ts
index b54997c..b081e540 100644
--- a/ash/webui/personalization_app/resources/trusted/iframe_api.ts
+++ b/ash/webui/personalization_app/resources/trusted/iframe_api.ts
@@ -58,7 +58,7 @@
  * failed to load.
  */
 export function sendImageCounts(
-    target: Window, counts: {[key: string]: number}) {
+    target: Window, counts: {[key: string]: number|null}) {
   const event: constants.SendImageCountsEvent = {
     type: constants.EventType.SEND_IMAGE_COUNTS,
     counts
@@ -146,11 +146,12 @@
  * contains valid data. Ignores messages that are not of the expected type.
  */
 export function validateReceivedSelection(
-    event: MessageEvent, choices: WallpaperCollection[]): WallpaperCollection;
+    event: MessageEvent,
+    choices: WallpaperCollection[]|null): WallpaperCollection;
 export function validateReceivedSelection(
-    event: MessageEvent, choices: WallpaperImage[]): WallpaperImage;
+    event: MessageEvent, choices: WallpaperImage[]|null): WallpaperImage;
 export function validateReceivedSelection(
-    event: MessageEvent, choices: (WallpaperCollection|WallpaperImage)[]):
+    event: MessageEvent, choices: (WallpaperCollection|WallpaperImage)[]|null):
     WallpaperCollection|WallpaperImage {
   assert(isNonEmptyArray(choices), 'choices must be a non-empty array');
 
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_router_element.ts b/ash/webui/personalization_app/resources/trusted/personalization_router_element.ts
index 73e74c7..ed21737 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_router_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/personalization_router_element.ts
@@ -50,8 +50,9 @@
   private query_: string;
   private queryParams_: {id?: string; googlePhotosAlbumId?: string;};
 
-  static instance() {
-    return document.querySelector(PersonalizationRouter.is);
+  static instance(): PersonalizationRouter {
+    return document.querySelector(PersonalizationRouter.is) as
+        PersonalizationRouter;
   }
 
   /**
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/untrusted_message_handler.js b/ash/webui/personalization_app/resources/trusted/wallpaper/untrusted_message_handler.ts
similarity index 80%
rename from ash/webui/personalization_app/resources/trusted/wallpaper/untrusted_message_handler.js
rename to ash/webui/personalization_app/resources/trusted/wallpaper/untrusted_message_handler.ts
index 3a300643..d982f0e 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/untrusted_message_handler.js
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/untrusted_message_handler.ts
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {EventType, untrustedOrigin} from '/common/constants.js';
-import {validateReceivedSelection} from '/trusted/iframe_api.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 
+import {Events, EventType, untrustedOrigin} from '../../common/constants.js';
+import {validateReceivedSelection} from '../iframe_api.js';
 import {PersonalizationRouter} from '../personalization_router_element.js';
 import {PersonalizationStore} from '../personalization_store.js';
 
@@ -16,16 +16,14 @@
  * @fileoverview message handler that receives data from untrusted.
  */
 
-/**
- * @param {!Event} event
- */
-export function onMessageReceived(event) {
+export function onMessageReceived(event: MessageEvent) {
   assert(
       event.origin === untrustedOrigin, 'Message not from the correct origin');
 
   const store = PersonalizationStore.getInstance();
 
-  switch (event.data.type) {
+  const data = event.data as Events;
+  switch (data.type) {
     case EventType.SELECT_COLLECTION:
       const collections = store.data.wallpaper.backdrop.collections;
 
@@ -40,6 +38,10 @@
       break;
     case EventType.SELECT_IMAGE:
       const collectionId = PersonalizationRouter.instance().collectionId;
+      if (!collectionId) {
+        console.warn('collectionId is not available when selecting image.');
+        return;
+      }
       const images = store.data.wallpaper.backdrop.images[collectionId];
       const selectedImage = validateReceivedSelection(event, images);
       selectWallpaper(selectedImage, getWallpaperProvider(), store);
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.js b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.js
deleted file mode 100644
index 2021717..0000000
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.js
+++ /dev/null
@@ -1,471 +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.
-
-/**
- * @fileoverview
- * Polymer element that fetches and displays a list of WallpaperCollection
- * objects.
- */
-
-import './styles.js';
-
-import {kMaximumLocalImagePreviews} from '/common/constants.js';
-import {isNonEmptyArray, isNullOrArray, isNullOrNumber, promisifyOnload} from '/common/utils.js';
-import {sendCollections, sendGooglePhotosCount, sendGooglePhotosPhotos, sendImageCounts, sendLocalImageData, sendLocalImages, sendVisible} from '/trusted/iframe_api.js';
-import {afterNextRender, html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {WithPersonalizationStore} from '../personalization_store.js';
-
-import {initializeBackdropData} from './wallpaper_controller.js';
-import {getWallpaperProvider} from './wallpaper_interface_provider.js';
-
-let sendCollectionsFunction = sendCollections;
-let sendGooglePhotosCountFunction = sendGooglePhotosCount;
-let sendGooglePhotosPhotosFunction = sendGooglePhotosPhotos;
-let sendImageCountsFunction = sendImageCounts;
-let sendLocalImagesFunction = sendLocalImages;
-let sendLocalImageDataFunction = sendLocalImageData;
-
-/**
- * Mock out the iframe api functions for testing. Return promises that are
- * resolved when the function is called by |WallpaperCollectionsElement|.
- * @return {{
- *   sendCollections: Promise<?>,
- *   sendGooglePhotosCount: Promise<?>,
- *   sendGooglePhotosPhotos: Promise<?>,
- *   sendImageCounts: Promise<?>,
- *   sendLocalImages: Promise<?>,
- *   sendLocalImageData: Promise<?>,
- * }}
- */
-export function promisifyIframeFunctionsForTesting() {
-  const resolvers = {};
-  const promises = [
-    sendCollections, sendGooglePhotosCount, sendGooglePhotosPhotos,
-    sendImageCounts, sendLocalImages, sendLocalImageData
-  ].reduce((result, next) => {
-    result[next.name] = new Promise(resolve => resolvers[next.name] = resolve);
-    return result;
-  }, {});
-  sendCollectionsFunction = (...args) => resolvers[sendCollections.name](args);
-  sendGooglePhotosCountFunction = (...args) =>
-      resolvers[sendGooglePhotosCount.name](args);
-  sendGooglePhotosPhotosFunction = (...args) =>
-      resolvers[sendGooglePhotosPhotos.name](args);
-  sendImageCountsFunction = (...args) => resolvers[sendImageCounts.name](args);
-  sendLocalImagesFunction = (...args) => resolvers[sendLocalImages.name](args);
-  sendLocalImageDataFunction = (...args) =>
-      resolvers[sendLocalImageData.name](args);
-  return promises;
-}
-
-/** @polymer */
-export class WallpaperCollections extends WithPersonalizationStore {
-  static get is() {
-    return 'wallpaper-collections';
-  }
-
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
-  static get properties() {
-    return {
-      /**
-       * Hidden state of this element. Used to notify iframe of visibility
-       * changes.
-       */
-      hidden: {
-        type: Boolean,
-        reflectToAttribute: true,
-        observer: 'onHiddenChanged_',
-      },
-
-      /**
-       * @type {?Array<!WallpaperCollection>}
-       * @private
-       */
-      collections_: {
-        type: Array,
-      },
-
-      /** @private */
-      collectionsLoading_: {
-        type: Boolean,
-      },
-
-      /**
-       * The list of Google Photos photos.
-       * @type {?Array<undefined>}
-       * @private
-       */
-      googlePhotos: {
-        type: Array,
-      },
-
-      /**
-       * Whether the list of Google Photos photos is currently loading.
-       * @type {boolean}
-       * @private
-       */
-      googlePhotosLoading_: {
-        type: Boolean,
-      },
-
-      /**
-       * The count of Google Photos photos.
-       * @type {?number}
-       * @private
-       */
-      googlePhotosCount: {
-        type: Number,
-      },
-
-      /**
-       * Whether the count of Google Photos photos is currently loading.
-       * @type {boolean}
-       * @private
-       */
-      googlePhotosCountLoading_: {
-        type: Boolean,
-      },
-
-      /**
-       * Contains a mapping of collection id to an array of images.
-       * @type {Object<string,
-       *     Array<!WallpaperImage>>}
-       * @private
-       */
-      images_: {
-        type: Object,
-      },
-
-      /**
-       * Contains a mapping of collection id to loading boolean.
-       * @type {Object<string, boolean>}
-       * @private
-       */
-      imagesLoading_: {
-        type: Object,
-      },
-
-      /**
-       * @type {Array<!mojoBase.mojom.FilePath>}
-       * @private
-       */
-      localImages_: {
-        type: Array,
-      },
-
-      /**
-       * Whether the local image list is currently loading.
-       * @type {boolean}
-       * @private
-       */
-      localImagesLoading_: {
-        type: Boolean,
-      },
-
-      /**
-       * Stores a mapping of local image id to loading status.
-       * @type {!Object<string, boolean>}
-       * @private
-       */
-      localImageDataLoading_: {
-        type: Object,
-      },
-
-      /**
-       * Stores a mapping of local image id to thumbnail data.
-       * @type {Object<string, string>}
-       * @private
-       */
-      localImageData_: {
-        type: Object,
-      },
-
-      /** @private */
-      hasError_: {
-        type: Boolean,
-        // Call computed functions with their dependencies as arguments so that
-        // polymer knows when to re-run the computation.
-        computed:
-            'computeHasError_(collections_, collectionsLoading_, localImages_, localImagesLoading_)',
-      },
-    };
-  }
-
-  static get observers() {
-    return [
-      'onCollectionsChanged_(collections_, collectionsLoading_)',
-      'onCollectionImagesChanged_(images_, imagesLoading_)',
-      'onGooglePhotosChanged_(googlePhotos_, googlePhotosLoading_)',
-      'onGooglePhotosCountChanged_(googlePhotosCount_, googlePhotosCountLoading_)',
-      'onLocalImagesChanged_(localImages_, localImagesLoading_)',
-      'onLocalImageDataChanged_(localImages_, localImageData_, localImageDataLoading_)',
-    ];
-  }
-
-  /** @override */
-  constructor() {
-    super();
-    /** @private */
-    this.wallpaperProvider_ = getWallpaperProvider();
-    this.iframePromise_ = /** @type {!Promise<!HTMLIFrameElement>} */ (
-        promisifyOnload(this, 'collections-iframe', afterNextRender));
-
-    /**
-     * @type {boolean}
-     */
-    this.didSendLocalImageData_ = false;
-  }
-
-  /** @override */
-  connectedCallback() {
-    super.connectedCallback();
-    this.watch('collections_', state => state.wallpaper.backdrop.collections);
-    this.watch(
-        'collectionsLoading_', state => state.wallpaper.loading.collections);
-    this.watch('googlePhotos_', state => state.wallpaper.googlePhotos.photos);
-    this.watch(
-        'googlePhotosLoading_',
-        state => state.wallpaper.loading.googlePhotos.photos);
-    this.watch(
-        'googlePhotosCount_', state => state.wallpaper.googlePhotos.count);
-    this.watch(
-        'googlePhotosCountLoading_',
-        state => state.wallpaper.loading.googlePhotos.count);
-    this.watch('images_', state => state.wallpaper.backdrop.images);
-    this.watch('imagesLoading_', state => state.wallpaper.loading.images);
-    this.watch('localImages_', state => state.wallpaper.local.images);
-    this.watch(
-        'localImagesLoading_', state => state.wallpaper.loading.local.images);
-    this.watch('localImageData_', state => state.wallpaper.local.data);
-    this.watch(
-        'localImageDataLoading_', state => state.wallpaper.loading.local.data);
-    this.updateFromStore();
-    initializeBackdropData(this.wallpaperProvider_, this.getStore());
-  }
-
-  /**
-   * Notify iframe that this element visibility has changed.
-   * @param {boolean} hidden
-   * @private
-   */
-  async onHiddenChanged_(hidden) {
-    if (!hidden) {
-      document.title = this.i18n('title');
-    }
-    const iframe = await this.iframePromise_;
-    sendVisible(/** @type {!Window} */ (iframe.contentWindow), !hidden);
-  }
-
-  /**
-   * @param {?Array<!WallpaperCollection>}
-   *     collections
-   * @param {boolean} collectionsLoading
-   * @param {Array<!mojoBase.mojom.FilePath>}
-   *     localImages
-   * @param {boolean} localImagesLoading
-   * @return {boolean}
-   * @private
-   */
-  computeHasError_(
-      collections, collectionsLoading, localImages, localImagesLoading) {
-    return this.localImagesError_(localImages, localImagesLoading) &&
-        this.collectionsError_(collections, collectionsLoading);
-  }
-
-  /**
-   * @param {?Array<!WallpaperCollection>}
-   *     collections
-   * @param {boolean} collectionsLoading
-   * @return {boolean}
-   * @private
-   */
-  collectionsError_(collections, collectionsLoading) {
-    return !collectionsLoading && !isNonEmptyArray(collections);
-  }
-
-  /**
-   * @param {Array<!mojoBase.mojom.FilePath>}
-   *     localImages
-   * @param {boolean} localImagesLoading
-   * @return {boolean}
-   * @private
-   */
-  localImagesError_(localImages, localImagesLoading) {
-    return !localImagesLoading && !isNonEmptyArray(localImages);
-  }
-
-  /**
-   * Send updated wallpaper collections to the iframe.
-   * @param {?Array<!WallpaperCollection>}
-   *     collections
-   * @private
-   */
-  async onCollectionsChanged_(collections, collectionsLoading) {
-    // Check whether collections are loaded before sending to
-    // the iframe. Collections could be null/empty array.
-    if (!collectionsLoading) {
-      const iframe = await this.iframePromise_;
-      sendCollectionsFunction(iframe.contentWindow, collections);
-    }
-  }
-
-  /**
-   * Send count of image units in each collection when a new collection is
-   * fetched. D/L variants of the same image represent a count of 1.
-   * @param {Object<string,
-   *     Array<!WallpaperImage>>} images
-   * @param {Object<string, boolean>} imagesLoading
-   * @private
-   */
-  async onCollectionImagesChanged_(images, imagesLoading) {
-    if (!images || !imagesLoading) {
-      return;
-    }
-    const counts =
-        Object.entries(images)
-            .filter(([collectionId]) => {
-              return imagesLoading[/** @type {string} */ (collectionId)] ===
-                  false;
-            })
-            .map(([key, value]) => {
-              // Collection has completed loading. If no images were retrieved,
-              // set count value to null to indicate failure.
-              if (Array.isArray(value)) {
-                const unitIds = new Set();
-                value.forEach(image => {
-                  unitIds.add(image.unitId);
-                });
-                return [key, unitIds.size];
-              } else {
-                return [key, null];
-              }
-            })
-            .reduce((result, [key, value]) => {
-              result[key] = value;
-              return result;
-            }, {});
-    const iframe = await this.iframePromise_;
-    sendImageCountsFunction(
-        /** @type {!Window} */ (iframe.contentWindow), counts);
-  }
-
-  /**
-   * Invoked on changes to the list of Google Photos photos.
-   * @param {?Array<undefined>} googlePhotos
-   * @param {boolean} googlePhotosLoading
-   * @private
-   */
-  async onGooglePhotosChanged_(googlePhotos, googlePhotosLoading) {
-    if (googlePhotosLoading || !isNullOrArray(googlePhotos)) {
-      return;
-    }
-    const iframe = await this.iframePromise_;
-    sendGooglePhotosPhotosFunction(
-        /** @type {!Window} */ (iframe.contentWindow), googlePhotos);
-  }
-
-  /**
-   * Invoked on changes to the count of Google Photos photos.
-   * @param {?number} googlePhotosCount
-   * @param {boolean} googlePhotosCountLoading
-   * @private
-   */
-  async onGooglePhotosCountChanged_(
-      googlePhotosCount, googlePhotosCountLoading) {
-    if (googlePhotosCountLoading || !isNullOrNumber(googlePhotosCount)) {
-      return;
-    }
-    const iframe = await this.iframePromise_;
-    sendGooglePhotosCountFunction(
-        /** @type {!Window} */ (iframe.contentWindow), googlePhotosCount);
-  }
-
-  /**
-   * Send updated local images list to the iframe.
-   * @param {?Array<!mojoBase.mojom.FilePath>} localImages
-   * @param {boolean} localImagesLoading
-   * @private
-   */
-  async onLocalImagesChanged_(localImages, localImagesLoading) {
-    this.didSendLocalImageData_ = false;
-    if (!localImagesLoading && Array.isArray(localImages)) {
-      const iframe = await this.iframePromise_;
-      sendLocalImagesFunction(
-          /** @type {!Window} */ (iframe.contentWindow), localImages);
-    }
-  }
-
-  /**
-   * Send up to |maximumImageThumbnailsCount| image thumbnails to untrusted.
-   * @param {?Array<!mojoBase.mojom.FilePath>} images
-   * @param {?Object<string, string>} imageData
-   * @param {?Object<string, boolean>} imageDataLoading
-   * @private
-   */
-  async onLocalImageDataChanged_(images, imageData, imageDataLoading) {
-    if (!Array.isArray(images) || !imageData || !imageDataLoading ||
-        this.didSendLocalImageData_) {
-      return;
-    }
-
-    /** @type !Array<string> */
-    const successfullyLoaded = images.map(image => image.path).filter(key => {
-      const doneLoading = imageDataLoading[key] === false;
-      const success = !!imageData[key];
-      return success && doneLoading;
-    });
-
-    /**
-     * @return {boolean}
-     */
-    function shouldSendImageData() {
-      // All images (up to |kMaximumLocalImagePreviews|) have loaded.
-      const didLoadMaximum = successfullyLoaded.length >=
-          Math.min(kMaximumLocalImagePreviews, images.length);
-
-      return didLoadMaximum ||
-          // No more images to load so send now even if some failed.
-          images.every(image => imageDataLoading[image.path] === false);
-    }
-
-
-    if (shouldSendImageData()) {
-      // Also send information about which images failed to load. This is
-      // necessary to decide whether to show loading animation or failure svg
-      // while updating local images.
-      const failures = images.map(image => image.path)
-                           .filter(key => {
-                             const doneLoading =
-                                 imageDataLoading[key] === false;
-                             const failure = imageData[key] === '';
-                             return failure && doneLoading;
-                           })
-                           .reduce((result, key) => {
-                             // Empty string means that this image failed to
-                             // load.
-                             result[key] = '';
-                             return result;
-                           }, {});
-
-      const data =
-          successfullyLoaded.filter((_, i) => i < kMaximumLocalImagePreviews)
-              .reduce((result, key) => {
-                result[key] = imageData[key];
-                return result;
-              }, failures);
-
-      this.didSendLocalImageData_ = true;
-
-      const iframe = await this.iframePromise_;
-      sendLocalImageDataFunction(
-          /** @type {!Window} */ (iframe.contentWindow), data);
-    }
-  }
-}
-
-customElements.define(WallpaperCollections.is, WallpaperCollections);
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.ts
new file mode 100644
index 0000000..1bab1e67
--- /dev/null
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.ts
@@ -0,0 +1,398 @@
+// 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.
+
+/**
+ * @fileoverview
+ * Polymer element that fetches and displays a list of WallpaperCollection
+ * objects.
+ */
+
+import './styles.js';
+
+import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
+import {afterNextRender, html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {kMaximumLocalImagePreviews} from '../../common/constants.js';
+import {isNonEmptyArray, isNullOrArray, isNullOrNumber, promisifyOnload} from '../../common/utils.js';
+import {sendCollections, sendGooglePhotosCount, sendGooglePhotosPhotos, sendImageCounts, sendLocalImageData, sendLocalImages, sendVisible} from '../iframe_api.js';
+import {WallpaperCollection, WallpaperImage, WallpaperProviderInterface} from '../personalization_app.mojom-webui.js';
+import {WithPersonalizationStore} from '../personalization_store.js';
+
+import {initializeBackdropData} from './wallpaper_controller.js';
+import {getWallpaperProvider} from './wallpaper_interface_provider.js';
+
+let sendCollectionsFunction = sendCollections;
+let sendGooglePhotosCountFunction = sendGooglePhotosCount;
+let sendGooglePhotosPhotosFunction = sendGooglePhotosPhotos;
+let sendImageCountsFunction = sendImageCounts;
+let sendLocalImagesFunction = sendLocalImages;
+let sendLocalImageDataFunction = sendLocalImageData;
+
+/**
+ * Mock out the iframe api functions for testing. Return promises that are
+ * resolved when the function is called by |WallpaperCollectionsElement|.
+ */
+interface PromisifyResult {
+  sendCollections: Promise<unknown>;
+  sendGooglePhotosCount: Promise<unknown>;
+  sendGooglePhotosPhotos: Promise<unknown>;
+  sendImageCounts: Promise<unknown>;
+  sendLocalImages: Promise<unknown>;
+  sendLocalImageData: Promise<unknown>;
+}
+
+export function promisifyIframeFunctionsForTesting(): PromisifyResult {
+  const resolvers = {} as
+      {[key in keyof PromisifyResult]: (_: unknown) => void};
+  const promises = ([
+                     'sendCollections',
+                     'sendGooglePhotosCount',
+                     'sendGooglePhotosPhotos',
+                     'sendImageCounts',
+                     'sendLocalImages',
+                     'sendLocalImageData',
+                   ] as (keyof PromisifyResult)[])
+                       .reduce((result, next) => {
+                         result[next] =
+                             new Promise(resolve => resolvers[next] = resolve);
+                         return result;
+                       }, {} as PromisifyResult);
+  sendCollectionsFunction = (...args) => resolvers['sendCollections'](args);
+  sendGooglePhotosCountFunction = (...args) =>
+      resolvers['sendGooglePhotosCount'](args);
+  sendGooglePhotosPhotosFunction = (...args) =>
+      resolvers['sendGooglePhotosPhotos'](args);
+  sendImageCountsFunction = (...args) => resolvers['sendImageCounts'](args);
+  sendLocalImagesFunction = (...args) => resolvers['sendLocalImages'](args);
+  sendLocalImageDataFunction = (...args) =>
+      resolvers['sendLocalImageData'](args);
+  return promises;
+}
+
+export class WallpaperCollections extends WithPersonalizationStore {
+  static get is() {
+    return 'wallpaper-collections';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      /**
+       * Hidden state of this element. Used to notify iframe of visibility
+       * changes.
+       */
+      hidden: {
+        type: Boolean,
+        reflectToAttribute: true,
+        observer: 'onHiddenChanged_',
+      },
+
+      collections_: Array,
+
+      collectionsLoading_: Boolean,
+
+      /**
+       * The list of Google Photos photos.
+       */
+      googlePhotos_: Array,
+
+      /**
+       * Whether the list of Google Photos photos is currently loading.
+       */
+      googlePhotosLoading_: Boolean,
+
+      /**
+       * The count of Google Photos photos.
+       */
+      googlePhotosCount_: Number,
+
+      /**
+       * Whether the count of Google Photos photos is currently loading.
+       */
+      googlePhotosCountLoading_: Boolean,
+
+      /**
+       * Contains a mapping of collection id to an array of images.
+       */
+      images_: Object,
+
+      /**
+       * Contains a mapping of collection id to loading boolean.
+       */
+      imagesLoading_: Object,
+
+      localImages_: Array,
+
+      /**
+       * Whether the local image list is currently loading.
+       */
+      localImagesLoading_: Boolean,
+
+      /**
+       * Stores a mapping of local image id to loading status.
+       */
+      localImageDataLoading_: Object,
+
+      /**
+       * Stores a mapping of local image id to thumbnail data.
+       */
+      localImageData_: Object,
+
+      hasError_: {
+        type: Boolean,
+        // Call computed functions with their dependencies as arguments so that
+        // polymer knows when to re-run the computation.
+        computed:
+            'computeHasError_(collections_, collectionsLoading_, localImages_, localImagesLoading_)',
+      },
+    };
+  }
+
+  hidden: boolean;
+  private collections_: WallpaperCollection[];
+  private collectionsLoading_: boolean;
+  private googlePhotos_: unknown[]|null;
+  private googlePhotosLoading_: boolean;
+  private googlePhotosCount_: number|null;
+  private googlePhotosCountLoading_: boolean;
+  private images_: Record<string, WallpaperImage[]>;
+  private imagesLoading_: Record<string, boolean>;
+  private localImages_: FilePath[];
+  private localImagesLoading_: boolean;
+  private localImageData_: Record<string, string>;
+  private localImageDataLoading_: Record<string, boolean>;
+  private hasError_: boolean;
+
+  private wallpaperProvider_: WallpaperProviderInterface;
+  private iframePromise_: Promise<HTMLIFrameElement>;
+  private didSendLocalImageData_: boolean;
+
+
+  static get observers() {
+    return [
+      'onCollectionsChanged_(collections_, collectionsLoading_)',
+      'onCollectionImagesChanged_(images_, imagesLoading_)',
+      'onGooglePhotosChanged_(googlePhotos_, googlePhotosLoading_)',
+      'onGooglePhotosCountChanged_(googlePhotosCount_, googlePhotosCountLoading_)',
+      'onLocalImagesChanged_(localImages_, localImagesLoading_)',
+      'onLocalImageDataChanged_(localImages_, localImageData_, localImageDataLoading_)',
+    ];
+  }
+
+  constructor() {
+    super();
+    this.wallpaperProvider_ = getWallpaperProvider();
+    this.iframePromise_ =
+        promisifyOnload(this, 'collections-iframe', afterNextRender) as
+        Promise<HTMLIFrameElement>;
+    this.didSendLocalImageData_ = false;
+  }
+
+  connectedCallback() {
+    super.connectedCallback();
+    this.watch('collections_', state => state.wallpaper.backdrop.collections);
+    this.watch(
+        'collectionsLoading_', state => state.wallpaper.loading.collections);
+    this.watch('googlePhotos_', state => state.wallpaper.googlePhotos.photos);
+    this.watch(
+        'googlePhotosLoading_',
+        state => state.wallpaper.loading.googlePhotos.photos);
+    this.watch(
+        'googlePhotosCount_', state => state.wallpaper.googlePhotos.count);
+    this.watch(
+        'googlePhotosCountLoading_',
+        state => state.wallpaper.loading.googlePhotos.count);
+    this.watch('images_', state => state.wallpaper.backdrop.images);
+    this.watch('imagesLoading_', state => state.wallpaper.loading.images);
+    this.watch('localImages_', state => state.wallpaper.local.images);
+    this.watch(
+        'localImagesLoading_', state => state.wallpaper.loading.local.images);
+    this.watch('localImageData_', state => state.wallpaper.local.data);
+    this.watch(
+        'localImageDataLoading_', state => state.wallpaper.loading.local.data);
+    this.updateFromStore();
+    initializeBackdropData(this.wallpaperProvider_, this.getStore());
+  }
+
+  /**
+   * Notify iframe that this element visibility has changed.
+   */
+  private async onHiddenChanged_(hidden: boolean) {
+    if (!hidden) {
+      document.title = this.i18n('title');
+    }
+    const iframe = await this.iframePromise_;
+    sendVisible(iframe.contentWindow!, !hidden);
+  }
+
+  private computeHasError_(
+      collections: WallpaperCollection[], collectionsLoading: boolean,
+      localImages: FilePath[], localImagesLoading: boolean): boolean {
+    return this.localImagesError_(localImages, localImagesLoading) &&
+        this.collectionsError_(collections, collectionsLoading);
+  }
+
+  private collectionsError_(
+      collections: WallpaperCollection[],
+      collectionsLoading: boolean): boolean {
+    return !collectionsLoading && !isNonEmptyArray(collections);
+  }
+
+  private localImagesError_(
+      localImages: FilePath[], localImagesLoading: boolean): boolean {
+    return !localImagesLoading && !isNonEmptyArray(localImages);
+  }
+
+  private async onCollectionsChanged_(
+      collections: WallpaperCollection[], collectionsLoading: boolean) {
+    // Check whether collections are loaded before sending to
+    // the iframe. Collections could be null/empty array.
+    if (!collectionsLoading) {
+      const iframe = await this.iframePromise_;
+      sendCollectionsFunction(iframe.contentWindow!, collections);
+    }
+  }
+
+  /**
+   * Send count of image units in each collection when a new collection is
+   * fetched. D/L variants of the same image represent a count of 1.
+   */
+  private async onCollectionImagesChanged_(
+      images: Record<string, WallpaperImage[]>,
+      imagesLoading: Record<string, boolean>) {
+    if (!images || !imagesLoading) {
+      return;
+    }
+    const counts = Object.entries(images)
+                       .filter(([collectionId]) => {
+                         return imagesLoading[collectionId] === false;
+                       })
+                       .map(([key, value]) => {
+                         // Collection has completed loading. If no images were
+                         // retrieved, set count value to null to indicate
+                         // failure.
+                         if (Array.isArray(value)) {
+                           const unitIds = new Set();
+                           value.forEach(image => {
+                             unitIds.add(image.unitId);
+                           });
+                           return [key, unitIds.size] as [string, number];
+                         } else {
+                           return [key, null] as [string, null];
+                         }
+                       })
+                       .reduce((result, [key, value]) => {
+                         result[key!] = value;
+                         return result;
+                       }, {} as Record<string, number|null>);
+    const iframe = await this.iframePromise_;
+    sendImageCountsFunction(iframe.contentWindow!, counts);
+  }
+
+  /**
+   * Invoked on changes to the list of Google Photos photos.
+   */
+  private async onGooglePhotosChanged_(
+      googlePhotos: unknown[], googlePhotosLoading: boolean) {
+    if (googlePhotosLoading || !isNullOrArray(googlePhotos)) {
+      return;
+    }
+    const iframe = await this.iframePromise_;
+    sendGooglePhotosPhotosFunction(iframe.contentWindow!, googlePhotos);
+  }
+
+  /**
+   * Invoked on changes to the count of Google Photos photos.
+   */
+  private async onGooglePhotosCountChanged_(
+      googlePhotosCount: number|null, googlePhotosCountLoading: boolean) {
+    if (googlePhotosCountLoading || !isNullOrNumber(googlePhotosCount)) {
+      return;
+    }
+    const iframe = await this.iframePromise_;
+    sendGooglePhotosCountFunction(iframe.contentWindow!, googlePhotosCount);
+  }
+
+  /**
+   * Send updated local images list to the iframe.
+   */
+  private async onLocalImagesChanged_(
+      localImages: FilePath[]|null, localImagesLoading: boolean) {
+    this.didSendLocalImageData_ = false;
+    if (!localImagesLoading && Array.isArray(localImages)) {
+      const iframe = await this.iframePromise_;
+      sendLocalImagesFunction(iframe.contentWindow!, localImages);
+    }
+  }
+
+  /**
+   * Send up to |maximumImageThumbnailsCount| image thumbnails to untrusted.
+   */
+  private async onLocalImageDataChanged_(
+      images: FilePath[]|null, imageData: Record<string, string>,
+      imageDataLoading: Record<string, boolean>) {
+    if (!Array.isArray(images) || !imageData || !imageDataLoading ||
+        this.didSendLocalImageData_) {
+      return;
+    }
+
+    const successfullyLoaded: string[] =
+        images.map(image => image.path).filter(key => {
+          const doneLoading = imageDataLoading[key] === false;
+          const success = !!imageData[key];
+          return success && doneLoading;
+        });
+
+    function shouldSendImageData() {
+      if (!Array.isArray(images)) {
+        return false;
+      }
+
+      // All images (up to |kMaximumLocalImagePreviews|) have loaded.
+      const didLoadMaximum = successfullyLoaded.length >=
+          Math.min(kMaximumLocalImagePreviews, images.length);
+
+      return didLoadMaximum ||
+          // No more images to load so send now even if some failed.
+          images.every(image => imageDataLoading[image.path] === false);
+    }
+
+
+    if (shouldSendImageData()) {
+      // Also send information about which images failed to load. This is
+      // necessary to decide whether to show loading animation or failure svg
+      // while updating local images.
+      const failures = images.map(image => image.path)
+                           .filter(key => {
+                             const doneLoading =
+                                 imageDataLoading[key] === false;
+                             const failure = imageData[key] === '';
+                             return failure && doneLoading;
+                           })
+                           .reduce((result, key) => {
+                             // Empty string means that this image failed to
+                             // load.
+                             result[key] = '';
+                             return result;
+                           }, {} as Record<string, string>);
+
+      const data =
+          successfullyLoaded.filter((_, i) => i < kMaximumLocalImagePreviews)
+              .reduce((result, key) => {
+                result[key] = imageData[key];
+                return result;
+              }, failures);
+
+      this.didSendLocalImageData_ = true;
+
+      const iframe = await this.iframePromise_;
+      sendLocalImageDataFunction(iframe.contentWindow!, data);
+    }
+  }
+}
+
+customElements.define(WallpaperCollections.is, WallpaperCollections);
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_fullscreen_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_fullscreen_element.ts
index 073df0cf..7a429a1d 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_fullscreen_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_fullscreen_element.ts
@@ -134,9 +134,8 @@
     if (hidden) {
       // SWA also supports exiting fullscreen when users press ESC. In this
       // case, the preview mode may be still on so we have to call cancel
-      // preview.
-      // This call is no-op when the user clicks on exit button or set as
-      // wallpaper button.
+      // preview. This call is no-op when the user clicks on set as wallpaper
+      // button.
       cancelPreviewWallpaper(this.wallpaperProvider_);
       this.dispatch(setFullscreenEnabledAction(/*enabled=*/ false));
       document.body.classList.remove(fullscreenClass);
@@ -146,14 +145,15 @@
   }
 
   private async onClickExit_() {
+    await this.exitFullscreen();
     await cancelPreviewWallpaper(this.wallpaperProvider_);
-    this.exitFullscreen();
   }
 
   private async onClickConfirm_() {
-    // Wait for wallpaper to be confirmed before exiting full screen mode.
+    // Begin to exit fullscreen mode before confirming preview wallpaper. This
+    // makes local images and online images execute updates in the same order.
+    await this.exitFullscreen();
     await confirmPreviewWallpaper(this.wallpaperProvider_);
-    this.exitFullscreen();
   }
 
   private async onClickLayout_(event: MouseEvent) {
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.js b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.js
index bf22e6c..a9d60c7 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.js
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.js
@@ -261,6 +261,16 @@
    *     currentWallpaper
    */
   onWallpaperChanged(currentWallpaper) {
+    // Ignore updates while in fullscreen preview mode. The attribution
+    // information is for the old (non-preview) wallpaper. This is because
+    // setting an image in preview mode updates the image but not the stored
+    // WallpaperInfo. The wallpaper app should treat the duration of preview
+    // mode as loading. Another onWallpaperChanged will fire when preview mode
+    // is canceled or confirmed.
+    if (this.getState().wallpaper.fullscreen) {
+      return;
+    }
+
     // Clear the initial load timer if wallpaper information is received.
     if (this.initialLoadTimeout_) {
       clearTimeout(this.initialLoadTimeout_);
@@ -508,11 +518,6 @@
    * @private
    */
   getAriaLabel_(image) {
-    // Wait until full screen preview is finished. Otherwise will incorrectly
-    // read out attribution of last photo before getting updated attribution.
-    if (document.fullscreenElement) {
-      return '';
-    }
     if (!image) {
       return this.i18n('currentlySet') + ' ' +
           this.i18n('unknownImageAttribution');
diff --git a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
index dc85e0d..ac68d19 100644
--- a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
+++ b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
@@ -225,7 +225,7 @@
   private collections_: WallpaperCollection[];
   private googlePhotos_: unknown[]|null;
   private googlePhotosCount_: number|null;
-  private imageCounts_: {[key: string]: number};
+  private imageCounts_: {[key: string]: number|null};
   private localImages_: FilePath[];
   private localImageData_: {[key: string]: string};
   private tiles_: Tile[];
@@ -264,7 +264,7 @@
    */
   private onCollectionLoaded_(
       collections: WallpaperCollection[]|null,
-      imageCounts: {[key: string]: number|undefined}) {
+      imageCounts: {[key: string]: number|null}) {
     if (!Array.isArray(collections) || !imageCounts) {
       return;
     }
diff --git a/ash/webui/resources/BUILD.gn b/ash/webui/resources/BUILD.gn
index 427f9cc..8987a7e 100644
--- a/ash/webui/resources/BUILD.gn
+++ b/ash/webui/resources/BUILD.gn
@@ -90,14 +90,17 @@
 
   # Resources used by chrome://sample-system-web-app
   ash_generated_grit("sample_system_web_app_resources") {
-    source = "$root_gen_dir/ash/webui/sample_system_web_app_ui/ash_sample_system_web_app_resources.grd"
-    deps = [ "//ash/webui/sample_system_web_app_ui:build_trusted_grd" ]
+    source = "$root_gen_dir/ash/webui/sample_system_web_app_ui/resources/trusted/ash_sample_system_web_app_resources.grd"
+    deps =
+        [ "//ash/webui/sample_system_web_app_ui/resources/trusted:trusted_grd" ]
   }
 
   # Resources used by chrome-untrusted://sample-system-web-app
   ash_generated_grit("sample_system_web_app_untrusted_resources") {
-    source = "$root_gen_dir/ash/webui/sample_system_web_app_ui/ash_sample_system_web_app_untrusted_resources.grd"
-    deps = [ "//ash/webui/sample_system_web_app_ui:build_untrusted_grd" ]
+    source = "$root_gen_dir/ash/webui/sample_system_web_app_ui/resources/untrusted/ash_sample_system_web_app_untrusted_resources.grd"
+    deps = [
+      "//ash/webui/sample_system_web_app_ui/resources/untrusted:untrusted_grd",
+    ]
   }
 }
 
diff --git a/ash/webui/sample_system_web_app_ui/BUILD.gn b/ash/webui/sample_system_web_app_ui/BUILD.gn
index c61d5d08..f12d252f 100644
--- a/ash/webui/sample_system_web_app_ui/BUILD.gn
+++ b/ash/webui/sample_system_web_app_ui/BUILD.gn
@@ -33,23 +33,6 @@
   ]
 }
 
-js_type_check("closure_compile") {
-  deps = [
-    ":trusted",
-    ":untrusted",
-  ]
-  closure_flags = default_closure_args + mojom_js_args
-}
-
-js_library("untrusted") {
-  sources = [ "resources/untrusted.js" ]
-}
-
-js_library("trusted") {
-  sources = [ "resources/page_handler.js" ]
-  deps = [ "//ash/webui/sample_system_web_app_ui/mojom:trusted_webui_js" ]
-}
-
 js2gtest("browser_tests_js") {
   test_type = "mojo_lite_webui"
 
@@ -57,104 +40,3 @@
 
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 }
-
-# Rules to generate grdp files for the generated mojo bindings.
-grd_prefix = "ash_sample_system_web_app"
-
-trusted_mojo_grdp =
-    "$target_gen_dir/sample_system_web_app_trusted_mojo_resources.grdp"
-generate_grd("build_trusted_mojo_grdp") {
-  out_grd = trusted_mojo_grdp
-  grd_prefix = grd_prefix
-
-  deps = [ "//ash/webui/sample_system_web_app_ui/mojom:trusted_webui_js" ]
-
-  # Flatten out the dependency tree of your mojom and add generated bindings
-  # file here.
-  input_files = [ "ash/webui/sample_system_web_app_ui/mojom/sample_system_web_app_ui.mojom-webui.js" ]
-
-  input_files_base_dir =
-      rebase_path("$root_gen_dir/mojom-webui", "$root_build_dir")
-}
-
-shared_mojo_grdp =
-    "$target_gen_dir/sample_system_web_app_shared_mojo_resources.grdp"
-generate_grd("build_shared_mojo_grdp") {
-  grd_prefix = grd_prefix
-  out_grd = shared_mojo_grdp
-
-  deps = [ "//ash/webui/sample_system_web_app_ui/mojom:shared_webui_js" ]
-
-  # Flatten out the dependency tree of your mojom and add generated bindings
-  # file here.
-  input_files = [ "ash/webui/sample_system_web_app_ui/mojom/sample_system_web_app_shared_ui.mojom-webui.js" ]
-
-  input_files_base_dir =
-      rebase_path("$root_gen_dir/mojom-webui", "$root_build_dir")
-}
-
-untrusted_mojo_grdp =
-    "$target_gen_dir/sample_system_web_app_untrusted_mojo_resources.grdp"
-generate_grd("build_untrusted_mojo_grdp") {
-  grd_prefix = grd_prefix
-  out_grd = untrusted_mojo_grdp
-
-  deps = [ "//ash/webui/sample_system_web_app_ui/mojom:untrusted_webui_js" ]
-
-  # Flatten out the dependency tree of your mojom and add generated bindings
-  # file here.
-  input_files = [ "ash/webui/sample_system_web_app_ui/mojom/sample_system_web_app_untrusted_ui.mojom-webui.js" ]
-
-  input_files_base_dir =
-      rebase_path("$root_gen_dir/mojom-webui", "$root_build_dir")
-}
-
-generate_grd("build_untrusted_grd") {
-  input_files_base_dir = rebase_path("resources", "//")
-  input_files = [
-    "untrusted.html",
-    "untrusted.js",
-    "untrusted_page_interface.js",
-  ]
-  grd_prefix = "ash_sample_system_web_app_untrusted"
-  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
-
-  deps = [
-    ":build_shared_mojo_grdp",
-    ":build_untrusted_mojo_grdp",
-  ]
-  grdp_files = [
-    shared_mojo_grdp,
-    untrusted_mojo_grdp,
-  ]
-}
-
-generate_grd("build_trusted_grd") {
-  input_files_base_dir = rebase_path("resources", "//")
-  input_files = [
-    "app_icon_192.png",
-    "component_playground.html",
-    "index.html",
-    "inter_frame_communication.html",
-    "inter_frame_communication.js",
-    "main.js",
-    "worker.js",
-    "page_handler.js",
-    "timer.html",
-    "timer.js",
-  ]
-
-  # Manifest for generated files generated by resources:component_playground.
-  manifest_files = [ "$target_gen_dir/resources/build_manifest.json" ]
-  grd_prefix = grd_prefix
-  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
-  deps = [
-    ":build_shared_mojo_grdp",
-    ":build_trusted_mojo_grdp",
-    "resources:component_playground",
-  ]
-  grdp_files = [
-    shared_mojo_grdp,
-    trusted_mojo_grdp,
-  ]
-}
diff --git a/ash/webui/sample_system_web_app_ui/mojom/BUILD.gn b/ash/webui/sample_system_web_app_ui/mojom/BUILD.gn
index 1f74a2c..99ed5ba 100644
--- a/ash/webui/sample_system_web_app_ui/mojom/BUILD.gn
+++ b/ash/webui/sample_system_web_app_ui/mojom/BUILD.gn
@@ -4,34 +4,64 @@
 
 import("//build/config/chromeos/ui_mode.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
+import("//tools/typescript/ts_library.gni")
 
 assert(is_chromeos_ash, "Sample System Web App is ash-chrome only")
 assert(!is_official_build,
        "Sample System Web App is only built for unofficial builds")
 
+cur_dir = rebase_path(".", "//")
+
 mojom("trusted") {
   sources = [ "sample_system_web_app_ui.mojom" ]
 
   public_deps = [
-    "//ash/webui/sample_system_web_app_ui/mojom:shared",
+    ":shared",
     "//mojo/public/mojom/base",
   ]
-  webui_module_path = "/ash/webui/sample_system_web_app_ui/mojom/"
+  webui_module_path = "/$cur_dir"
 }
 
 mojom("shared") {
   sources = [ "sample_system_web_app_shared_ui.mojom" ]
 
   public_deps = [ "//mojo/public/mojom/base" ]
-  webui_module_path = "/ash/webui/sample_system_web_app_ui/mojom/"
+  webui_module_path = "/$cur_dir"
 }
 
 mojom("untrusted") {
   sources = [ "sample_system_web_app_untrusted_ui.mojom" ]
 
   public_deps = [
-    "//ash/webui/sample_system_web_app_ui/mojom:shared",
+    ":shared",
     "//mojo/public/mojom/base",
   ]
-  webui_module_path = "/ash/webui/sample_system_web_app_ui/mojom/"
+  webui_module_path = "/" + rebase_path(".", "//")
+}
+
+# Builds a TS library for generated Mojo files that can be depended on by
+# downstream TS clients. In particular, we need d.ts files to be generated from
+# the JS files so that downstream TS clients get type information.
+# TODO(crbug.com/1002798): Mojo should generate this ts_library() from actual
+# TS files rather than creating d.ts files from the generated JS files.
+ts_library("mojom_ts") {
+  mojom_gen_dir = "$root_gen_dir/mojom-webui/$cur_dir"
+  root_dir = mojom_gen_dir
+  out_dir = "$mojom_gen_dir/tsc"
+  tsconfig_base = "../tsconfig_base.json"
+  in_files = [
+    "sample_system_web_app_untrusted_ui.mojom-webui.js",
+    "sample_system_web_app_ui.mojom-webui.js",
+    "sample_system_web_app_shared_ui.mojom-webui.js",
+  ]
+
+  # Targets that generate the above .mojom-webui.js files.
+  extra_deps = [
+    ":shared_webui_js",
+    ":trusted_webui_js",
+    ":untrusted_webui_js",
+  ]
+
+  # Generates d.ts files which allows this library to be depended upon.
+  composite = true
 }
diff --git a/ash/webui/sample_system_web_app_ui/resources/BUILD.gn b/ash/webui/sample_system_web_app_ui/resources/BUILD.gn
deleted file mode 100644
index 012229e..0000000
--- a/ash/webui/sample_system_web_app_ui/resources/BUILD.gn
+++ /dev/null
@@ -1,19 +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.
-
-import("//build/config/chromeos/ui_mode.gni")
-import("//third_party/material_web_components/build_mwc_app.gni")
-
-assert(is_chromeos_ash, "Sample System Web App is ash-chrome only")
-assert(!is_official_build,
-       "Sample System Web App is only built for unofficial builds")
-
-build_mwc_app("component_playground") {
-  host = "sample-system-web-app"
-  input = rebase_path(".", root_build_dir)
-  js_module_in_files = [ "component_playground.js" ]
-  js_out_files = [ "component_playground.rollup.js" ]
-  out_manifest = "$target_gen_dir/build_manifest.json"
-  deps = []
-}
diff --git a/ash/webui/sample_system_web_app_ui/resources/inter_frame_communication.js b/ash/webui/sample_system_web_app_ui/resources/inter_frame_communication.js
deleted file mode 100644
index ac78845..0000000
--- a/ash/webui/sample_system_web_app_ui/resources/inter_frame_communication.js
+++ /dev/null
@@ -1,60 +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.
-
-// This file demonstrates how a chrome:// page can communicate with its
-// embedded chrome-untrusted:// child page.
-
-import {ParentTrustedPageReceiver} from '/ash/webui/sample_system_web_app_ui/mojom/sample_system_web_app_shared_ui.mojom-webui.js';
-import {callbackRouter} from './page_handler.js';
-
-/**
- * Implements ParentTrustedPage interface to handle requests from the child
- * page.
- *
- * Note: If you expect to have multiple listeners for your interface, consider
- * using a CallbackRouter instead. CallbackRouter provides a more event-like
- * API that makes it easier to have multiple listeners.
- *
- * @implements {ash.mojom.sample_swa.ParentTrustedPage}
- */
-class ParentTrustedPageImpl {
-  /**
-   * @param {ash.mojom.sample_swa.ParentTrustedPagePendingReceiver}
-   *     pendingReceiver
-   */
-  constructor(pendingReceiver) {
-    this.receiver_ = new ParentTrustedPageReceiver(this);
-    this.receiver_.$.bindHandle(pendingReceiver.handle);
-  }
-
-  async doSomethingForChild(task) {
-    document.querySelector('#child-task').innerText = task;
-
-    // Mojo interface's JS implementation should return an Object, even if the
-    // method only has one return value.
-    //
-    // Each field should match their return value name defined in .mojom file.
-    return {resp: 'Task done'};
-  }
-}
-
-// A promise that resolves when child page is ready. Other modules wishing to
-// use childPage need to wait for the promise.
-export const childPageReady = new Promise(resolve => {
-  callbackRouter.createParentPage.addListener(
-      (childPageRemote, parentPagePendingReceiver) => {
-        resolve({
-          childPage: childPageRemote,
-          parentPageReceiver:
-              new ParentTrustedPageImpl(parentPagePendingReceiver)
-        });
-      });
-});
-
-// Expose for testing.
-window.childPageReady = childPageReady;
-
-childPageReady.then(({childPage}) => {
-  childPage.doSomethingForParent('Hello from chrome://');
-});
diff --git a/ash/webui/sample_system_web_app_ui/resources/main.js b/ash/webui/sample_system_web_app_ui/resources/main.js
deleted file mode 100644
index 6fb58a78..0000000
--- a/ash/webui/sample_system_web_app_ui/resources/main.js
+++ /dev/null
@@ -1,60 +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.
-
-import {pageHandler, callbackRouter} from './page_handler.js';
-
-const first = document.querySelector('#number1');
-const second = document.querySelector('#number2');
-const additional = document.querySelector('#additional');
-
-const result = document.querySelector('#result');
-const form = document.querySelector('form');
-
-const workerUrlPolicy = trustedTypes.createPolicy(
-    'worker-js-static',
-    {createScriptURL: () => 'chrome://sample-system-web-app/worker.js'});
-const myWorker = new SharedWorker(workerUrlPolicy.createScriptURL(''));
-
-first.onchange = () => {
-  myWorker.port.postMessage([first.value, second.value]);
-};
-
-second.onchange = () => {
-  myWorker.port.postMessage([first.value, second.value]);
-};
-
-myWorker.port.onmessage = (event) => {
-  result.textContent = event.data[0];
-  additional.value = event.data[1];
-};
-
-// Exposes the pageHandler to the user as a window's global variable for
-// testing.
-window.pageHandler = pageHandler;
-window.callbackRouter = callbackRouter;
-window.eventCount = new Map();
-
-// Example of adding an event listener for `OnEventOccurred`.
-callbackRouter.onEventOccurred.addListener((name) => {
-  document.querySelector('#mojo-event').value = name;
-  window.eventCount.set(name, 1 + (window.eventCount.get(name) || 0));
-});
-
-// Example of sending information to the browser process.
-pageHandler.send(`message at ${Date.now()}`);
-
-// Example of getting information from the browser process.
-(async () => {
-  // Mojo results get wrapped in a "response" object that contains
-  // a member for each of the Mojo callback's argument, in this case
-  // a `preferences` member.
-  const {preferences} = await pageHandler.getPreferences();
-  document.querySelector('#background').value = preferences.background;
-  document.querySelector('#foreground').value = preferences.foreground;
-})();
-
-const mojoButton = document.querySelector('#do-something');
-mojoButton.onclick = () => {
-  pageHandler.doSomething();
-};
diff --git a/ash/webui/sample_system_web_app_ui/resources/trusted/BUILD.gn b/ash/webui/sample_system_web_app_ui/resources/trusted/BUILD.gn
new file mode 100644
index 0000000..efb1d072
--- /dev/null
+++ b/ash/webui/sample_system_web_app_ui/resources/trusted/BUILD.gn
@@ -0,0 +1,84 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+import("//third_party/material_web_components/build_mwc_app.gni")
+import("//tools/typescript/ts_library.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+assert(is_chromeos_ash, "Sample System Web App is ash-chrome only")
+assert(!is_official_build,
+       "Sample System Web App is only built for unofficial builds")
+
+generate_grd("trusted_grd") {
+  grd_prefix = "ash_sample_system_web_app"
+  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
+
+  input_files = [
+    "app_icon_192.png",
+    "component_playground.html",
+    "index.html",
+    "inter_frame_communication.html",
+    "timer.html",
+  ]
+  input_files_base_dir = rebase_path(".", "//")
+
+  # tsconfig.manifest is generated by :trusted_ts and adds all of the
+  # in_files to the grd.
+  manifest_files = [
+    "$target_gen_dir/tsconfig.manifest",
+    "$target_gen_dir/component_playground.manifest",
+  ]
+
+  # Flatten out the dependency tree of your mojom and add generated bindings
+  # grdp files here.
+  # TODO(crbug.com/1002798): We could add a mojo_grdp_deps variable to
+  # generate_grd that automatically adds these grdp files when you add a
+  # X_webui_grdp target to it.
+  grdp_files = [
+    "$target_gen_dir/../../mojom/shared_webui_resources.grdp",
+    "$target_gen_dir/../../mojom/trusted_webui_resources.grdp",
+  ]
+
+  deps = [
+    ":component_playground",
+    ":trusted_ts",
+    "../../mojom:shared_webui_grdp",
+    "../../mojom:trusted_webui_grdp",
+  ]
+}
+
+ts_library("trusted_ts") {
+  root_dir = "."
+  out_dir = "$target_gen_dir/tsc"
+
+  in_files = [
+    "inter_frame_communication.ts",
+    "main.ts",
+    "page_handler.ts",
+
+    # TODO(crbug.com/1002798): Migrate these Shared Worker files to TS.
+    "timer.js",
+    "worker.js",
+  ]
+
+  tsconfig_base = "../../tsconfig_base.json"
+  composite = true
+
+  # Allows TSC to check the generated mojom-webui/ directory when it encounters
+  # imports.
+  path_mappings =
+      [ "/*|" + rebase_path("$root_gen_dir/mojom-webui/*", target_gen_dir) ]
+
+  deps = [ "../../mojom:mojom_ts" ]
+}
+
+build_mwc_app("component_playground") {
+  host = "sample-system-web-app"
+  input = rebase_path(".", root_build_dir)
+  js_module_in_files = [ "component_playground.js" ]
+  js_out_files = [ "component_playground.rollup.js" ]
+  out_manifest = "$target_gen_dir/component_playground.manifest"
+  deps = []
+}
diff --git a/ash/webui/sample_system_web_app_ui/resources/app_icon_192.png b/ash/webui/sample_system_web_app_ui/resources/trusted/app_icon_192.png
similarity index 100%
rename from ash/webui/sample_system_web_app_ui/resources/app_icon_192.png
rename to ash/webui/sample_system_web_app_ui/resources/trusted/app_icon_192.png
Binary files differ
diff --git a/ash/webui/sample_system_web_app_ui/resources/component_playground.html b/ash/webui/sample_system_web_app_ui/resources/trusted/component_playground.html
similarity index 100%
rename from ash/webui/sample_system_web_app_ui/resources/component_playground.html
rename to ash/webui/sample_system_web_app_ui/resources/trusted/component_playground.html
diff --git a/ash/webui/sample_system_web_app_ui/resources/component_playground.js b/ash/webui/sample_system_web_app_ui/resources/trusted/component_playground.js
similarity index 100%
rename from ash/webui/sample_system_web_app_ui/resources/component_playground.js
rename to ash/webui/sample_system_web_app_ui/resources/trusted/component_playground.js
diff --git a/ash/webui/sample_system_web_app_ui/resources/index.html b/ash/webui/sample_system_web_app_ui/resources/trusted/index.html
similarity index 100%
rename from ash/webui/sample_system_web_app_ui/resources/index.html
rename to ash/webui/sample_system_web_app_ui/resources/trusted/index.html
diff --git a/ash/webui/sample_system_web_app_ui/resources/inter_frame_communication.html b/ash/webui/sample_system_web_app_ui/resources/trusted/inter_frame_communication.html
similarity index 100%
rename from ash/webui/sample_system_web_app_ui/resources/inter_frame_communication.html
rename to ash/webui/sample_system_web_app_ui/resources/trusted/inter_frame_communication.html
diff --git a/ash/webui/sample_system_web_app_ui/resources/trusted/inter_frame_communication.ts b/ash/webui/sample_system_web_app_ui/resources/trusted/inter_frame_communication.ts
new file mode 100644
index 0000000..fb5634aa
--- /dev/null
+++ b/ash/webui/sample_system_web_app_ui/resources/trusted/inter_frame_communication.ts
@@ -0,0 +1,66 @@
+// 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.
+
+// This file demonstrates how a chrome:// page can communicate with its
+// embedded chrome-untrusted:// child page.
+
+import {ChildUntrustedPageRemote, ParentTrustedPage, ParentTrustedPagePendingReceiver, ParentTrustedPageReceiver} from '/ash/webui/sample_system_web_app_ui/mojom/sample_system_web_app_shared_ui.mojom-webui.js';
+
+import {callbackRouter} from './page_handler.js';
+
+/**
+ * Implements ParentTrustedPage interface to handle requests from the child
+ * page.
+ *
+ * Note: If you expect to have multiple listeners for your interface, consider
+ * using a CallbackRouter instead. CallbackRouter provides a more event-like
+ * API that makes it easier to have multiple listeners.
+ */
+class ParentTrustedPageImpl implements ParentTrustedPage {
+  private receiver_ = new ParentTrustedPageReceiver(this);
+  constructor(pendingReceiver: ParentTrustedPagePendingReceiver) {
+    this.receiver_.$.bindHandle(pendingReceiver.handle);
+  }
+
+  async doSomethingForChild(task: string) {
+    document.querySelector<HTMLDivElement>('#child-task')!.innerText = task;
+
+    // Mojo interface's JS implementation should return an Object, even if the
+    // method only has one return value.
+    //
+    // Each field should match their return value name defined in .mojom file.
+    return {resp: 'Task done'};
+  }
+}
+
+// A promise that resolves when child page is ready. Other modules wishing to
+// use childPage need to wait for the promise.
+interface ChildPageReadyResult {
+  childPage: ChildUntrustedPageRemote;
+  parentPageReceiver: ParentTrustedPage;
+}
+export const childPageReady = new Promise<ChildPageReadyResult>(resolve => {
+  callbackRouter.createParentPage.addListener(
+      (childPageRemote: ChildUntrustedPageRemote,
+       parentPagePendingReceiver: ParentTrustedPagePendingReceiver) => {
+        resolve({
+          childPage: childPageRemote,
+          parentPageReceiver:
+              new ParentTrustedPageImpl(parentPagePendingReceiver)
+        });
+      });
+});
+
+// Expose for testing.
+declare global {
+  interface Window {
+    childPageReady: Promise<{childPage: ChildUntrustedPageRemote}>;
+  }
+}
+
+window.childPageReady = childPageReady;
+
+childPageReady.then((result: ChildPageReadyResult) => {
+  result.childPage.doSomethingForParent('Hello from chrome://');
+});
diff --git a/ash/webui/sample_system_web_app_ui/resources/trusted/main.ts b/ash/webui/sample_system_web_app_ui/resources/trusted/main.ts
new file mode 100644
index 0000000..d3c2b1a
--- /dev/null
+++ b/ash/webui/sample_system_web_app_ui/resources/trusted/main.ts
@@ -0,0 +1,75 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {PageCallbackRouter, PageHandlerRemote} from '/ash/webui/sample_system_web_app_ui/mojom/sample_system_web_app_ui.mojom-webui.js';
+
+import {callbackRouter, pageHandler} from './page_handler.js';
+
+const first = document.querySelector<HTMLInputElement>('#number1')!;
+const second = document.querySelector<HTMLInputElement>('#number2')!;
+const additional = document.querySelector<HTMLInputElement>('#additional')!;
+
+const result = document.querySelector('#result')!;
+
+// TODO(crbug.com/1002798): Replace this with real type definitions.
+declare const trustedTypes: any;
+
+declare global {
+  interface Window {
+    pageHandler: PageHandlerRemote;
+    callbackRouter: PageCallbackRouter;
+    eventCount: Map<string, number>;
+  }
+}
+
+const workerUrlPolicy = trustedTypes.createPolicy(
+    'worker-js-static',
+    {createScriptURL: () => 'chrome://sample-system-web-app/worker.js'});
+const myWorker = new SharedWorker(workerUrlPolicy.createScriptURL(''));
+
+first.onchange = () => {
+  myWorker.port.postMessage([first.value, second.value]);
+};
+
+second.onchange = () => {
+  myWorker.port.postMessage([first.value, second.value]);
+};
+
+myWorker.port.onmessage = (event: any) => {
+  result.textContent = event.data[0];
+  additional.value = event.data[1];
+};
+
+
+// Exposes the pageHandler to the user as a window's global variable for
+// testing.
+window.pageHandler = pageHandler;
+window.callbackRouter = callbackRouter;
+window.eventCount = new Map();
+
+// Example of adding an event listener for `OnEventOccurred`.
+callbackRouter.onEventOccurred.addListener((name: string) => {
+  document.querySelector<HTMLInputElement>('#mojo-event')!.value = name;
+  window.eventCount.set(name, 1 + (window.eventCount.get(name) || 0));
+});
+
+// Example of sending information to the browser process.
+pageHandler.send(`message at ${Date.now()}`);
+
+// Example of getting information from the browser process.
+(async () => {
+  // Mojo results get wrapped in a "response" object that contains
+  // a member for each of the Mojo callback's argument, in this case
+  // a `preferences` member.
+  const {preferences} = await pageHandler.getPreferences();
+  document.querySelector<HTMLInputElement>('#background')!.value =
+      preferences.background;
+  document.querySelector<HTMLInputElement>('#foreground')!.value =
+      preferences.foreground;
+})();
+
+const mojoButton = document.querySelector<HTMLButtonElement>('#do-something')!;
+mojoButton.onclick = () => {
+  pageHandler.doSomething();
+};
diff --git a/ash/webui/sample_system_web_app_ui/resources/page_handler.js b/ash/webui/sample_system_web_app_ui/resources/trusted/page_handler.ts
similarity index 99%
rename from ash/webui/sample_system_web_app_ui/resources/page_handler.js
rename to ash/webui/sample_system_web_app_ui/resources/trusted/page_handler.ts
index b8a17ac..6cbf802 100644
--- a/ash/webui/sample_system_web_app_ui/resources/page_handler.js
+++ b/ash/webui/sample_system_web_app_ui/resources/trusted/page_handler.ts
@@ -49,4 +49,4 @@
 const factoryRemote = PageHandlerFactory.getRemote();
 factoryRemote.createPageHandler(
     pageHandler.$.bindNewPipeAndPassReceiver(),
-    callbackRouter.$.bindNewPipeAndPassRemote());
\ No newline at end of file
+    callbackRouter.$.bindNewPipeAndPassRemote());
diff --git a/ash/webui/sample_system_web_app_ui/resources/timer.html b/ash/webui/sample_system_web_app_ui/resources/trusted/timer.html
similarity index 100%
rename from ash/webui/sample_system_web_app_ui/resources/timer.html
rename to ash/webui/sample_system_web_app_ui/resources/trusted/timer.html
diff --git a/ash/webui/sample_system_web_app_ui/resources/timer.js b/ash/webui/sample_system_web_app_ui/resources/trusted/timer.js
similarity index 100%
rename from ash/webui/sample_system_web_app_ui/resources/timer.js
rename to ash/webui/sample_system_web_app_ui/resources/trusted/timer.js
diff --git a/ash/webui/sample_system_web_app_ui/resources/worker.js b/ash/webui/sample_system_web_app_ui/resources/trusted/worker.js
similarity index 100%
rename from ash/webui/sample_system_web_app_ui/resources/worker.js
rename to ash/webui/sample_system_web_app_ui/resources/trusted/worker.js
diff --git a/ash/webui/sample_system_web_app_ui/resources/untrusted/BUILD.gn b/ash/webui/sample_system_web_app_ui/resources/untrusted/BUILD.gn
new file mode 100644
index 0000000..3a0df76
--- /dev/null
+++ b/ash/webui/sample_system_web_app_ui/resources/untrusted/BUILD.gn
@@ -0,0 +1,59 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+import("//tools/typescript/ts_library.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+assert(is_chromeos_ash, "Sample System Web App is ash-chrome only")
+assert(!is_official_build,
+       "Sample System Web App is only built for unofficial builds")
+
+generate_grd("untrusted_grd") {
+  grd_prefix = "ash_sample_system_web_app_untrusted"
+  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
+
+  input_files = [ "untrusted.html" ]
+  input_files_base_dir = rebase_path(".", "//")
+
+  # tsconfig.manifest is generated by :untrusted_ts and adds all of the
+  # in_files to the grd.
+  manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
+
+  # Flatten out the dependency tree of your mojom and add generated bindings
+  # grdp files here.
+  # TODO(crbug.com/1002798): We could add a mojo_grdp_deps variable to
+  # generate_grd that automatically adds these grdp files when you add a
+  # X_webui_grdp target to it.
+  grdp_files = [
+    "$target_gen_dir/../../mojom/shared_webui_resources.grdp",
+    "$target_gen_dir/../../mojom/untrusted_webui_resources.grdp",
+  ]
+
+  deps = [
+    ":untrusted_ts",
+    "../../mojom:shared_webui_grdp",
+    "../../mojom:untrusted_webui_grdp",
+  ]
+}
+
+ts_library("untrusted_ts") {
+  root_dir = "."
+  out_dir = "$target_gen_dir/tsc"
+
+  in_files = [
+    "untrusted.ts",
+    "untrusted_page_interface.ts",
+  ]
+
+  composite = true
+  tsconfig_base = "../../tsconfig_base.json"
+
+  # Allows TSC to check the generated mojom-webui/ directory when it encounters
+  # imports.
+  path_mappings =
+      [ "/*|" + rebase_path("$root_gen_dir/mojom-webui/*", target_gen_dir) ]
+
+  deps = [ "../../mojom:mojom_ts" ]
+}
diff --git a/ash/webui/sample_system_web_app_ui/resources/untrusted.html b/ash/webui/sample_system_web_app_ui/resources/untrusted/untrusted.html
similarity index 100%
rename from ash/webui/sample_system_web_app_ui/resources/untrusted.html
rename to ash/webui/sample_system_web_app_ui/resources/untrusted/untrusted.html
diff --git a/ash/webui/sample_system_web_app_ui/resources/untrusted.js b/ash/webui/sample_system_web_app_ui/resources/untrusted/untrusted.ts
similarity index 79%
rename from ash/webui/sample_system_web_app_ui/resources/untrusted.js
rename to ash/webui/sample_system_web_app_ui/resources/untrusted/untrusted.ts
index 7ac193c..5f8bb06b 100644
--- a/ash/webui/sample_system_web_app_ui/resources/untrusted.js
+++ b/ash/webui/sample_system_web_app_ui/resources/untrusted/untrusted.ts
@@ -1,9 +1,9 @@
 // Copyright 2020 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-import {PARENT_PAGE_ORIGIN, parentPage} from '/untrusted_page_interface.js';
+import {PARENT_PAGE_ORIGIN, parentPage} from './untrusted_page_interface.js';
 
-const header = document.querySelector('#untrusted-title');
+const header = document.querySelector<HTMLTitleElement>('#untrusted-title')!;
 header.textContent = 'Untrusted Sample System Web App';
 
 // For testing purposes: notify the parent window the iframe has been embedded
@@ -30,5 +30,6 @@
 
 // Ask the parent page to do something, and retrieve the result.
 parentPage.doSomethingForChild('Say hello').then(result => {
-  document.querySelector('#parent-resp').innerText = result.resp;
-});
\ No newline at end of file
+  document.querySelector<HTMLParagraphElement>('#parent-resp')!.innerText =
+      result.resp;
+});
diff --git a/ash/webui/sample_system_web_app_ui/resources/untrusted_page_interface.js b/ash/webui/sample_system_web_app_ui/resources/untrusted/untrusted_page_interface.ts
similarity index 89%
rename from ash/webui/sample_system_web_app_ui/resources/untrusted_page_interface.js
rename to ash/webui/sample_system_web_app_ui/resources/untrusted/untrusted_page_interface.ts
index 1944a13..a9099ce 100644
--- a/ash/webui/sample_system_web_app_ui/resources/untrusted_page_interface.js
+++ b/ash/webui/sample_system_web_app_ui/resources/untrusted/untrusted_page_interface.ts
@@ -18,17 +18,16 @@
  * @implements {ash.mojom.sample_swa.ChildUntrustedPage}
  */
 class ChildUntrustedPageImpl {
-  constructor() {
-    this.receiver_ = new ChildUntrustedPageReceiver(this);
-  }
+  private receiver_ = new ChildUntrustedPageReceiver(this);
 
   // Returns a Mojo remote that should be send to the parent page to be bound.
   bindNewPipeAndPassRemote() {
     return this.receiver_.$.bindNewPipeAndPassRemote();
   }
 
-  doSomethingForParent(task) {
-    document.querySelector('#parent-task').innerText = task;
+  doSomethingForParent(task: string) {
+    document.querySelector<HTMLParagraphElement>('#parent-task')!.innerText =
+        task;
 
     // For testing, report the received task.
     window.parent.postMessage(
diff --git a/ash/webui/sample_system_web_app_ui/tsconfig_base.json b/ash/webui/sample_system_web_app_ui/tsconfig_base.json
new file mode 100644
index 0000000..33f7126
--- /dev/null
+++ b/ash/webui/sample_system_web_app_ui/tsconfig_base.json
@@ -0,0 +1,6 @@
+{
+  "extends": "../../../tools/typescript/tsconfig_base.json",
+  "compilerOptions": {
+    "allowJs": true
+  }
+}
diff --git a/ash/wm/desks/desks_restore_util.cc b/ash/wm/desks/desks_restore_util.cc
index 43db2ce..30af319 100644
--- a/ash/wm/desks/desks_restore_util.cc
+++ b/ash/wm/desks/desks_restore_util.cc
@@ -143,9 +143,9 @@
   if (primary_user_prefs->GetBoolean(kUserHasUsedDesksRecently))
     UMA_HISTOGRAM_BOOLEAN("Ash.Desks.UserHasUsedDesksRecently", true);
 
-  const base::ListValue* desks_names =
+  const base::Value* desks_names =
       primary_user_prefs->GetList(prefs::kDesksNamesList);
-  const base::ListValue* desks_metrics =
+  const base::Value* desks_metrics =
       primary_user_prefs->GetList(prefs::kDesksMetricsList);
 
   // First create the same number of desks.
diff --git a/ash/wm/desks/desks_textfield.cc b/ash/wm/desks/desks_textfield.cc
index 21e7601..090c99e 100644
--- a/ash/wm/desks/desks_textfield.cc
+++ b/ash/wm/desks/desks_textfield.cc
@@ -69,6 +69,9 @@
 
 void DesksTextfield::UpdateViewAppearance() {
   background()->SetNativeControlColor(GetBackgroundColor());
+  // Paint the whole view to update the background. The `SchedulePaint` in
+  // `UpdateFocusRingState` will only repaint the focus ring.
+  SchedulePaint();
   UpdateFocusRingState();
 }
 
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 3bd5bcf..704d0ca4 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -1985,7 +1985,7 @@
 // given list of |desks_names|.
 void VerifyDesksRestoreData(PrefService* user_prefs,
                             const std::vector<std::string>& desks_names) {
-  const base::ListValue* desks_restore_names =
+  const base::Value* desks_restore_names =
       user_prefs->GetList(prefs::kDesksNamesList);
   ASSERT_EQ(desks_names.size(), desks_restore_names->GetList().size());
 
diff --git a/ash/wm/desks/templates/desks_templates_animations.cc b/ash/wm/desks/templates/desks_templates_animations.cc
index 80b9926..31955450 100644
--- a/ash/wm/desks/templates/desks_templates_animations.cc
+++ b/ash/wm/desks/templates/desks_templates_animations.cc
@@ -32,10 +32,17 @@
 }
 
 // Fade out animation using AnimationBuilder.
-void FadeOutLayer(ui::Layer* layer, int duration_in_ms) {
+void FadeOutLayer(ui::Layer* layer,
+                  base::OnceClosure on_animation_ended_callback,
+                  int duration_in_ms) {
+  std::pair<base::OnceClosure, base::OnceClosure> split =
+      base::SplitOnceCallback(std::move(on_animation_ended_callback));
+
   views::AnimationBuilder()
       .SetPreemptionStrategy(
           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
+      .OnEnded(std::move(split.first))
+      .OnAborted(std::move(split.second))
       .Once()
       .SetDuration(base::TimeDelta())
       .SetOpacity(layer, 1.0f)
@@ -50,8 +57,10 @@
   FadeInLayer(layer, kFadeInDurationMs);
 }
 
-void PerformFadeOutLayer(ui::Layer* layer) {
-  FadeOutLayer(layer, kFadeOutDurationMs);
+void PerformFadeOutLayer(ui::Layer* layer,
+                         base::OnceClosure on_animation_ended_callback) {
+  FadeOutLayer(layer, std::move(on_animation_ended_callback),
+               kFadeOutDurationMs);
 }
 
 }  // namespace ash
diff --git a/ash/wm/desks/templates/desks_templates_animations.h b/ash/wm/desks/templates/desks_templates_animations.h
index efa7675..4f543d3 100644
--- a/ash/wm/desks/templates/desks_templates_animations.h
+++ b/ash/wm/desks/templates/desks_templates_animations.h
@@ -5,6 +5,9 @@
 #ifndef ASH_WM_DESKS_TEMPLATES_DESKS_TEMPLATES_ANIMATIONS_H_
 #define ASH_WM_DESKS_TEMPLATES_DESKS_TEMPLATES_ANIMATIONS_H_
 
+#include "base/callback_forward.h"
+#include "base/callback_helpers.h"
+
 namespace ui {
 class Layer;
 }  // namespace ui
@@ -16,7 +19,9 @@
 void PerformFadeInLayer(ui::Layer* layer);
 
 // Animates linear fade out of overview items.
-void PerformFadeOutLayer(ui::Layer* layer);
+void PerformFadeOutLayer(
+    ui::Layer* layer,
+    base::OnceClosure on_animation_ended_callback = base::DoNothing());
 
 }  // namespace ash
 
diff --git a/ash/wm/desks/templates/desks_templates_item_view.cc b/ash/wm/desks/templates/desks_templates_item_view.cc
index d367d35..b38aa51 100644
--- a/ash/wm/desks/templates/desks_templates_item_view.cc
+++ b/ash/wm/desks/templates/desks_templates_item_view.cc
@@ -272,6 +272,20 @@
   if (observed_view == this)
     return;
 
+  // If we exit overview while the `name_view_` is still focused, the shutdown
+  // sequence will reset the presenter before `OnViewBlurred` gets called. This
+  // checks and makes sure that we don't call the presenter while trying to
+  // shutdown the overview session.
+  // `overview_session` may also be null as `OnViewBlurred` may be called after
+  // the owning widget is no longer owned by the session for overview exit
+  // animation. See https://crbug.com/1281422.
+  // TODO(richui): Revisit this once the behavior of the template name when
+  // exiting overview is determined.
+  OverviewSession* overview_session =
+      Shell::Get()->overview_controller()->overview_session();
+  if (!overview_session || overview_session->is_shutting_down())
+    return;
+
   DCHECK_EQ(observed_view, name_view_);
   is_template_name_being_modified_ = false;
   defer_select_all_ = false;
@@ -297,16 +311,6 @@
   updated_template->set_template_name(name_view_->GetText());
   OnTemplateNameChanged(updated_template->template_name());
 
-  // If we exit overview while the `name_view_` is still focused, the shutdown
-  // sequence will reset the presenter before `OnViewBlurred` gets called. This
-  // checks and makes sure that we don't call the presenter while trying to
-  // shutdown the overview session.
-  OverviewSession* overview_session =
-      Shell::Get()->overview_controller()->overview_session();
-  DCHECK(overview_session);
-  if (overview_session->is_shutting_down())
-    return;
-
   DesksTemplatesPresenter::Get()->SaveOrUpdateDeskTemplate(
       /*is_update=*/false, std::move(updated_template));
 }
diff --git a/ash/wm/desks/templates/desks_templates_presenter.cc b/ash/wm/desks/templates/desks_templates_presenter.cc
index 89b1a92..daffefeb 100644
--- a/ash/wm/desks/templates/desks_templates_presenter.cc
+++ b/ash/wm/desks/templates/desks_templates_presenter.cc
@@ -97,6 +97,14 @@
       Shell::Get()->tablet_mode_controller()->InTabletMode();
   should_show_templates_ui_ = !in_tablet_mode && GetEntryCount() > 0u;
   for (auto& overview_grid : overview_session_->grid_list()) {
+    if (!should_show_templates_ui_ &&
+        overview_grid->IsShowingDesksTemplatesGrid()) {
+      // When deleting, it is possible to delete the last template. In this
+      // case, close the template grid and go back to overview.
+      overview_grid->HideDesksTemplatesGrid(/*exit_overview=*/false);
+      continue;
+    }
+
     if (DesksBarView* desks_bar_view =
             const_cast<DesksBarView*>(overview_grid->desks_bar_view())) {
       // When templates is enabled but templates haven't loaded, the templates
@@ -105,18 +113,7 @@
       desks_bar_view->UpdateDesksTemplatesButtonVisibility();
       desks_bar_view->UpdateButtonsForDesksTemplatesGrid();
       desks_bar_view->Layout();
-    }
-
-    overview_grid->UpdateSaveDeskAsTemplateButton();
-
-    if (!overview_grid->IsShowingDesksTemplatesGrid())
-      continue;
-
-    if (!should_show_templates_ui_) {
-      // When deleting, it is possible to delete the last template. In this
-      // case, close the template grid and go back to overview.
-      overview_grid->HideDesksTemplatesGrid(/*exit_overview=*/false);
-      continue;
+      overview_grid->UpdateSaveDeskAsTemplateButton();
     }
   }
 }
diff --git a/ash/wm/desks/templates/desks_templates_unittest.cc b/ash/wm/desks/templates/desks_templates_unittest.cc
index d2dc5d82..6ac73cc 100644
--- a/ash/wm/desks/templates/desks_templates_unittest.cc
+++ b/ash/wm/desks/templates/desks_templates_unittest.cc
@@ -53,6 +53,7 @@
 #include "ui/aura/window.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
 #include "ui/compositor/layer.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/textfield/textfield.h"
@@ -312,6 +313,12 @@
     OverviewTestBase::SetUp();
   }
 
+ protected:
+  // Tests should normally create a local `ScopedAnimationDurationScaleMode`.
+  // Create this object if a non zero scale mode needs to be used during test
+  // tear down.
+  std::unique_ptr<ui::ScopedAnimationDurationScaleMode> animation_scale_;
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -1816,6 +1823,36 @@
   SendKey(ui::VKEY_TAB);
 }
 
+// Tests that there is no crash when leaving the template name view focused with
+// a changed name during shutdown. Regression test for
+// https://crbug.com/1281422.
+TEST_F(DesksTemplatesTest, EditTemplateNameShutdownNoCrash) {
+  // The fade out animation of the desks templates grid must be enabled for this
+  // crash to have happened.
+  animation_scale_ = std::make_unique<ui::ScopedAnimationDurationScaleMode>(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+
+  AddEntry(base::GUID::GenerateRandomV4(), "a", base::Time::Now());
+  AddEntry(base::GUID::GenerateRandomV4(), "b", base::Time::Now());
+
+  OpenOverviewAndShowTemplatesGrid();
+  DesksTemplatesNameView* name_view =
+      GetItemViewFromTemplatesGrid(0)->name_view();
+
+  // Tab until we focus the name view of the first template item.
+  SendKey(ui::VKEY_TAB);
+  SendKey(ui::VKEY_TAB);
+  ASSERT_EQ(name_view, GetHighlightedView());
+
+  // Rename template "a" to template "ddd".
+  SendKey(ui::VKEY_RETURN);
+  SendKey(ui::VKEY_D);
+  SendKey(ui::VKEY_D);
+  SendKey(ui::VKEY_D);
+
+  // Verify that there is no crash while the test tears down.
+}
+
 // Tests that the hovering over the templates name shows the expected cursor.
 TEST_F(DesksTemplatesTest, TemplatesNameHitTest) {
   auto* cursor_manager = Shell::Get()->cursor_manager();
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index cabfece..6810c4c 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -351,20 +351,6 @@
   return item->animating_to_close() || ignored_items.contains(item);
 }
 
-// Apply the property that makes windows visible in the desk mini view,
-// recursively. Windows that have been updated are added to the vector.
-void SetForceVisibleInMiniView(
-    aura::Window* window,
-    std::vector<aura::Window*>* out_updated_windows) {
-  if (!window->GetProperty(kForceVisibleInMiniViewKey)) {
-    window->SetProperty(kForceVisibleInMiniViewKey, true);
-    out_updated_windows->push_back(window);
-  }
-
-  for (aura::Window* child : window->children())
-    SetForceVisibleInMiniView(child, out_updated_windows);
-}
-
 }  // namespace
 
 // The class to observe the overview window that the dragged tabs will merge
@@ -1714,24 +1700,6 @@
         std::make_unique<DesksTemplatesGridView>());
   }
 
-  // Before showing the grid, we need to hide the overview items. However, we
-  // don't want to affect the desk preview. To do this, we temporarily set a
-  // window property that will keep them visible.
-  if (DeskMiniView* desk_mini_view = desks_bar_view_->FindMiniViewForDesk(
-          DesksController::Get()->GetTargetActiveDesk())) {
-    std::vector<aura::Window*> updated_windows;
-    SetForceVisibleInMiniView(desk_mini_view->GetDeskContainer(),
-                              &updated_windows);
-
-    // This makes the desk preview pick up on the window property change.
-    desk_mini_view->desk_preview()->RecreateDeskContentsMirrorLayers();
-
-    // Remove the property that was temporarily applied.
-    for (aura::Window* window : updated_windows)
-      window->ClearProperty(kForceVisibleInMiniViewKey);
-  }
-
-  // We can now hide the overview mode items.
   for (auto& overview_mode_item : window_list_)
     overview_mode_item->HideForDesksTemplatesGrid();
 
@@ -1752,31 +1720,44 @@
 }
 
 void OverviewGrid::HideDesksTemplatesGrid(bool exit_overview) {
-  // Un-hide the overview mode items.
-  for (auto& overview_mode_item : window_list_)
-    overview_mode_item->RevertHideForDesksTemplatesGrid();
+  if (!desks_templates_grid_widget_)
+    return;
 
-  if (exit_overview && overview_session_->enter_exit_overview_type() !=
+  auto* grid_layer = desks_templates_grid_widget_->GetLayer();
+  const bool already_hiding_grid = grid_layer->GetAnimator()->is_animating() &&
+                                   grid_layer->GetTargetOpacity() == 0.f;
+  if (already_hiding_grid)
+    return;
+
+  if (exit_overview && overview_session_->enter_exit_overview_type() ==
                            OverviewEnterExitType::kImmediateExit) {
+    // Since we're immediately exiting, we don't need to animate anything and
+    // can let the `desks_templates_grid_widget_` handle its own destruction.
+    return;
+  }
+
+  if (exit_overview) {
+    // Un-hide the overview mode items.
+    for (auto& overview_mode_item : window_list_)
+      overview_mode_item->RevertHideForDesksTemplatesGrid();
+
     // Disable the `desks_templates_grid_widget_`'s event targeting so it can't
     // get any events during the animation.
     desks_templates_grid_widget_->GetNativeWindow()->SetEventTargetingPolicy(
         aura::EventTargetingPolicy::kNone);
+
     FadeOutWidgetFromOverview(
         std::move(desks_templates_grid_widget_),
         OVERVIEW_ANIMATION_EXIT_OVERVIEW_MODE_DESKS_TEMPLATES_GRID_FADE_OUT);
     return;
   }
 
-  desks_templates_grid_widget_->Hide();
-
-  // Activate the overview focus window to match the behavior of entering
-  // overview mode in the beginning. Otherwise there are cases where some
-  // overview windows are not able to be focused and activated.
-  wm::ActivateWindow(overview_session_->GetOverviewFocusWindow());
-
-  desks_bar_view_->UpdateButtonsForDesksTemplatesGrid();
-  desks_bar_view_->OnDesksTemplatesGridHidden();
+  // Fade out the `desks_templates_grid_widget_` and then when its animation is
+  // done fade in the supporting widgets and revert the overview item hides.
+  PerformFadeOutLayer(
+      desks_templates_grid_widget_->GetLayer(),
+      base::BindOnce(&OverviewGrid::OnDesksTemplatesGridFadedOut,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 bool OverviewGrid::IsShowingDesksTemplatesGrid() const {
@@ -1852,8 +1833,12 @@
       !IsShowingDesksTemplatesGrid();
 
   if (!visible) {
-    if (save_desk_as_template_widget_)
-      save_desk_as_template_widget_->Hide();
+    if (save_desk_as_template_widget_) {
+      PerformFadeOutLayer(
+          save_desk_as_template_widget_->GetLayer(),
+          base::BindOnce(&OverviewGrid::OnSaveDeskAsTemplateButtonFadedOut,
+                         weak_ptr_factory_.GetWeakPtr()));
+    }
     return;
   }
 
@@ -1867,6 +1852,7 @@
         PillButton::Type::kIcon, &kSaveDeskAsTemplateIcon));
   }
   save_desk_as_template_widget_->Show();
+  PerformFadeInLayer(save_desk_as_template_widget_->GetLayer());
 
   // Disable the create templates button if the current number of templates has
   // reached the max or the current desk has only unsupported apps.
@@ -2334,6 +2320,26 @@
   DesksTemplatesPresenter::Get()->MaybeSaveActiveDeskAsTemplate();
 }
 
+void OverviewGrid::OnDesksTemplatesGridFadedOut() {
+  for (auto& overview_mode_item : window_list_)
+    overview_mode_item->RevertHideForDesksTemplatesGrid();
+
+  desks_templates_grid_widget_->Hide();
+
+  // Activate the overview focus window to match the behavior of entering
+  // overview mode in the beginning. Otherwise there are cases where some
+  // overview windows are not able to be focused and activated.
+  wm::ActivateWindow(overview_session_->GetOverviewFocusWindow());
+
+  desks_bar_view_->UpdateButtonsForDesksTemplatesGrid();
+  desks_bar_view_->OnDesksTemplatesGridHidden();
+  UpdateSaveDeskAsTemplateButton();
+}
+
+void OverviewGrid::OnSaveDeskAsTemplateButtonFadedOut() {
+  save_desk_as_template_widget_->Hide();
+}
+
 int OverviewGrid::GetDesksBarHeight() const {
   const bool should_show_zero_state_desks_bar =
       desks_bar_view_ ? desks_bar_view_->IsZeroState()
diff --git a/ash/wm/overview/overview_grid.h b/ash/wm/overview/overview_grid.h
index 9a4887c7..f85d611f 100644
--- a/ash/wm/overview/overview_grid.h
+++ b/ash/wm/overview/overview_grid.h
@@ -501,6 +501,14 @@
   // Called back when the button to save a desk template is pressed.
   void OnSaveDeskAsTemplateButtonPressed();
 
+  // Called when the animation for fading the `desks_templates_grid_widget_` out
+  // is completed.
+  void OnDesksTemplatesGridFadedOut();
+
+  // Called when the animation for fading the `save_desk_as_template_widget_`
+  // out is completed.
+  void OnSaveDeskAsTemplateButtonFadedOut();
+
   // Returns the height of `desks_bar_view_`.
   int GetDesksBarHeight() const;
 
@@ -589,6 +597,8 @@
   // A widget that contains a button which creates a new desk template when
   // pressed.
   std::unique_ptr<views::Widget> save_desk_as_template_widget_;
+
+  base::WeakPtrFactory<OverviewGrid> weak_ptr_factory_{this};
 };
 
 }  // namespace ash
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index 89b6bcb..b08bc1c 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -210,11 +210,16 @@
 }
 
 void OverviewItem::HideForDesksTemplatesGrid() {
+  // To hide the window, we will set its layer opacity to 0. This would normally
+  // also hide the window from the mini view, which we don't want. By setting a
+  // property on the window, we can force it to stay visible.
+  GetWindow()->SetProperty(kForceVisibleInMiniViewKey, true);
+
   DCHECK(item_widget_);
   item_widget_->GetLayer()->SetOpacity(0.0f);
 
-  for (aura::Window* transient_child :
-       GetTransientTreeIterator(transform_window_.window())) {
+  for (aura::Window* transient_child : GetTransientTreeIterator(GetWindow())) {
+    transient_child->SetProperty(kForceVisibleInMiniViewKey, true);
     transient_child->layer()->SetOpacity(0.0f);
   }
 
@@ -254,6 +259,11 @@
     MaximizeIfSnapped(GetWindow());
   }
 
+  GetWindow()->ClearProperty(kForceVisibleInMiniViewKey);
+  for (aura::Window* transient_child : GetTransientTreeIterator(GetWindow())) {
+    transient_child->ClearProperty(kForceVisibleInMiniViewKey);
+  }
+
   overview_item_view_->OnOverviewItemWindowRestoring();
   transform_window_.RestoreWindow(
       reset_transform,
diff --git a/ash/wm/window_restore/window_restore_controller_unittest.cc b/ash/wm/window_restore/window_restore_controller_unittest.cc
index a28c465..66d8405a 100644
--- a/ash/wm/window_restore/window_restore_controller_unittest.cc
+++ b/ash/wm/window_restore/window_restore_controller_unittest.cc
@@ -294,12 +294,6 @@
         ++restore_window_id;
       window->SetProperty(app_restore::kRestoreWindowIdKey, restore_window_id);
     }
-
-    // WindowRestoreController relies on getting OnWindowInitialized events from
-    // aura::Env via full_restore::FullRestoreInfo. That object does not exist
-    // for ash unit tests, so we will observe aura::Env ourselves and forward
-    // the event to WindowRestoreController.
-    WindowRestoreController::Get()->OnWindowInitialized(window);
   }
 
  private:
@@ -322,23 +316,6 @@
                    fake_window_restore_file_[restore_window_id].info.get());
   }
 
-  // Callback function that is run when WindowRestoreController tries to read
-  // window data from the file. Immediately reads from our fake file
-  // `fake_window_restore_file_`.
-  std::unique_ptr<app_restore::WindowInfo> OnGetWindowInfo(
-      aura::Window* window) {
-    DCHECK(window);
-    const int32_t restore_window_id =
-        window->GetProperty(app_restore::kRestoreWindowIdKey);
-    if (!fake_window_restore_file_.contains(restore_window_id))
-      return nullptr;
-
-    auto window_info = std::make_unique<app_restore::WindowInfo>();
-    CopyWindowInfo(*fake_window_restore_file_[restore_window_id].info,
-                   window_info.get());
-    return window_info;
-  }
-
   // Copies the info from `src` to `out_dst` since `fullrestore::WindowInfo`
   // copy constructor is deleted.
   void CopyWindowInfo(const app_restore::WindowInfo& src,
diff --git a/base/BUILD.gn b/base/BUILD.gn
index b9c5b65..197f27f2 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -665,6 +665,7 @@
     "task/default_delayed_task_handle_delegate.h",
     "task/deferred_sequenced_task_runner.cc",
     "task/deferred_sequenced_task_runner.h",
+    "task/delay_policy.h",
     "task/delayed_task_handle.cc",
     "task/delayed_task_handle.h",
     "task/lazy_thread_pool_task_runner.cc",
@@ -3252,6 +3253,7 @@
     "test/metrics/histogram_tester_unittest.cc",
     "test/metrics/user_action_tester_unittest.cc",
     "test/mock_callback_unittest.cc",
+    "test/repeating_test_future_unittest.cc",
     "test/scoped_feature_list_unittest.cc",
     "test/scoped_logging_settings.h",
     "test/scoped_mock_clock_override_unittest.cc",
diff --git a/base/allocator/partition_alloc_support_unittest.cc b/base/allocator/partition_alloc_support_unittest.cc
index 5315ed14..9ab42a5 100644
--- a/base/allocator/partition_alloc_support_unittest.cc
+++ b/base/allocator/partition_alloc_support_unittest.cc
@@ -32,25 +32,33 @@
     pcscan_enabled = false;
 #endif
 
-    std::string brp_expectation = "Unavailable";
+    std::string brp_expectation;
+    std::string pcscan_expectation;
+
+    {
+      test::ScopedFeatureList brp_scope;
+      brp_scope.InitWithFeatures({}, {features::kPartitionAllocBackupRefPtr});
+
+      brp_expectation = "Unavailable";
 #if BUILDFLAG(USE_BACKUP_REF_PTR)
-    brp_expectation = pcscan_enabled ? "Ignore_PCScanIsOn" : "Ignore_NoGroup";
+      brp_expectation = pcscan_enabled ? "Ignore_PCScanIsOn" : "Ignore_NoGroup";
 #endif
-    std::string pcscan_expectation = "Unavailable";
+      pcscan_expectation = "Unavailable";
 #if defined(PA_ALLOW_PCSCAN)
-    pcscan_expectation = pcscan_enabled ? "Enabled" : "Disabled";
+      pcscan_expectation = pcscan_enabled ? "Enabled" : "Disabled";
 #endif
 
-    auto trials = ProposeSyntheticFinchTrials(false);
-    auto group_iter = trials.find("BackupRefPtr_Effective");
-    EXPECT_NE(group_iter, trials.end());
-    EXPECT_EQ(group_iter->second, brp_expectation);
-    group_iter = trials.find("PCScan_Effective");
-    EXPECT_NE(group_iter, trials.end());
-    EXPECT_EQ(group_iter->second, pcscan_expectation);
-    group_iter = trials.find("PCScan_Effective_Fallback");
-    EXPECT_NE(group_iter, trials.end());
-    EXPECT_EQ(group_iter->second, pcscan_expectation);
+      auto trials = ProposeSyntheticFinchTrials(false);
+      auto group_iter = trials.find("BackupRefPtr_Effective");
+      EXPECT_NE(group_iter, trials.end());
+      EXPECT_EQ(group_iter->second, brp_expectation);
+      group_iter = trials.find("PCScan_Effective");
+      EXPECT_NE(group_iter, trials.end());
+      EXPECT_EQ(group_iter->second, pcscan_expectation);
+      group_iter = trials.find("PCScan_Effective_Fallback");
+      EXPECT_NE(group_iter, trials.end());
+      EXPECT_EQ(group_iter->second, pcscan_expectation);
+    }
 
     {
       test::ScopedFeatureList brp_scope;
@@ -75,8 +83,8 @@
 #endif  // BUILDFLAG(USE_BACKUP_REF_PTR)
 #endif  // defined(PA_ALLOW_PCSCAN)
 
-      trials = ProposeSyntheticFinchTrials(false);
-      group_iter = trials.find("BackupRefPtr_Effective");
+      auto trials = ProposeSyntheticFinchTrials(false);
+      auto group_iter = trials.find("BackupRefPtr_Effective");
       EXPECT_NE(group_iter, trials.end());
       EXPECT_EQ(group_iter->second, brp_expectation);
       group_iter = trials.find("PCScan_Effective");
@@ -136,8 +144,8 @@
                                     : (pcscan_enabled ? "Enabled" : "Disabled");
 #endif  // defined(PA_ALLOW_PCSCAN)
 
-        trials = ProposeSyntheticFinchTrials(false);
-        group_iter = trials.find("BackupRefPtr_Effective");
+        auto trials = ProposeSyntheticFinchTrials(false);
+        auto group_iter = trials.find("BackupRefPtr_Effective");
         EXPECT_NE(group_iter, trials.end());
         EXPECT_EQ(group_iter->second, brp_expectation);
         group_iter = trials.find("PCScan_Effective");
diff --git a/base/json/json_reader_unittest.cc b/base/json/json_reader_unittest.cc
index 9dbe3b6..085a0a8 100644
--- a/base/json/json_reader_unittest.cc
+++ b/base/json/json_reader_unittest.cc
@@ -64,6 +64,18 @@
   root = JSONReader::Read("true // comment");
   ASSERT_TRUE(root);
   EXPECT_TRUE(root->is_bool());
+  // Comments in different contexts.
+  root = JSONReader::Read("{   \"cheese\": 3\n\n   // Here's a comment\n}");
+  ASSERT_TRUE(root);
+  EXPECT_TRUE(root->is_dict());
+  root = JSONReader::Read("{   \"cheese\": 3// Here's a comment\n}");
+  ASSERT_TRUE(root);
+  EXPECT_TRUE(root->is_dict());
+  // Multiple comment markers.
+  root = JSONReader::Read(
+      "{   \"cheese\": 3// Here's a comment // and another\n}");
+  ASSERT_TRUE(root);
+  EXPECT_TRUE(root->is_dict());
   root = JSONReader::Read("/* comment */\"sample string\"");
   ASSERT_TRUE(root);
   EXPECT_TRUE(root->is_string());
@@ -225,6 +237,7 @@
   EXPECT_FALSE(JSONReader::Read("4.3.1"));
   EXPECT_FALSE(JSONReader::Read("4e3.1"));
   EXPECT_FALSE(JSONReader::Read("4.a"));
+  EXPECT_FALSE(JSONReader::Read("42a"));
 }
 
 TEST(JSONReaderTest, SimpleString) {
@@ -302,17 +315,26 @@
   EXPECT_TRUE(list->GetList().empty());
 }
 
-TEST(JSONReaderTest, NestedArrays) {
-  absl::optional<Value> list =
-      JSONReader::Read("[[true], [], [false, [], [null]], null]");
+TEST(JSONReaderTest, CompleteArray) {
+  absl::optional<Value> list = JSONReader::Read("[\"a\", 3, 4.56, null]");
   ASSERT_TRUE(list);
   ASSERT_TRUE(list->is_list());
   EXPECT_EQ(4U, list->GetList().size());
+}
+
+TEST(JSONReaderTest, NestedArrays) {
+  absl::optional<Value> list = JSONReader::Read(
+      "[[true], [], {\"smell\": \"nice\",\"taste\": \"yummy\" }, [false, [], "
+      "[null]], null]");
+  ASSERT_TRUE(list);
+  ASSERT_TRUE(list->is_list());
+  EXPECT_EQ(5U, list->GetList().size());
 
   // Lots of trailing commas.
-  absl::optional<Value> root2 =
-      JSONReader::Read("[[true], [], [false, [], [null, ]  , ], null,]",
-                       JSON_ALLOW_TRAILING_COMMAS);
+  absl::optional<Value> root2 = JSONReader::Read(
+      "[[true], [], {\"smell\": \"nice\",\"taste\": \"yummy\" }, [false, [], "
+      "[null, ]  , ], null,]",
+      JSON_ALLOW_TRAILING_COMMAS);
   ASSERT_TRUE(root2);
   EXPECT_EQ(*list, *root2);
 }
@@ -361,7 +383,8 @@
 
 TEST(JSONReaderTest, CompleteDictionary) {
   absl::optional<Value> dict_val = JSONReader::Read(
-      "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\" }");
+      "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\", \"bool\": "
+      "false, \"more\": {} }");
   ASSERT_TRUE(dict_val);
   ASSERT_TRUE(dict_val->is_dict());
   auto double_val = dict_val->FindDoubleKey("number");
@@ -374,9 +397,13 @@
   const std::string* str_val = dict_val->FindStringKey("S");
   ASSERT_TRUE(str_val);
   EXPECT_EQ("str", *str_val);
+  auto bool_val = dict_val->FindBoolKey("bool");
+  ASSERT_TRUE(bool_val);
+  ASSERT_FALSE(*bool_val);
 
   absl::optional<Value> root2 = JSONReader::Read(
-      "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\", }",
+      "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\", \"bool\": "
+      "false, \"more\": {},}",
       JSON_PARSE_CHROMIUM_EXTENSIONS | JSON_ALLOW_TRAILING_COMMAS);
   ASSERT_TRUE(root2);
   ASSERT_TRUE(root2->is_dict());
@@ -388,6 +415,8 @@
       "  \"number\":9.87654321,\n"
       "  \"null\":null,\n"
       "  \"\\x53\":\"str\",\n"
+      "  \"bool\": false,\n"
+      "  \"more\": {},\n"
       "}\n",
       JSON_PARSE_CHROMIUM_EXTENSIONS | JSON_ALLOW_TRAILING_COMMAS);
   ASSERT_TRUE(root2);
@@ -399,6 +428,8 @@
       "  \"number\":9.87654321,\r\n"
       "  \"null\":null,\r\n"
       "  \"\\x53\":\"str\",\r\n"
+      "  \"bool\": false,\r\n"
+      "  \"more\": {},\r\n"
       "}\r\n",
       JSON_PARSE_CHROMIUM_EXTENSIONS | JSON_ALLOW_TRAILING_COMMAS);
   ASSERT_TRUE(root2);
@@ -408,14 +439,14 @@
 
 TEST(JSONReaderTest, NestedDictionaries) {
   absl::optional<Value> dict_val = JSONReader::Read(
-      "{\"inner\":{\"array\":[true]},\"false\":false,\"d\":{}}");
+      "{\"inner\":{\"array\":[true, 3, 4.56, null]},\"false\":false,\"d\":{}}");
   ASSERT_TRUE(dict_val);
   ASSERT_TRUE(dict_val->is_dict());
   const Value* inner_dict = dict_val->FindDictKey("inner");
   ASSERT_TRUE(inner_dict);
   const Value* inner_array = inner_dict->FindListKey("array");
   ASSERT_TRUE(inner_array);
-  EXPECT_EQ(1U, inner_array->GetList().size());
+  EXPECT_EQ(4U, inner_array->GetList().size());
   auto bool_value = dict_val->FindBoolKey("false");
   ASSERT_TRUE(bool_value);
   EXPECT_FALSE(*bool_value);
@@ -423,7 +454,8 @@
   EXPECT_TRUE(inner_dict);
 
   absl::optional<Value> root2 = JSONReader::Read(
-      "{\"inner\": {\"array\":[true] , },\"false\":false,\"d\":{},}",
+      "{\"inner\": {\"array\":[true, 3, 4.56, null] , "
+      "},\"false\":false,\"d\":{},}",
       JSON_ALLOW_TRAILING_COMMAS);
   ASSERT_TRUE(root2);
   EXPECT_EQ(*dict_val, *root2);
@@ -478,6 +510,7 @@
   EXPECT_FALSE(JSONReader::Read("{foo:true}"));
   EXPECT_FALSE(JSONReader::Read("{1234: false}"));
   EXPECT_FALSE(JSONReader::Read("{:false}"));
+  EXPECT_FALSE(JSONReader::Read("{ , }"));
 
   // Trailing comma.
   EXPECT_FALSE(JSONReader::Read("{\"a\":true,}"));
diff --git a/base/sequence_checker.h b/base/sequence_checker.h
index 50a548a..3471db83 100644
--- a/base/sequence_checker.h
+++ b/base/sequence_checker.h
@@ -6,9 +6,9 @@
 #define BASE_SEQUENCE_CHECKER_H_
 
 #include "base/check.h"
+#include "base/dcheck_is_on.h"
 #include "base/sequence_checker_impl.h"
 #include "base/strings/string_piece.h"
-#include "build/build_config.h"
 
 #if DCHECK_IS_ON()
 #include "base/debug/stack_trace.h"
@@ -138,16 +138,6 @@
                      : "");
   }
 
-  explicit ScopedValidateSequenceChecker(const SequenceChecker& checker,
-                                         const StringPiece& msg)
-      EXCLUSIVE_LOCK_FUNCTION(checker) {
-    std::unique_ptr<debug::StackTrace> bound_at;
-    DCHECK(checker.CalledOnValidSequence(&bound_at))
-        << msg
-        << (bound_at ? "\nWas attached to sequence at:\n" + bound_at->ToString()
-                     : "");
-  }
-
   ~ScopedValidateSequenceChecker() UNLOCK_FUNCTION() {}
 
  private:
diff --git a/base/task/delay_policy.h b/base/task/delay_policy.h
new file mode 100644
index 0000000..0803506
--- /dev/null
+++ b/base/task/delay_policy.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TASK_DELAY_POLICY_H_
+#define BASE_TASK_DELAY_POLICY_H_
+
+namespace base {
+namespace subtle {
+
+// Policies affecting how a delayed task is scheduled when a TimeTicks is
+// specified.
+enum class DelayPolicy {
+  // A delayed task with kFlexibleNoSooner may not run any sooner than the
+  // specified time, but might run slightly after. This is the behavior implied
+  // by PostDelayedTask.
+  kFlexibleNoSooner,
+  // A delayed task with kFlexiblePreferEarly means the task should attempt to
+  // run near the deadline and preferably a little bit before than after if the
+  // scheduler applies slack.
+  kFlexiblePreferEarly,
+  // A delayed task with kPrecise means it may not run any sooner than the
+  // specified time and preferably as close as possible to the specified time,
+  // which may affect scheduling policies if the scheduler usually applies
+  // slack.
+  kPrecise,
+};
+
+}  // namespace subtle
+}  // namespace base
+
+#endif  // BASE_TASK_SEQUENCED_TASK_RUNNER_H_
diff --git a/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
index d52f7575..b21d92b 100644
--- a/base/task/sequence_manager/sequence_manager_impl_unittest.cc
+++ b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
@@ -830,6 +830,29 @@
   EXPECT_FALSE(queue->HasTaskToRunImmediatelyOrReadyDelayedTask());
 }
 
+TEST_P(SequenceManagerTest, DelayedTaskAtPosting) {
+  auto queue = CreateTaskQueue();
+
+  std::vector<EnqueueOrder> run_order;
+  constexpr TimeDelta kDelay(Milliseconds(10));
+  auto handle = queue->task_runner()->PostCancelableDelayedTaskAt(
+      subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE,
+      BindOnce(&TestTask, 1, &run_order),
+      sequence_manager()->NowTicks() + kDelay);
+  EXPECT_EQ(kDelay, NextPendingTaskDelay());
+  EXPECT_FALSE(queue->HasTaskToRunImmediatelyOrReadyDelayedTask());
+  EXPECT_TRUE(run_order.empty());
+
+  // The task doesn't run before the delay has completed.
+  FastForwardBy(kDelay - Milliseconds(1));
+  EXPECT_TRUE(run_order.empty());
+
+  // After the delay has completed, the task runs normally.
+  FastForwardBy(Milliseconds(1));
+  EXPECT_THAT(run_order, ElementsAre(1u));
+  EXPECT_FALSE(queue->HasTaskToRunImmediatelyOrReadyDelayedTask());
+}
+
 TEST(SequenceManagerTestWithMockTaskRunner,
      DelayedTaskExecutedInOneMessageLoopTask) {
   FixtureWithMockTaskRunner fixture;
@@ -871,6 +894,38 @@
   EXPECT_THAT(run_order, ElementsAre(3u, 2u, 1u));
 }
 
+TEST_P(SequenceManagerTest,
+       DelayedTaskAtPosting_MultipleTasks_DescendingOrder) {
+  auto queue = CreateTaskQueue();
+
+  std::vector<EnqueueOrder> run_order;
+  auto handle1 = queue->task_runner()->PostCancelableDelayedTaskAt(
+      subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE,
+      BindOnce(&TestTask, 1, &run_order),
+      sequence_manager()->NowTicks() + Milliseconds(10));
+
+  queue->task_runner()->PostDelayedTask(
+      FROM_HERE, BindOnce(&TestTask, 2, &run_order), Milliseconds(8));
+
+  auto handle2 = queue->task_runner()->PostCancelableDelayedTaskAt(
+      subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE,
+      BindOnce(&TestTask, 3, &run_order),
+      sequence_manager()->NowTicks() + Milliseconds(5));
+
+  EXPECT_EQ(Milliseconds(5), NextPendingTaskDelay());
+
+  FastForwardBy(Milliseconds(5));
+  EXPECT_THAT(run_order, ElementsAre(3u));
+  EXPECT_EQ(Milliseconds(3), NextPendingTaskDelay());
+
+  FastForwardBy(Milliseconds(3));
+  EXPECT_THAT(run_order, ElementsAre(3u, 2u));
+  EXPECT_EQ(Milliseconds(2), NextPendingTaskDelay());
+
+  FastForwardBy(Milliseconds(2));
+  EXPECT_THAT(run_order, ElementsAre(3u, 2u, 1u));
+}
+
 TEST_P(SequenceManagerTest, DelayedTaskPosting_MultipleTasks_AscendingOrder) {
   auto queue = CreateTaskQueue();
 
@@ -898,6 +953,37 @@
   EXPECT_THAT(run_order, ElementsAre(1u, 2u, 3u));
 }
 
+TEST_P(SequenceManagerTest, DelayedTaskAtPosting_MultipleTasks_AscendingOrder) {
+  auto queue = CreateTaskQueue();
+
+  std::vector<EnqueueOrder> run_order;
+  auto handle1 = queue->task_runner()->PostCancelableDelayedTaskAt(
+      subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE,
+      BindOnce(&TestTask, 1, &run_order),
+      sequence_manager()->NowTicks() + Milliseconds(1));
+
+  queue->task_runner()->PostDelayedTask(
+      FROM_HERE, BindOnce(&TestTask, 2, &run_order), Milliseconds(5));
+
+  auto handle2 = queue->task_runner()->PostCancelableDelayedTaskAt(
+      subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE,
+      BindOnce(&TestTask, 3, &run_order),
+      sequence_manager()->NowTicks() + Milliseconds(10));
+
+  EXPECT_EQ(Milliseconds(1), NextPendingTaskDelay());
+
+  FastForwardBy(Milliseconds(1));
+  EXPECT_THAT(run_order, ElementsAre(1u));
+  EXPECT_EQ(Milliseconds(4), NextPendingTaskDelay());
+
+  FastForwardBy(Milliseconds(4));
+  EXPECT_THAT(run_order, ElementsAre(1u, 2u));
+  EXPECT_EQ(Milliseconds(5), NextPendingTaskDelay());
+
+  FastForwardBy(Milliseconds(5));
+  EXPECT_THAT(run_order, ElementsAre(1u, 2u, 3u));
+}
+
 TEST(SequenceManagerTestWithMockTaskRunner,
      PostDelayedTask_SharesUnderlyingDelayedTasks) {
   FixtureWithMockTaskRunner fixture;
diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc
index cec117f..788bad1 100644
--- a/base/task/sequence_manager/task_queue_impl.cc
+++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -16,6 +16,7 @@
 #include "base/ranges/algorithm.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/common/scoped_defer_task_posting.h"
+#include "base/task/default_delayed_task_handle_delegate.h"
 #include "base/task/sequence_manager/delayed_task_handle_delegate.h"
 #include "base/task/sequence_manager/fence.h"
 #include "base/task/sequence_manager/sequence_manager_impl.h"
@@ -113,16 +114,39 @@
                                            task_type_));
 }
 
+bool TaskQueueImpl::TaskRunner::PostDelayedTaskAt(
+    subtle::PostDelayedTaskPassKey,
+    const Location& location,
+    OnceClosure callback,
+    TimeTicks delayed_run_time,
+    base::subtle::DelayPolicy delay_policy) {
+  return task_poster_->PostTask(PostedTask(this, std::move(callback), location,
+                                           delayed_run_time, delay_policy,
+                                           Nestable::kNestable, task_type_));
+}
+
+DelayedTaskHandle TaskQueueImpl::TaskRunner::PostCancelableDelayedTaskAt(
+    subtle::PostDelayedTaskPassKey pass_key,
+    const Location& location,
+    OnceClosure callback,
+    TimeTicks delayed_run_time,
+    base::subtle::DelayPolicy delay_policy) {
+  if (!IsRemoveCanceledTasksInTaskQueueFeatureEnabled()) {
+    // This will revert to PostDelayedTaskAt() with default DelayedTaskHandle.
+    return SequencedTaskRunner::PostCancelableDelayedTaskAt(
+        pass_key, location, std::move(callback), delayed_run_time,
+        delay_policy);
+  }
+  return task_poster_->PostCancelableTask(
+      PostedTask(this, std::move(callback), location, delayed_run_time,
+                 delay_policy, Nestable::kNestable, task_type_));
+}
+
 DelayedTaskHandle TaskQueueImpl::TaskRunner::PostCancelableDelayedTask(
     const Location& location,
     OnceClosure callback,
     TimeDelta delay) {
-  if (!remove_canceled_tasks_in_task_queue_.has_value()) {
-    remove_canceled_tasks_in_task_queue_ =
-        FeatureList::IsEnabled(kRemoveCanceledTasksInTaskQueue);
-  }
-
-  if (!remove_canceled_tasks_in_task_queue_.value()) {
+  if (!IsRemoveCanceledTasksInTaskQueueFeatureEnabled()) {
     return SequencedTaskRunner::PostCancelableDelayedTask(
         location, std::move(callback), delay);
   }
@@ -145,6 +169,15 @@
   return associated_thread_->IsBoundToCurrentThread();
 }
 
+bool TaskQueueImpl::TaskRunner::
+    IsRemoveCanceledTasksInTaskQueueFeatureEnabled() {
+  if (!remove_canceled_tasks_in_task_queue_.has_value()) {
+    remove_canceled_tasks_in_task_queue_ =
+        FeatureList::IsEnabled(kRemoveCanceledTasksInTaskQueue);
+  }
+  return remove_canceled_tasks_in_task_queue_.value();
+}
+
 TaskQueueImpl::TaskQueueImpl(SequenceManagerImpl* sequence_manager,
                              WakeUpQueue* wake_up_queue,
                              const TaskQueue::Spec& spec)
diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h
index d1d3b675..c6d1e24 100644
--- a/base/task/sequence_manager/task_queue_impl.h
+++ b/base/task/sequence_manager/task_queue_impl.h
@@ -322,6 +322,17 @@
     bool PostDelayedTask(const Location& location,
                          OnceClosure callback,
                          TimeDelta delay) final;
+    bool PostDelayedTaskAt(subtle::PostDelayedTaskPassKey,
+                           const Location& location,
+                           OnceClosure callback,
+                           TimeTicks delayed_run_time,
+                           base::subtle::DelayPolicy delay_policy) final;
+    DelayedTaskHandle PostCancelableDelayedTaskAt(
+        subtle::PostDelayedTaskPassKey,
+        const Location& location,
+        OnceClosure callback,
+        TimeTicks delayed_run_time,
+        base::subtle::DelayPolicy delay_policy) final;
     DelayedTaskHandle PostCancelableDelayedTask(const Location& location,
                                                 OnceClosure callback,
                                                 TimeDelta delay) final;
@@ -333,6 +344,8 @@
    private:
     ~TaskRunner() final;
 
+    bool IsRemoveCanceledTasksInTaskQueueFeatureEnabled();
+
     const scoped_refptr<GuardedTaskPoster> task_poster_;
     const scoped_refptr<AssociatedThreadId> associated_thread_;
     const TaskType task_type_;
diff --git a/base/task/sequence_manager/tasks.cc b/base/task/sequence_manager/tasks.cc
index 25a3152..dca3946 100644
--- a/base/task/sequence_manager/tasks.cc
+++ b/base/task/sequence_manager/tasks.cc
@@ -113,6 +113,7 @@
     OnceClosure callback,
     Location location,
     TimeTicks delayed_run_time,
+    subtle::DelayPolicy delay_policy,
     Nestable nestable,
     TaskType task_type,
     WeakPtr<DelayedTaskHandleDelegate> delayed_task_handle_delegate)
@@ -121,6 +122,7 @@
       nestable(nestable),
       task_type(task_type),
       delay_or_delayed_run_time(delayed_run_time),
+      delay_policy(delay_policy),
       task_runner(std::move(task_runner)),
       delayed_task_handle_delegate(std::move(delayed_task_handle_delegate)) {}
 
diff --git a/base/task/sequence_manager/tasks.h b/base/task/sequence_manager/tasks.h
index 247aa0c..3c23e14 100644
--- a/base/task/sequence_manager/tasks.h
+++ b/base/task/sequence_manager/tasks.h
@@ -7,9 +7,11 @@
 
 #include "base/containers/intrusive_heap.h"
 #include "base/pending_task.h"
+#include "base/task/delay_policy.h"
 #include "base/task/sequence_manager/delayed_task_handle_delegate.h"
 #include "base/task/sequence_manager/enqueue_order.h"
 #include "base/task/sequenced_task_runner.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
 namespace base {
@@ -37,6 +39,7 @@
                       OnceClosure callback,
                       Location location,
                       TimeTicks delayed_run_time,
+                      subtle::DelayPolicy delay_policy,
                       Nestable nestable = Nestable::kNestable,
                       TaskType task_type = kTaskTypeNone,
                       WeakPtr<DelayedTaskHandleDelegate>
@@ -56,6 +59,7 @@
   Nestable nestable = Nestable::kNestable;
   TaskType task_type = kTaskTypeNone;
   absl::variant<TimeDelta, TimeTicks> delay_or_delayed_run_time;
+  absl::optional<subtle::DelayPolicy> delay_policy;
   // The task runner this task is running on. Can be used by task runners that
   // support posting back to the "current sequence".
   scoped_refptr<SequencedTaskRunner> task_runner;
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
index cd0521a..c16eadd 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
@@ -151,9 +151,11 @@
                TimeTicks delayed_run_time) {
     DCHECK(tasks_.empty() || delayed_run_time.is_null() ||
            tasks_.back().delayed_run_time < delayed_run_time);
-    tasks_.push(Task(internal::PostedTask(nullptr, std::move(task), posted_from,
-                                          delayed_run_time),
-                     EnqueueOrder::FromIntForTesting(13)));
+    tasks_.push(
+        Task(internal::PostedTask(nullptr, std::move(task), posted_from,
+                                  delayed_run_time,
+                                  base::subtle::DelayPolicy::kFlexibleNoSooner),
+             EnqueueOrder::FromIntForTesting(13)));
   }
 
   bool HasPendingHighResolutionTasks() override {
diff --git a/base/task/sequence_manager/work_queue_sets_unittest.cc b/base/task/sequence_manager/work_queue_sets_unittest.cc
index 8883c1e1..08209d0 100644
--- a/base/task/sequence_manager/work_queue_sets_unittest.cc
+++ b/base/task/sequence_manager/work_queue_sets_unittest.cc
@@ -75,7 +75,8 @@
 
   Task FakeTaskWithTaskOrder(TaskOrder task_order) {
     Task fake_task(PostedTask(nullptr, BindOnce([] {}), FROM_HERE,
-                              task_order.delayed_run_time()),
+                              task_order.delayed_run_time(),
+                              subtle::DelayPolicy::kFlexibleNoSooner),
                    EnqueueOrder::FromIntForTesting(task_order.sequence_num()),
                    task_order.enqueue_order());
     return fake_task;
diff --git a/base/task/sequence_manager/work_queue_unittest.cc b/base/task/sequence_manager/work_queue_unittest.cc
index c501424..46a99ca8 100644
--- a/base/task/sequence_manager/work_queue_unittest.cc
+++ b/base/task/sequence_manager/work_queue_unittest.cc
@@ -94,7 +94,8 @@
 
   Task FakeTaskWithTaskOrder(TaskOrder task_order) {
     Task fake_task(PostedTask(nullptr, BindOnce(&NopTask), FROM_HERE,
-                              task_order.delayed_run_time()),
+                              task_order.delayed_run_time(),
+                              subtle::DelayPolicy::kFlexibleNoSooner),
                    EnqueueOrder::FromIntForTesting(task_order.sequence_num()),
                    task_order.enqueue_order());
     return fake_task;
diff --git a/base/task/sequenced_task_runner.cc b/base/task/sequenced_task_runner.cc
index 2132f05..307c2a9 100644
--- a/base/task/sequenced_task_runner.cc
+++ b/base/task/sequenced_task_runner.cc
@@ -37,6 +37,37 @@
   return delayed_task_handle;
 }
 
+DelayedTaskHandle SequencedTaskRunner::PostCancelableDelayedTaskAt(
+    subtle::PostDelayedTaskPassKey pass_key,
+    const Location& from_here,
+    OnceClosure task,
+    TimeTicks delayed_run_time,
+    subtle::DelayPolicy deadline_policy) {
+  auto delayed_task_handle_delegate =
+      std::make_unique<DefaultDelayedTaskHandleDelegate>();
+
+  task = delayed_task_handle_delegate->BindCallback(std::move(task));
+
+  DelayedTaskHandle delayed_task_handle(
+      std::move(delayed_task_handle_delegate));
+
+  if (!PostDelayedTaskAt(pass_key, from_here, std::move(task), delayed_run_time,
+                         deadline_policy)) {
+    DCHECK(!delayed_task_handle.IsValid());
+  }
+  return delayed_task_handle;
+}
+
+bool SequencedTaskRunner::PostDelayedTaskAt(
+    subtle::PostDelayedTaskPassKey,
+    const Location& from_here,
+    OnceClosure task,
+    TimeTicks delayed_run_time,
+    subtle::DelayPolicy deadline_policy) {
+  return PostDelayedTask(from_here, std::move(task),
+                         delayed_run_time - TimeTicks::Now());
+}
+
 bool SequencedTaskRunner::DeleteOrReleaseSoonInternal(
     const Location& from_here,
     void (*deleter)(const void*),
diff --git a/base/task/sequenced_task_runner.h b/base/task/sequenced_task_runner.h
index 0725c522..0230fd6 100644
--- a/base/task/sequenced_task_runner.h
+++ b/base/task/sequenced_task_runner.h
@@ -9,12 +9,31 @@
 
 #include "base/base_export.h"
 #include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/task/delay_policy.h"
 #include "base/task/delayed_task_handle.h"
 #include "base/task/sequenced_task_runner_helpers.h"
 #include "base/task/task_runner.h"
+#include "base/types/pass_key.h"
 
 namespace base {
 
+namespace subtle {
+
+// Used to restrict access to PostCancelableDelayedTaskAt() to authorize
+// callers.
+class PostDelayedTaskPassKey {
+ private:
+  // Avoid =default to disallow creation by uniform initialization.
+  PostDelayedTaskPassKey() {}
+
+  friend class PostDelayedTaskPassKeyForTesting;
+};
+
+class PostDelayedTaskPassKeyForTesting : public PostDelayedTaskPassKey {};
+
+}  // namespace subtle
+
 // A SequencedTaskRunner is a subclass of TaskRunner that provides
 // additional guarantees on the order that tasks are started, as well
 // as guarantees on when tasks are in sequence, i.e. one task finishes
@@ -132,6 +151,18 @@
                                                       OnceClosure task,
                                                       TimeDelta delay);
 
+  // Posts the given |task| to be run at |delayed_run_time|, following
+  // |delay_policy|. Returns a handle that can be used to cancel the task.
+  // This should not be used directly, consider using higher level base::Timer
+  // primitives.
+  WARN_UNUSED_RESULT virtual DelayedTaskHandle PostCancelableDelayedTaskAt(
+      subtle::PostDelayedTaskPassKey,
+      const Location& from_here,
+      OnceClosure task,
+      TimeTicks delayed_run_time,
+      subtle::DelayPolicy delay_policy =
+          subtle::DelayPolicy::kFlexibleNoSooner);
+
   // Submits a non-nestable task to delete the given object.  Returns
   // true if the object may be deleted at some point in the future,
   // and false if the object definitely will not be deleted.
@@ -184,6 +215,20 @@
  protected:
   ~SequencedTaskRunner() override = default;
 
+  // Posts the given |task| to be run at |delayed_run_time|, following
+  // |delay_policy|. This is used by the default implementation of
+  // PostCancelableDelayedTaskAt(). The default behavior subtracts
+  // TimeTicks::Now() from |delayed_run_time| to get a delay. See base::Timer to
+  // post precise/repeating timeouts.
+  // TODO(1153139): Make pure virtual once all SequencedTaskRunners implement
+  // this.
+  virtual bool PostDelayedTaskAt(subtle::PostDelayedTaskPassKey,
+                                 const Location& from_here,
+                                 OnceClosure task,
+                                 TimeTicks delayed_run_time,
+                                 subtle::DelayPolicy delay_policy =
+                                     subtle::DelayPolicy::kFlexibleNoSooner);
+
  private:
   bool DeleteOrReleaseSoonInternal(const Location& from_here,
                                    void (*deleter)(const void*),
diff --git a/base/task/thread_pool/pooled_sequenced_task_runner.cc b/base/task/thread_pool/pooled_sequenced_task_runner.cc
index 7a5b5cf8..04f60e7 100644
--- a/base/task/thread_pool/pooled_sequenced_task_runner.cc
+++ b/base/task/thread_pool/pooled_sequenced_task_runner.cc
@@ -5,6 +5,7 @@
 #include "base/task/thread_pool/pooled_sequenced_task_runner.h"
 
 #include "base/sequence_token.h"
+#include "base/task/default_delayed_task_handle_delegate.h"
 
 namespace base {
 namespace internal {
@@ -35,6 +36,24 @@
                                                             sequence_);
 }
 
+bool PooledSequencedTaskRunner::PostDelayedTaskAt(
+    subtle::PostDelayedTaskPassKey,
+    const Location& from_here,
+    OnceClosure closure,
+    TimeTicks delayed_run_time,
+    subtle::DelayPolicy delay_policy) {
+  if (!PooledTaskRunnerDelegate::MatchesCurrentDelegate(
+          pooled_task_runner_delegate_)) {
+    return false;
+  }
+
+  Task task(from_here, std::move(closure), TimeTicks::Now(), delayed_run_time);
+
+  // Post the task as part of |sequence_|.
+  return pooled_task_runner_delegate_->PostTaskWithSequence(std::move(task),
+                                                            sequence_);
+}
+
 bool PooledSequencedTaskRunner::PostNonNestableDelayedTask(
     const Location& from_here,
     OnceClosure closure,
diff --git a/base/task/thread_pool/pooled_sequenced_task_runner.h b/base/task/thread_pool/pooled_sequenced_task_runner.h
index 3656549..4031431 100644
--- a/base/task/thread_pool/pooled_sequenced_task_runner.h
+++ b/base/task/thread_pool/pooled_sequenced_task_runner.h
@@ -35,6 +35,12 @@
                        OnceClosure closure,
                        TimeDelta delay) override;
 
+  bool PostDelayedTaskAt(subtle::PostDelayedTaskPassKey,
+                         const Location& from_here,
+                         OnceClosure closure,
+                         TimeTicks delayed_run_time,
+                         subtle::DelayPolicy delay_policy) override;
+
   bool PostNonNestableDelayedTask(const Location& from_here,
                                   OnceClosure closure,
                                   TimeDelta delay) override;
diff --git a/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc b/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc
index e9b882e..a6568ea 100644
--- a/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc
+++ b/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc
@@ -16,6 +16,7 @@
 #include "base/ranges/algorithm.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/atomic_flag.h"
+#include "base/task/default_delayed_task_handle_delegate.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool/delayed_task_manager.h"
@@ -412,23 +413,20 @@
       return false;
 
     Task task(from_here, std::move(closure), TimeTicks::Now(), delay);
+    return PostTask(std::move(task));
+  }
 
-    if (!outer_->task_tracker_->WillPostTask(&task,
-                                             sequence_->shutdown_behavior())) {
+  bool PostDelayedTaskAt(subtle::PostDelayedTaskPassKey,
+                         const Location& from_here,
+                         OnceClosure closure,
+                         TimeTicks delayed_run_time,
+                         subtle::DelayPolicy delay_policy) override {
+    if (!g_manager_is_alive)
       return false;
-    }
 
-    if (task.delayed_run_time.is_null())
-      return GetDelegate()->PostTaskNow(sequence_, std::move(task));
-
-    // Unretained(GetDelegate()) is safe because this TaskRunner and its
-    // worker are kept alive as long as there are pending Tasks.
-    outer_->delayed_task_manager_->AddDelayedTask(
-        std::move(task),
-        BindOnce(IgnoreResult(&WorkerThreadDelegate::PostTaskNow),
-                 Unretained(GetDelegate()), sequence_),
-        this);
-    return true;
+    Task task(from_here, std::move(closure), TimeTicks::Now(),
+              delayed_run_time);
+    return PostTask(std::move(task));
   }
 
   bool PostNonNestableDelayedTask(const Location& from_here,
@@ -470,6 +468,25 @@
     }
   }
 
+  bool PostTask(Task task) {
+    if (!outer_->task_tracker_->WillPostTask(&task,
+                                             sequence_->shutdown_behavior())) {
+      return false;
+    }
+
+    if (task.delayed_run_time.is_null())
+      return GetDelegate()->PostTaskNow(sequence_, std::move(task));
+
+    // Unretained(GetDelegate()) is safe because this TaskRunner and its
+    // worker are kept alive as long as there are pending Tasks.
+    outer_->delayed_task_manager_->AddDelayedTask(
+        std::move(task),
+        BindOnce(IgnoreResult(&WorkerThreadDelegate::PostTaskNow),
+                 Unretained(GetDelegate()), sequence_),
+        this);
+    return true;
+  }
+
   WorkerThreadDelegate* GetDelegate() const {
     return static_cast<WorkerThreadDelegate*>(worker_->delegate());
   }
diff --git a/base/task/thread_pool/task.cc b/base/task/thread_pool/task.cc
index 763aa7f0..516a87e 100644
--- a/base/task/thread_pool/task.cc
+++ b/base/task/thread_pool/task.cc
@@ -21,10 +21,16 @@
            OnceClosure task,
            TimeTicks queue_time,
            TimeDelta delay)
-    : PendingTask(posted_from,
-                  std::move(task),
-                  queue_time,
-                  delay.is_zero() ? TimeTicks() : queue_time + delay) {
+    : Task(posted_from,
+           std::move(task),
+           queue_time,
+           delay.is_zero() ? TimeTicks() : queue_time + delay) {}
+
+Task::Task(const Location& posted_from,
+           OnceClosure task,
+           TimeTicks queue_time,
+           TimeTicks delayed_run_time)
+    : PendingTask(posted_from, std::move(task), queue_time, delayed_run_time) {
   // ThreadPoolImpl doesn't use |sequence_num| but tracing (toplevel.flow)
   // relies on it being unique. While this subtle dependency is a bit
   // overreaching, ThreadPoolImpl is the only task system that doesn't use
diff --git a/base/task/thread_pool/task.h b/base/task/thread_pool/task.h
index c4780e9e..99f9614f 100644
--- a/base/task/thread_pool/task.h
+++ b/base/task/thread_pool/task.h
@@ -29,6 +29,11 @@
        OnceClosure task,
        TimeTicks queue_time,
        TimeDelta delay);
+  // |delayed_run_time| is the time when the task should be run.
+  Task(const Location& posted_from,
+       OnceClosure task,
+       TimeTicks queue_time,
+       TimeTicks delayed_run_time);
 
   // Task is move-only to avoid mistakes that cause reference counts to be
   // accidentally bumped.
diff --git a/base/task/thread_pool/thread_pool_impl_unittest.cc b/base/task/thread_pool/thread_pool_impl_unittest.cc
index d1f14f3..8037464 100644
--- a/base/task/thread_pool/thread_pool_impl_unittest.cc
+++ b/base/task/thread_pool/thread_pool_impl_unittest.cc
@@ -406,6 +406,50 @@
   task_ran.Wait();
 }
 
+namespace {
+
+scoped_refptr<SequencedTaskRunner> CreateSequencedTaskRunnerAndExecutionMode(
+    ThreadPoolImpl* thread_pool,
+    const TaskTraits& traits,
+    TaskSourceExecutionMode execution_mode,
+    SingleThreadTaskRunnerThreadMode default_single_thread_task_runner_mode =
+        SingleThreadTaskRunnerThreadMode::SHARED) {
+  switch (execution_mode) {
+    case TaskSourceExecutionMode::kSequenced:
+      return thread_pool->CreateSequencedTaskRunner(traits);
+    case TaskSourceExecutionMode::kSingleThread: {
+      return thread_pool->CreateSingleThreadTaskRunner(
+          traits, default_single_thread_task_runner_mode);
+    }
+    case TaskSourceExecutionMode::kParallel:
+    case TaskSourceExecutionMode::kJob:
+      ADD_FAILURE() << "Tests below don't cover these modes";
+      return nullptr;
+  }
+  ADD_FAILURE() << "Unknown ExecutionMode";
+  return nullptr;
+}
+
+}  // namespace
+
+TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
+       PostDelayedTaskAtViaTaskRunner) {
+  StartThreadPool();
+  TestWaitableEvent task_ran;
+  // Only runs for kSequenced and kSingleThread.
+  auto handle =
+      CreateSequencedTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
+                                                GetExecutionMode())
+          ->PostCancelableDelayedTaskAt(
+              subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE,
+              BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetTraits(),
+                       GetGroupTypes(),
+                       TimeTicks::Now() + TestTimeouts::tiny_timeout(),
+                       Unretained(&task_ran)),
+              TimeTicks::Now() + TestTimeouts::tiny_timeout());
+  task_ran.Wait();
+}
+
 // Verifies that Tasks posted via a TaskRunner with parameterized TaskTraits and
 // ExecutionMode run on a thread with the expected priority and I/O restrictions
 // and respect the characteristics of their ExecutionMode.
diff --git a/base/template_util.h b/base/template_util.h
index e298a92..8f4de74 100644
--- a/base/template_util.h
+++ b/base/template_util.h
@@ -12,7 +12,6 @@
 #include <utility>
 
 #include "base/compiler_specific.h"
-#include "build/build_config.h"
 
 #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 7
 #include <vector>
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 078e49b..f626b80f 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -88,6 +88,7 @@
     "perf_time_logger.h",
     "power_monitor_test.cc",
     "power_monitor_test.h",
+    "repeating_test_future.h",
     "scoped_command_line.cc",
     "scoped_command_line.h",
     "scoped_feature_list.cc",
@@ -117,6 +118,7 @@
     "test_file_util.cc",
     "test_file_util.h",
     "test_future.h",
+    "test_future_internal.h",
     "test_io_thread.cc",
     "test_io_thread.h",
     "test_message_loop.cc",
diff --git a/base/test/repeating_test_future.h b/base/test/repeating_test_future.h
new file mode 100644
index 0000000..266feddb
--- /dev/null
+++ b/base/test/repeating_test_future.h
@@ -0,0 +1,235 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_REPEATING_TEST_FUTURE_H_
+#define BASE_TEST_REPEATING_TEST_FUTURE_H_
+
+#include <utility>
+
+#include "base/check.h"
+#include "base/compiler_specific.h"
+#include "base/containers/queue.h"
+#include "base/memory/weak_ptr.h"
+#include "base/run_loop.h"
+#include "base/sequence_checker.h"
+#include "base/test/test_future_internal.h"
+#include "base/thread_annotations.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace base {
+namespace test {
+
+// Related class to |base::test::TestFuture|, which allows its callback and
+// AddValue() method to be called multiple times.
+//
+// Each call to Take() will return one element in FIFO fashion.
+// If no element is available, Take() will wait until an element becomes
+// available.
+//
+// Just like |base::test::TestFuture|, |base::test::RepeatingTestFuture| also
+// supports callbacks which take multiple values. If this is the case Take()
+// will return a tuple containing all values passed to the callback.
+//
+// Example usage:
+//
+//   TEST_F(MyTestFixture, MyTest) {
+//     RepeatingTestFuture<ResultType> future;
+//
+//     InvokeCallbackAsyncTwice(future.GetCallback());
+//
+//     ResultType first_result = future.Get();
+//     ResultType second_result = future.Get();
+//
+//     // When you come here, InvokeCallbackAsyncTwice has finished,
+//     // |first_result| contains the value passed to the first invocation
+//     // of the callback, and |second_result| has the result of the second
+//     // invocation.
+//   }
+//
+// Example without using a callback but using AddValue() instead:
+//
+//   TEST_F(MyTestFixture, MyTest) {
+//     RepeatingTestFuture<std::string> future;
+//
+//     // AddValue() can be used to add an element to the future.
+//     future.AddValue("first-value");
+//     future.AddValue("second-value");
+//
+//     EXPECT_EQ("first-value", future.Take());
+//     EXPECT_EQ("second-value", future.Take());
+//   }
+//
+// Or an example using RepeatingTestFuture::Wait():
+//
+//   TEST_F(MyTestFixture, MyWaitTest) {
+//     RepeatingTestFuture<ResultType> future;
+//
+//     object_under_test.DoSomethingAsync(future.GetCallback());
+//
+//     // Optional. The Take() call below will also wait until the value
+//     // arrives, but this explicit call to Wait() can be useful if you want to
+//     // add extra information.
+//     ASSERT_TRUE(future.Wait()) << "Detailed error message";
+//
+//     ResultType actual_result = future.Take();
+//   }
+//
+// All access to this class must be made from the same sequence.
+template <typename... Types>
+class RepeatingTestFuture {
+ public:
+  // Helper type to make the SFINAE templates easier on the eyes.
+  using T = std::tuple<Types...>;
+  using FirstType = typename std::tuple_element<0, T>::type;
+
+  RepeatingTestFuture() = default;
+  RepeatingTestFuture(const RepeatingTestFuture&) = delete;
+  RepeatingTestFuture& operator=(const RepeatingTestFuture&) = delete;
+  RepeatingTestFuture(RepeatingTestFuture&&) = delete;
+  RepeatingTestFuture& operator=(RepeatingTestFuture&&) = delete;
+  ~RepeatingTestFuture() = default;
+
+  void AddValue(Types... values) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    elements_.push(std::make_tuple(std::forward<Types>(values)...));
+    SignalElementIsAvailable();
+  }
+
+  // Waits until an element is available.
+  // Returns immediately if one or more elements are already available.
+  //
+  // Returns true if an element arrived, or false if a timeout happens.
+  //
+  // Directly calling Wait() is not required as Take() will also wait for
+  // the value to arrive, however you can use a direct call to Wait() to
+  // improve the error reported:
+  //
+  //   ASSERT_TRUE(queue.Wait()) << "Detailed error message";
+  //
+  bool WARN_UNUSED_RESULT Wait() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    if (IsEmpty())
+      WaitForANewElement();
+
+    return !IsEmpty();
+  }
+
+  // Returns a callback that when invoked will store all the argument values,
+  // and unblock any waiters.
+  // This method is templated so you can specify how you need the arguments to
+  // be passed - be it const, as reference, or anything you can think off.
+  // By default the callback accepts the arguments as |Types...|.
+  //
+  // Example usage:
+  //
+  //   RepeatingTestFuture<int, std::string> future;
+  //
+  //   // returns base::RepeatingCallback<void(int, std::string)>
+  //   future.GetCallback();
+  //
+  //   // returns base::RepeatingCallback<void(int, const std::string&)>
+  //   future.GetCallback<int, const std::string&>();
+  //
+  template <typename... CallbackArgumentsTypes>
+  base::RepeatingCallback<void(CallbackArgumentsTypes...)> GetCallback() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    return base::BindRepeating(
+        &RepeatingTestFuture<Types...>::AddValueFromCallbackArguments<
+            CallbackArgumentsTypes...>,
+        weak_ptr_factory_.GetWeakPtr());
+  }
+
+  base::RepeatingCallback<void(Types...)> GetCallback() {
+    return GetCallback<Types...>();
+  }
+
+  // Returns true if no elements are currently present. Note that consuming all
+  // elements through Take() will cause this method to return true after the
+  // last available element has been consumed.
+  bool IsEmpty() const {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    return elements_.empty();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+  //  Accessor methods only available if each element in the future holds a
+  //  single value.
+  //////////////////////////////////////////////////////////////////////////////
+
+  // Wait for an element to arrive, and move its value out.
+  //
+  // Will DCHECK if a timeout happens.
+  template <typename U = T, internal::EnableIfSingleValue<U> = true>
+  FirstType Take() {
+    return std::get<0>(TakeTuple());
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+  //  Accessor methods only available if each element in the future holds
+  //  multiple values.
+  //////////////////////////////////////////////////////////////////////////////
+
+  // Wait for an element to arrive, and move a tuple with its values out.
+  //
+  // Will DCHECK if a timeout happens.
+  template <typename U = T, internal::EnableIfMultiValue<U> = true>
+  std::tuple<Types...> Take() {
+    return TakeTuple();
+  }
+
+ private:
+  // Wait until a new element is available.
+  void WaitForANewElement() VALID_CONTEXT_REQUIRED(sequence_checker_) {
+    DCHECK(!run_loop_.has_value());
+
+    // Create a new run loop.
+    run_loop_.emplace();
+    // Wait until 'run_loop_->Quit()' is called.
+    run_loop_->Run();
+    run_loop_.reset();
+  }
+
+  void SignalElementIsAvailable() VALID_CONTEXT_REQUIRED(sequence_checker_) {
+    if (run_loop_.has_value())
+      run_loop_->Quit();
+  }
+
+  std::tuple<Types...> TakeTuple() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    // Ensure an element is available.
+    bool success = Wait();
+    DCHECK(success) << "Waiting for an element timed out.";
+
+    auto result = std::move(elements_.front());
+    elements_.pop();
+    return result;
+  }
+
+  // Used by GetCallback() to adapt between the form in which the callback
+  // provides arguments, and the argument types specified to this template.
+  // e.g. callbacks often carry arguments as |const Foo&| rather than |Foo|.
+  template <typename... CallbackArgumentsTypes>
+  void AddValueFromCallbackArguments(CallbackArgumentsTypes... values) {
+    AddValue(std::forward<CallbackArgumentsTypes>(values)...);
+  }
+
+  base::queue<std::tuple<Types...>> elements_
+      GUARDED_BY_CONTEXT(sequence_checker_);
+
+  // Used by Wait() to know when AddValue() is called.
+  absl::optional<base::RunLoop> run_loop_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<RepeatingTestFuture<Types...>> weak_ptr_factory_{this};
+};
+
+}  // namespace test
+}  // namespace base
+
+#endif  // BASE_TEST_REPEATING_TEST_FUTURE_H_
diff --git a/base/test/repeating_test_future_unittest.cc b/base/test/repeating_test_future_unittest.cc
new file mode 100644
index 0000000..2394841
--- /dev/null
+++ b/base/test/repeating_test_future_unittest.cc
@@ -0,0 +1,242 @@
+// 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 "base/test/repeating_test_future.h"
+
+#include "base/test/bind.h"
+#include "base/test/gtest_util.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gtest/include/gtest/gtest-spi.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace test {
+
+namespace {
+
+struct MoveOnlyValue {
+ public:
+  MoveOnlyValue() = default;
+  MoveOnlyValue(const MoveOnlyValue&) = delete;
+  auto& operator=(const MoveOnlyValue&) = delete;
+  MoveOnlyValue(MoveOnlyValue&&) = default;
+  MoveOnlyValue& operator=(MoveOnlyValue&&) = default;
+  ~MoveOnlyValue() = default;
+
+  std::string data;
+};
+
+}  // namespace
+
+class RepeatingTestFutureTest : public ::testing::Test {
+ public:
+  RepeatingTestFutureTest() = default;
+  RepeatingTestFutureTest(const RepeatingTestFutureTest&) = delete;
+  RepeatingTestFutureTest& operator=(const RepeatingTestFutureTest&) = delete;
+  ~RepeatingTestFutureTest() override = default;
+
+  void RunLater(OnceClosure callable) {
+    ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callable));
+  }
+
+ private:
+  test::SingleThreadTaskEnvironment environment_;
+};
+
+TEST_F(RepeatingTestFutureTest, ShouldBeEmptyInitially) {
+  RepeatingTestFuture<std::string> future;
+
+  EXPECT_TRUE(future.IsEmpty());
+}
+
+TEST_F(RepeatingTestFutureTest, ShouldNotBeEmptyAfterAddingAValue) {
+  RepeatingTestFuture<std::string> future;
+
+  future.AddValue("a value");
+
+  EXPECT_FALSE(future.IsEmpty());
+}
+
+TEST_F(RepeatingTestFutureTest, ShouldBeEmptyAfterTakingTheOnlyElement) {
+  RepeatingTestFuture<std::string> future;
+
+  future.AddValue("a value");
+  future.Take();
+
+  EXPECT_TRUE(future.IsEmpty());
+}
+
+TEST_F(RepeatingTestFutureTest,
+       ShouldNotBeEmptyIfTakingOneElementFromAfutureWith2Elements) {
+  RepeatingTestFuture<std::string> future;
+
+  future.AddValue("first value");
+  future.AddValue("second value");
+  future.Take();
+
+  EXPECT_FALSE(future.IsEmpty());
+}
+
+TEST_F(RepeatingTestFutureTest, ShouldBeAbleToTakeElementsFiFo) {
+  RepeatingTestFuture<std::string> future;
+
+  future.AddValue("first value");
+  future.AddValue("second value");
+
+  EXPECT_EQ(future.Take(), "first value");
+  EXPECT_EQ(future.Take(), "second value");
+}
+
+TEST_F(RepeatingTestFutureTest, WaitShouldBlockUntilElementArrives) {
+  RepeatingTestFuture<std::string> future;
+
+  RunLater(BindLambdaForTesting([&future]() { future.AddValue("a value"); }));
+  EXPECT_TRUE(future.IsEmpty());
+
+  EXPECT_TRUE(future.Wait());
+
+  EXPECT_FALSE(future.IsEmpty());
+}
+
+TEST_F(RepeatingTestFutureTest, WaitShouldReturnTrueWhenValueArrives) {
+  RepeatingTestFuture<std::string> future;
+
+  RunLater(BindLambdaForTesting([&future]() { future.AddValue("a value"); }));
+
+  EXPECT_TRUE(future.Wait());
+
+  EXPECT_FALSE(future.IsEmpty());
+}
+
+TEST_F(RepeatingTestFutureTest,
+       WaitShouldReturnTrueImmediatelyWhenValueIsAlreadyPresent) {
+  RepeatingTestFuture<std::string> future;
+
+  future.AddValue("value already present");
+
+  EXPECT_TRUE(future.Wait());
+}
+
+TEST_F(RepeatingTestFutureTest, WaitShouldReturnFalseIfTimeoutHappens) {
+  test::ScopedRunLoopTimeout timeout(FROM_HERE, Milliseconds(1));
+
+  // |ScopedRunLoopTimeout| will automatically fail the test when a timeout
+  // happens, so we use EXPECT_FATAL_FAILURE to handle this failure.
+  // EXPECT_FATAL_FAILURE only works on static objects.
+  static bool success;
+  static RepeatingTestFuture<std::string> future;
+
+  EXPECT_FATAL_FAILURE({ success = future.Wait(); }, "timed out");
+
+  EXPECT_FALSE(success);
+}
+
+TEST_F(RepeatingTestFutureTest, TakeShouldBlockUntilAnElementArrives) {
+  RepeatingTestFuture<std::string> future;
+
+  RunLater(BindLambdaForTesting(
+      [&future]() { future.AddValue("value pushed delayed"); }));
+
+  EXPECT_EQ(future.Take(), "value pushed delayed");
+}
+
+TEST_F(RepeatingTestFutureTest, TakeShouldDcheckIfTimeoutHappens) {
+  test::ScopedRunLoopTimeout timeout(FROM_HERE, Milliseconds(1));
+
+  RepeatingTestFuture<std::string> future;
+
+  EXPECT_DCHECK_DEATH_WITH(future.Take(), "timed out");
+}
+
+TEST_F(RepeatingTestFutureTest, TakeShouldWorkWithMoveOnlyValue) {
+  RepeatingTestFuture<MoveOnlyValue> future;
+
+  RunLater(BindLambdaForTesting(
+      [&future]() { future.AddValue({.data = "move only value"}); }));
+
+  MoveOnlyValue result = future.Take();
+
+  EXPECT_EQ(result.data, "move only value");
+}
+
+TEST_F(RepeatingTestFutureTest, ShouldStoreValuePassedToCallback) {
+  RepeatingTestFuture<std::string> future;
+
+  RunLater(BindOnce(future.GetCallback(), "value"));
+
+  EXPECT_EQ("value", future.Take());
+}
+
+TEST_F(RepeatingTestFutureTest, ShouldAllowInvokingCallbackMultipleTimes) {
+  RepeatingTestFuture<std::string> future;
+
+  RunLater(BindLambdaForTesting([callback = future.GetCallback()]() {
+    callback.Run("first value");
+    callback.Run("second value");
+    callback.Run("third value");
+  }));
+
+  EXPECT_EQ("first value", future.Take());
+  EXPECT_EQ("second value", future.Take());
+  EXPECT_EQ("third value", future.Take());
+}
+
+TEST_F(RepeatingTestFutureTest, ShouldAllowReferenceArgumentsForCallback) {
+  RepeatingTestFuture<std::string> future;
+
+  RepeatingCallback<void(const std::string&)> callback =
+      future.GetCallback<const std::string&>();
+  RunLater(BindOnce(std::move(callback), "expected value"));
+
+  EXPECT_EQ("expected value", future.Take());
+}
+
+TEST_F(RepeatingTestFutureTest, ShouldStoreMultipleValuesInATuple) {
+  const int expected_int_value = 5;
+  const std::string expected_string_value = "value";
+
+  RepeatingTestFuture<int, std::string> future;
+
+  RunLater(BindLambdaForTesting(
+      [&]() { future.AddValue(expected_int_value, expected_string_value); }));
+
+  std::tuple<int, std::string> actual = future.Take();
+  EXPECT_EQ(expected_int_value, std::get<0>(actual));
+  EXPECT_EQ(expected_string_value, std::get<1>(actual));
+}
+
+TEST_F(RepeatingTestFutureTest, ShouldAllowCallbackWithMultipleValues) {
+  const int expected_int_value = 5;
+  const std::string expected_string_value = "value";
+
+  RepeatingTestFuture<int, std::string> future;
+
+  RunLater(BindOnce(future.GetCallback(), expected_int_value,
+                    expected_string_value));
+
+  std::tuple<int, std::string> actual = future.Take();
+  EXPECT_EQ(expected_int_value, std::get<0>(actual));
+  EXPECT_EQ(expected_string_value, std::get<1>(actual));
+}
+
+TEST_F(RepeatingTestFutureTest,
+       ShouldAllowCallbackWithMultipleReferenceValues) {
+  const int expected_int_value = 5;
+  const std::string expected_string_value = "value";
+
+  RepeatingTestFuture<int, std::string> future;
+
+  RepeatingCallback<void(const int&, std::string)> callback =
+      future.GetCallback<const int&, std::string>();
+  RunLater(
+      BindOnce(std::move(callback), expected_int_value, expected_string_value));
+
+  std::tuple<int, std::string> actual = future.Take();
+  EXPECT_EQ(expected_int_value, std::get<0>(actual));
+  EXPECT_EQ(expected_string_value, std::get<1>(actual));
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/base/test/test_future.h b/base/test/test_future.h
index 64ba0737..050ec51 100644
--- a/base/test/test_future.h
+++ b/base/test/test_future.h
@@ -15,6 +15,7 @@
 #include "base/run_loop.h"
 #include "base/sequence_checker.h"
 #include "base/test/bind.h"
+#include "base/test/test_future_internal.h"
 #include "base/thread_annotations.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -22,20 +23,6 @@
 namespace base {
 namespace test {
 
-namespace internal {
-
-// Helper to only implement a method if the future holds a single value
-template <typename Tuple>
-using EnableIfSingleValue =
-    std::enable_if_t<(std::tuple_size<Tuple>::value <= 1), bool>;
-
-// Helper to only implement a method if the future holds multiple values
-template <typename Tuple>
-using EnableIfMultiValue =
-    std::enable_if_t<(std::tuple_size<Tuple>::value > 1), bool>;
-
-}  // namespace internal
-
 // Helper class to test code that returns its result(s) asynchronously through a
 // callback:
 //
@@ -191,7 +178,9 @@
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
     DCHECK(!values_.has_value())
-        << "The value of a TestFuture can only be set once.";
+        << "The value of a TestFuture can only be set once. If you need to "
+           "handle an ordered stream of result values, use "
+           "|base::test::RepeatingTestFuture|.";
 
     values_ = std::make_tuple(std::forward<Types>(values)...);
     run_loop_.Quit();
diff --git a/base/test/test_future_internal.h b/base/test/test_future_internal.h
new file mode 100644
index 0000000..84739ab
--- /dev/null
+++ b/base/test/test_future_internal.h
@@ -0,0 +1,28 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_FUTURE_INTERNAL_H_
+#define BASE_TEST_TEST_FUTURE_INTERNAL_H_
+
+namespace base {
+namespace test {
+
+namespace internal {
+
+// Helper to only implement a method if the future holds a single value
+template <typename Tuple>
+using EnableIfSingleValue =
+    std::enable_if_t<(std::tuple_size<Tuple>::value <= 1), bool>;
+
+// Helper to only implement a method if the future holds multiple values
+template <typename Tuple>
+using EnableIfMultiValue =
+    std::enable_if_t<(std::tuple_size<Tuple>::value > 1), bool>;
+
+}  // namespace internal
+
+}  // namespace test
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_FUTURE_INTERNAL_H_
diff --git a/base/trace_event/memory_infra_background_allowlist.cc b/base/trace_event/memory_infra_background_allowlist.cc
index e13528d4..8d4a0ce8 100644
--- a/base/trace_event/memory_infra_background_allowlist.cc
+++ b/base/trace_event/memory_infra_background_allowlist.cc
@@ -116,6 +116,7 @@
     "extensions/value_store/Extensions.Database.Open.Rules/0x?",
     "extensions/value_store/Extensions.Database.Open.State/0x?",
     "extensions/value_store/Extensions.Database.Open.Scripts/0x?",
+    "extensions/value_store/Extensions.Database.Open.WebAppsLockScreen/0x?",
     "extensions/value_store/Extensions.Database.Open/0x?",
     "extensions/value_store/Extensions.Database.Restore/0x?",
     "extensions/value_store/Extensions.Database.Value.Restore/0x?",
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 7a1825e..e45fa2c8 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -129,8 +129,8 @@
   # TODO(gbiv): We disable optimizations by default on most platforms because
   # the space overhead is too great. We should use some mixture of profiles and
   # optimization settings to better tune the size increase.
-  thin_lto_enable_optimizations =
-      (is_chromeos_ash || is_android || is_win || is_linux) && is_official_build
+  thin_lto_enable_optimizations = (is_chromeos_ash || is_android || is_win ||
+                                   is_linux || is_mac) && is_official_build
 
   # Initialize all local variables with a pattern. This flag will fill
   # uninitialized floating-point types (and 32-bit pointers) with 0xFF and the
@@ -654,10 +654,11 @@
   if (!is_debug && use_thin_lto && is_a_target_toolchain) {
     assert(use_lld, "LTO is only supported with lld")
 
-    cflags += [
-      "-flto=thin",
-      "-fsplit-lto-unit",
-    ]
+    cflags += [ "-flto=thin" ]
+    if (!is_mac) {
+      # TODO(lgrey): Enable unit splitting for Mac when supported.
+      cflags += [ "-fsplit-lto-unit" ]
+    }
 
     # Limit the size of the ThinLTO cache to the lesser of 10% of
     # available disk space, 40GB and 100000 files.
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index a41df19..3520e428 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -74,7 +74,8 @@
   use_thin_lto =
       is_cfi ||
       (is_clang && is_official_build && chrome_pgo_phase != 1 &&
-       (is_linux || is_win || (is_android && target_os != "chromeos") ||
+       (is_linux || is_win || is_mac ||
+        (is_android && target_os != "chromeos") ||
         ((is_chromeos_ash || is_chromeos_lacros) && is_chromeos_device)))
 
   # If true, use Goma for ThinLTO code generation where applicable.
diff --git a/build/config/rust.gni b/build/config/rust.gni
index b1b2e59..0745285 100644
--- a/build/config/rust.gni
+++ b/build/config/rust.gni
@@ -74,7 +74,7 @@
 # We use the Rust linker for building test executables, so we only build them
 # if we're able to use the Rust linker. We could use the C++ linker for this
 # too, we've just not set up GN to do so at the moment.
-build_rust_unit_tests = rustc_can_link
+build_rust_unit_tests = toolchain_has_rust && rustc_can_link
 
 # We want to store rust_sysroot as a source-relative variable for ninja
 # portability. In practice if an external toolchain was specified, it might
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index a1e37e8..dd074b9 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20211217.0.1
+7.20211221.0.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index a1e37e8..dd074b9 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20211217.0.1
+7.20211221.0.1
diff --git a/build/rust/BUILD.gn b/build/rust/BUILD.gn
index d28b4380..1ca9564 100644
--- a/build/rust/BUILD.gn
+++ b/build/rust/BUILD.gn
@@ -51,3 +51,11 @@
 group("cxx_rustdeps") {
   deps = [ "//third_party/rust/cxx/v1:lib" ]
 }
+
+# lld currently eats a section which Rust executables need to
+# determine their command line arguments. These lines work around this
+# by retaining all sections.
+# TODO(crbug/1281664) - remove these lines when this is fixed.
+config("keep_sections") {
+  ldflags = [ "-Wl,--no-gc-sections" ]
+}
diff --git a/build/rust/cargo_crate.gni b/build/rust/cargo_crate.gni
index 30293cc..eeda51b 100644
--- a/build/rust/cargo_crate.gni
+++ b/build/rust/cargo_crate.gni
@@ -256,4 +256,14 @@
   executable_configs = default_executable_configs
   executable_configs -= [ "//build/config/compiler:chromium_code" ]
   executable_configs += [ "//build/config/compiler:no_chromium_code" ]
+
+  # lld currently eats a section which Rust executables need to
+  # determine their command line arguments. These lines work around this
+  # by retaining all sections.
+  # TODO(crbug/1281664) - remove these lines when this is fixed.
+  executable_configs -= [ "//build/config/compiler:default_optimization" ]
+  executable_configs += [
+    "//build/config/compiler:no_optimize",
+    "//build/rust:keep_sections",
+  ]
 }
diff --git a/build/rust/mixed_executable.gni b/build/rust/mixed_executable.gni
index ced6426..bec8e94 100644
--- a/build/rust/mixed_executable.gni
+++ b/build/rust/mixed_executable.gni
@@ -20,4 +20,16 @@
 
 set_defaults("mixed_executable") {
   configs = default_executable_configs
+
+  # lld currently eats a section which Rust executables need to
+  # determine their command line arguments. These lines work around this
+  # by retaining all sections.
+  # TODO(crbug/1281664) - remove these lines when this is fixed.
+  if (toolchain_has_rust) {
+    configs -= [ "//build/config/compiler:default_optimization" ]
+    configs += [
+      "//build/config/compiler:no_optimize",
+      "//build/rust:keep_sections",
+    ]
+  }
 }
diff --git a/build/rust/rust_unit_test.gni b/build/rust/rust_unit_test.gni
index 1dd3250..f3eee62 100644
--- a/build/rust/rust_unit_test.gni
+++ b/build/rust/rust_unit_test.gni
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/rust.gni")
+import("//build/rust/rust_unit_tests_group.gni")
 
 # Defines a Rust unit test.
 #
@@ -73,41 +74,9 @@
   # TODO(crbug.com/1256930) - verify this is correct
   assert(defined(invoker.sources), "sources must be listed")
 
-  _original_target_name = target_name
   _exe_target_name = target_name + "_exe"
-  _script_target_name = target_name + "_wrapper_script"
-  group(target_name) {
-    testonly = true
-    deps = [
-      ":$_exe_target_name",
-      ":$_script_target_name",
-    ]
-  }
-
-  # Note that the location/filename of the wrapper script has to follow the
-  # requirements of "generated_script" as outlined in the doc comment at the
-  # top of //testing/buildbot/gn_isolate_map.pyl
-  _script_filepath = "$root_out_dir/bin/run_${_original_target_name}"
-
-  # TODO(https://crbug.com/1271215): Also generate: bin/run_${target_name}.bat
-  # on Windows: if (is_win) { ... }.
-  action(_script_target_name) {
-    testonly = true
-    script = "//testing/scripts/rust/generate_bash_script.py"
-    outputs = [ _script_filepath ]
-    data = [ _script_filepath ]
-    data_deps = [
-      ":$_exe_target_name",
-      "//testing/scripts/rust",
-    ]
-    args = [
-      "--rust-test-executable",
-      _crate_name,
-      "--exe-dir",
-      rebase_path(root_out_dir, root_build_dir),
-      "--script-path",
-      rebase_path(_script_filepath, root_build_dir),
-    ]
+  rust_unit_tests_group(target_name) {
+    deps = [ ":$_exe_target_name" ]
   }
 
   # TODO(crbug.com/1229320): Arrange to run test executables on try bots.
@@ -138,11 +107,25 @@
     configs = _configs
     crate_name = _crate_name
     crate_root = _crate_root
+    metadata = {
+      # Consumed by "rust_unit_tests_group" gni template.
+      rust_unit_test_executables = [ _crate_name ]
+    }
   }
 }
 
 set_defaults("rust_unit_test") {
   configs = default_executable_configs
+
+  # lld currently eats a section which Rust executables need to
+  # determine their command line arguments. These lines work around this
+  # by retaining all sections.
+  # TODO(crbug/1281664) - remove these lines when this is fixed.
+  configs -= [ "//build/config/compiler:default_optimization" ]
+  configs += [
+    "//build/config/compiler:no_optimize",
+    "//build/rust:keep_sections",
+  ]
   deps = []
   rustflags = []
 }
diff --git a/build/rust/rust_unit_tests_group.gni b/build/rust/rust_unit_tests_group.gni
new file mode 100644
index 0000000..0d67c7e
--- /dev/null
+++ b/build/rust/rust_unit_tests_group.gni
@@ -0,0 +1,89 @@
+# 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.
+
+# Defines a Rust unit tests group.
+#
+# This generates a script that wraps 1 or more Rust unit test executables.
+# Such script would typically wrap all Rust unit tests from a set of related
+# crates (e.g. all crates under //base).
+#
+# The script is primarily useful to enable running the tests on Chromium bots,
+# but it is also a convenience for having a single entry point for running
+# the tests locally (without having to manually fan out to all the individual
+# executables).
+#
+# Parameters:
+#
+#   deps - Will be recursively traversed to discover all the Rust unit test
+#          executables.
+#
+# Example usage:
+#
+#   # This will generate a script at out/Default/bin/run_foo_tests that wraps
+#   # out/Default/foo_crate1_unittests,
+#   # out/Default/foo_mixed_source_set2_rs_unittests,
+#   # and out/Default/foo_mixed_source_set3_rs_unittests executables containing
+#   # native Rust unit tests.
+#   rust_unit_tests_group("foo_tests") {
+#     deps = [
+#       "foo_crate1",
+#       "foo_mixed_source_set2",
+#       "foo_mixed_source_set3",
+#     ]
+#   }
+#   # TODO(https://crbug.com/1271215): Mention .bat generation once implemented.
+
+template("rust_unit_tests_group") {
+  assert(defined(invoker.deps), "deps must be listed")
+
+  # As noted in the top-level comment of //testing/buildbot/gn_isolate_map.pyl
+  # the script *must* be in output_dir/bin/run_$target (or
+  # output_dir\bin\run_$target.bat on Windows).
+  _script_filepath = "$root_out_dir/bin/run_${target_name}"
+
+  # Gathering metadata provided by the rust_unit_test gni template from all of
+  # our dependencies.
+  _metadata_target_name = "${target_name}_metadata"
+  _metadata_filepath = "$root_build_dir/${target_name}__rust_unittest_exes.txt"
+  generated_file(_metadata_target_name) {
+    forward_variables_from(invoker, [ "deps" ], [])
+
+    testonly = true
+    outputs = [ _metadata_filepath ]
+    data_keys = [ "rust_unit_test_executables" ]
+  }
+
+  # Generating a script that can run all of the wrapped Rust unit test
+  # executables.
+  #
+  # TODO(https://crbug.com/1271215): Also generate: bin/run_${target_name}.bat
+  # when *targeting* Windows: if (is_win) { ... }.  (The "targeting" part means
+  # that we can't just detect whether the build is *hosted* on Windows.)
+  action(target_name) {
+    forward_variables_from(invoker, "*", [])
+
+    testonly = true
+    script = "//testing/scripts/rust/generate_bash_script.py"
+    outputs = [ _script_filepath ]
+
+    data = [ _script_filepath ]
+
+    if (!defined(data_deps)) {
+      data_deps = []
+    }
+    data_deps += [ "//testing/scripts/rust" ]
+    data_deps += deps
+
+    deps += [ ":$_metadata_target_name" ]
+
+    args = [
+      "--rust-test-executables",
+      rebase_path(_metadata_filepath, root_build_dir),
+      "--exe-dir",
+      rebase_path(root_out_dir, root_build_dir),
+      "--script-path",
+      rebase_path(_script_filepath, root_build_dir),
+    ]
+  }
+}
diff --git a/build/rust/tests/BUILD.gn b/build/rust/tests/BUILD.gn
index 7e7ea2c..f5e4487d 100644
--- a/build/rust/tests/BUILD.gn
+++ b/build/rust/tests/BUILD.gn
@@ -3,12 +3,22 @@
 # found in the LICENSE file.
 
 import("//build/config/rust.gni")
+import("//build/rust/rust_unit_tests_group.gni")
 
 group("tests") {
   # Build some minimal binaries to exercise the Rust toolchain
   # only if that toolchain is enabled in gn args.
   testonly = true
 
+  deps = [ ":deps" ]
+  if (build_rust_unit_tests) {
+    deps += [ ":native_rust_unittests__from__build_rust_tests" ]
+  }
+}
+
+group("deps") {
+  testonly = true
+
   # These should build with or without Rust, in different modes
   deps = [
     "test_mixed_component:test_mixed_component_demo",
@@ -46,3 +56,9 @@
     }
   }
 }
+
+if (build_rust_unit_tests) {
+  rust_unit_tests_group("native_rust_unittests__from__build_rust_tests") {
+    deps = [ ":deps" ]
+  }
+}
diff --git a/cc/layers/texture_layer.cc b/cc/layers/texture_layer.cc
index a48cc7e..a2699a5 100644
--- a/cc/layers/texture_layer.cc
+++ b/cc/layers/texture_layer.cc
@@ -103,16 +103,16 @@
     viz::ReleaseCallback release_callback,
     bool requires_commit) {
   DCHECK(IsMutationAllowed());
-  DCHECK(resource.mailbox_holder.mailbox.IsZero() || !holder_ref_ ||
-         resource != holder_ref_->holder()->resource());
+  DCHECK(resource.mailbox_holder.mailbox.IsZero() || !resource_holder_ ||
+         resource != resource_holder_->resource());
   DCHECK_EQ(resource.mailbox_holder.mailbox.IsZero(), !release_callback);
 
   // If we never commited the mailbox, we need to release it here.
   if (!resource.mailbox_holder.mailbox.IsZero()) {
-    holder_ref_ = TransferableResourceHolder::Create(
+    resource_holder_ = TransferableResourceHolder::Create(
         resource, std::move(release_callback));
   } else {
-    holder_ref_ = nullptr;
+    resource_holder_ = nullptr;
   }
   needs_set_resource_ = true;
   // If we are within a commit, no need to do it again immediately after.
@@ -143,7 +143,7 @@
   // If we're removed from the tree, the TextureLayerImpl will be destroyed, and
   // we will need to set the mailbox again on a new TextureLayerImpl the next
   // time we push.
-  if (!host && holder_ref_)
+  if (!host && resource_holder_)
     needs_set_resource_ = true;
   if (host) {
     // When attached to a new LayerTreeHost, all previously registered
@@ -158,7 +158,7 @@
 }
 
 bool TextureLayer::HasDrawableContent() const {
-  return (client_ || holder_ref_) && Layer::HasDrawableContent();
+  return (client_ || resource_holder_) && Layer::HasDrawableContent();
 }
 
 bool TextureLayer::Update() {
@@ -209,13 +209,13 @@
   if (needs_set_resource_) {
     viz::TransferableResource resource;
     viz::ReleaseCallback release_callback;
-    if (holder_ref_) {
-      TransferableResourceHolder* holder = holder_ref_->holder();
-      resource = holder->resource();
+    if (resource_holder_) {
+      resource = resource_holder_->resource();
       release_callback =
-          holder->GetCallbackForImplThread(layer->layer_tree_impl()
+          base::BindOnce(&TransferableResourceHolder::Return, resource_holder_,
+                         base::RetainedRef(layer->layer_tree_impl()
                                                ->task_runner_provider()
-                                               ->MainThreadTaskRunner());
+                                               ->MainThreadTaskRunner()));
     }
     texture_layer->SetTransferableResource(resource,
                                            std::move(release_callback));
@@ -268,23 +268,6 @@
   SetNeedsPushProperties();
 }
 
-TextureLayer::TransferableResourceHolder::MainThreadReference::
-    MainThreadReference(TransferableResourceHolder* holder)
-    : holder_(holder) {
-  holder_->InternalAddRef();
-}
-
-TextureLayer::TransferableResourceHolder::MainThreadReference::
-    ~MainThreadReference() {
-#if DCHECK_IS_ON()
-  {
-    base::AutoLock hold(holder_->posted_internal_derefs_lock_);
-    ++holder_->posted_internal_derefs_;
-  }
-#endif
-  holder_->InternalRelease();
-}
-
 TextureLayer::TransferableResourceHolder::TransferableResourceHolder(
     const viz::TransferableResource& resource,
     viz::ReleaseCallback release_callback)
@@ -293,85 +276,36 @@
       sync_token_(resource.mailbox_holder.sync_token) {}
 
 TextureLayer::TransferableResourceHolder::~TransferableResourceHolder() {
-#if DCHECK_IS_ON()
-  {
-    // If the MessageLoop is destroyed while a posted deref is waiting to run,
-    // this object will be destroyed with an internal_references_ still present.
-    // So we must also include the outstanding posted derefences.
-    base::AutoLock hold(posted_internal_derefs_lock_);
-    DCHECK_EQ(internal_references_, posted_internal_derefs_);
-  }
-#endif
   if (release_callback_) {
-    // We land here if the dereferences are posted but not run and the
-    // MessageLoop is destroyed, destroying those tasks and this object with it.
-    // We run the ReleaseCallback in that case assuming the MessageLoop is being
-    // destroyed on the main thread.
-    DCHECK(main_thread_checker_.CalledOnValidThread());
-    std::move(release_callback_).Run(sync_token_, is_lost_);
+    if (!release_callback_task_runner_ ||
+        release_callback_task_runner_->RunsTasksInCurrentSequence()) {
+      std::move(release_callback_).Run(sync_token_, is_lost_);
+    } else {
+      DCHECK(release_callback_task_runner_);
+      release_callback_task_runner_->PostTask(
+          FROM_HERE,
+          base::BindOnce(std::move(release_callback_), sync_token_, is_lost_));
+    }
   }
 }
 
-std::unique_ptr<TextureLayer::TransferableResourceHolder::MainThreadReference>
+scoped_refptr<TextureLayer::TransferableResourceHolder>
 TextureLayer::TransferableResourceHolder::Create(
     const viz::TransferableResource& resource,
     viz::ReleaseCallback release_callback) {
-  return std::make_unique<MainThreadReference>(
-      new TransferableResourceHolder(resource, std::move(release_callback)));
+  return new TransferableResourceHolder(resource, std::move(release_callback));
 }
 
 void TextureLayer::TransferableResourceHolder::Return(
+    scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner,
     const gpu::SyncToken& sync_token,
     bool is_lost) {
-  base::AutoLock lock(arguments_lock_);
   sync_token_ = sync_token;
   is_lost_ = is_lost;
-}
-
-viz::ReleaseCallback
-TextureLayer::TransferableResourceHolder::GetCallbackForImplThread(
-    scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner) {
-  // We can't call GetCallbackForImplThread if we released the main thread
-  // reference.
-  DCHECK_GT(internal_references_, 0);
-  InternalAddRef();
-  return base::BindOnce(
-      &TransferableResourceHolder::ReturnAndReleaseOnImplThread, this,
-      std::move(main_thread_task_runner));
-}
-
-void TextureLayer::TransferableResourceHolder::InternalAddRef() {
-  ++internal_references_;
-}
-
-void TextureLayer::TransferableResourceHolder::InternalRelease() {
-  DCHECK(main_thread_checker_.CalledOnValidThread());
-#if DCHECK_IS_ON()
-  {
-    base::AutoLock hold(posted_internal_derefs_lock_);
-    --posted_internal_derefs_;
-  }
-#endif
-  if (!--internal_references_) {
-    std::move(release_callback_).Run(sync_token_, is_lost_);
-    resource_ = viz::TransferableResource();
-  }
-}
-
-void TextureLayer::TransferableResourceHolder::ReturnAndReleaseOnImplThread(
-    const scoped_refptr<base::SequencedTaskRunner>& main_thread_task_runner,
-    const gpu::SyncToken& sync_token,
-    bool is_lost) {
-  Return(sync_token, is_lost);
-#if DCHECK_IS_ON()
-  {
-    base::AutoLock hold(posted_internal_derefs_lock_);
-    ++posted_internal_derefs_;
-  }
-#endif
-  main_thread_task_runner->PostTask(
-      FROM_HERE,
-      base::BindOnce(&TransferableResourceHolder::InternalRelease, this));
+  // When this method returns, the refcount of the holder will be decremented,
+  // which might cause it to be destructed on the impl thread. We store the
+  // main thread task runner here to make sure it's available to the destructor.
+  release_callback_task_runner_ = std::move(main_thread_task_runner);
 }
 
 }  // namespace cc
diff --git a/cc/layers/texture_layer.h b/cc/layers/texture_layer.h
index 7701767..e8323dc 100644
--- a/cc/layers/texture_layer.h
+++ b/cc/layers/texture_layer.h
@@ -38,75 +38,48 @@
   class CC_EXPORT TransferableResourceHolder
       : public base::RefCountedThreadSafe<TransferableResourceHolder> {
    public:
-    class CC_EXPORT MainThreadReference {
-     public:
-      explicit MainThreadReference(TransferableResourceHolder* holder);
-      MainThreadReference(const MainThreadReference&) = delete;
-      ~MainThreadReference();
-
-      MainThreadReference& operator=(const MainThreadReference&) = delete;
-
-      TransferableResourceHolder* holder() { return holder_.get(); }
-
-     private:
-      scoped_refptr<TransferableResourceHolder> holder_;
-    };
-
     TransferableResourceHolder(const TransferableResourceHolder&) = delete;
     TransferableResourceHolder& operator=(const TransferableResourceHolder&) =
         delete;
 
     const viz::TransferableResource& resource() const { return resource_; }
-    void Return(const gpu::SyncToken& sync_token, bool is_lost);
-
-    // Gets a viz::ReleaseCallback that can be called from another thread. Note:
-    // the caller must ensure the callback is called.
-    viz::ReleaseCallback GetCallbackForImplThread(
-        scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner);
+    void Return(
+        scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner,
+        const gpu::SyncToken& sync_token,
+        bool is_lost);
 
    protected:
     friend class TextureLayer;
 
-    // Protected visiblity so only TextureLayer and unit tests can create these.
-    static std::unique_ptr<MainThreadReference> Create(
+    // Protected visibility so only TextureLayer and unit tests can create
+    // these.
+    static scoped_refptr<TransferableResourceHolder> Create(
         const viz::TransferableResource& resource,
         viz::ReleaseCallback release_callback);
     virtual ~TransferableResourceHolder();
 
    private:
     friend class base::RefCountedThreadSafe<TransferableResourceHolder>;
-    friend class MainThreadReference;
     explicit TransferableResourceHolder(
         const viz::TransferableResource& resource,
         viz::ReleaseCallback release_callback);
 
-    void InternalAddRef();
-    void InternalRelease();
-    void ReturnAndReleaseOnImplThread(
-        const scoped_refptr<base::SequencedTaskRunner>& main_thread_task_runner,
-        const gpu::SyncToken& sync_token,
-        bool is_lost);
+    const viz::TransferableResource resource_;
 
-    // These members are only accessed on the main thread, or on the impl thread
-    // during commit where the main thread is blocked.
-    int internal_references_ = 0;
-#if DCHECK_IS_ON()
-    // The number of derefs posted from the impl thread, and a lock for
-    // accessing it.
-    base::Lock posted_internal_derefs_lock_;
-    int posted_internal_derefs_ = 0;
-#endif
-    viz::TransferableResource resource_;
+    // This is accessed only on the main thread.
     viz::ReleaseCallback release_callback_;
 
-    // This lock guards the sync_token_ and is_lost_ fields because they can be
-    // accessed on both the impl and main thread. We do this to ensure that the
-    // values of these fields are well-ordered such that the last call to
-    // ReturnAndReleaseOnImplThread() defines their values.
-    base::Lock arguments_lock_;
+    // release_callback_task_runner_, sync_token_, and is_lost_ are only
+    // modified on the impl thread, and only read from the destructor, so they
+    // are not subject to race conditions.
+
+    // If a reference to the resource is sent to the impl thread, then there's a
+    // possibility that the resource will be destructed on the impl thread; but
+    // release_callback_ has to run on the main thread. In that case, we use
+    // release_callback_task_runner_ to PostTask to run the ReleaseCallback.
+    scoped_refptr<base::SequencedTaskRunner> release_callback_task_runner_;
     gpu::SyncToken sync_token_;
     bool is_lost_ = false;
-    base::ThreadChecker main_thread_checker_;
   };
 
   // Used when mailbox names are specified instead of texture IDs.
@@ -171,8 +144,8 @@
       scoped_refptr<CrossThreadSharedBitmap> bitmap) override;
 
   viz::TransferableResource current_transferable_resource() const {
-    return holder_ref_ ? holder_ref_->holder()->resource()
-                       : viz::TransferableResource();
+    return resource_holder_ ? resource_holder_->resource()
+                            : viz::TransferableResource();
   }
 
  protected:
@@ -203,7 +176,7 @@
   bool blend_background_color_ = false;
   bool force_texture_to_opaque_ = false;
 
-  std::unique_ptr<TransferableResourceHolder::MainThreadReference> holder_ref_;
+  scoped_refptr<TransferableResourceHolder> resource_holder_;
   bool needs_set_resource_ = false;
 
   // The set of SharedBitmapIds to register with the LayerTreeFrameSink on the
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc
index 6fd740f..c4671a9 100644
--- a/cc/layers/texture_layer_unittest.cc
+++ b/cc/layers/texture_layer_unittest.cc
@@ -398,21 +398,22 @@
   }
 
   void CreateMainRef() {
-    main_ref_ = TestMailboxHolder::Create(test_data_.resource1_,
-                                          test_data_.release_callback1_);
+    resource_holder_ = TestMailboxHolder::Create(test_data_.resource1_,
+                                                 test_data_.release_callback1_);
   }
 
-  void ReleaseMainRef() { main_ref_ = nullptr; }
+  void ReleaseMainRef() { resource_holder_ = nullptr; }
 
   void CreateImplRef(
       viz::ReleaseCallback* impl_ref,
       scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner) {
-    *impl_ref = main_ref_->holder()->GetCallbackForImplThread(
-        std::move(main_thread_task_runner));
+    *impl_ref =
+        base::BindOnce(&TextureLayer::TransferableResourceHolder::Return,
+                       resource_holder_, main_thread_task_runner);
   }
 
  protected:
-  std::unique_ptr<TestMailboxHolder::MainThreadReference> main_ref_;
+  scoped_refptr<TextureLayer::TransferableResourceHolder> resource_holder_;
   base::Thread main_thread_;
 };
 
@@ -651,8 +652,8 @@
         // Resetting the resource will call the callback now, before another
         // commit is needed, as the ReleaseCallback is already in flight from
         // RemoveFromParent().
-        layer_->ClearTexture();
         pending_callback_ = true;
+        layer_->ClearTexture();
         frame_number_ = layer_tree_host()->SourceFrameNumber();
         break;
       case 8:
@@ -676,8 +677,16 @@
     ++callback_count_;
 
     // If we are waiting on a callback, advance now.
-    if (pending_callback_)
-      AdvanceTestCase();
+    if (pending_callback_) {
+      layer_tree_host()
+          ->GetTaskRunnerProvider()
+          ->MainThreadTaskRunner()
+          ->PostTask(
+              FROM_HERE,
+              base::BindOnce(
+                  &TextureLayerImplWithMailboxThreadedCallback::AdvanceTestCase,
+                  base::Unretained(this)));
+    }
   }
 
   void SetMailbox(char mailbox_char) {
@@ -1869,11 +1878,15 @@
     }
   }
 
+  void WillCommit(const CommitState& commit_state) override {
+    source_frame_number_ = commit_state.source_frame_number;
+  }
+
   void ReleaseCallback(const gpu::SyncToken& token, bool lost) {
     // The software resource is not released when the LayerTreeFrameSink is lost
     // since software resources are not destroyed by the GPU process dying. It
     // is released only after we call TextureLayer::ClearClient().
-    EXPECT_EQ(layer_tree_host()->SourceFrameNumber(), 4);
+    EXPECT_EQ(source_frame_number_, 3);
     released_ = true;
     EndTest();
   }
@@ -1882,6 +1895,7 @@
 
   int step_ = 0;
   int verified_frames_ = 0;
+  int source_frame_number_ = 0;
   bool released_ = false;
   viz::SharedBitmapId id_;
   SharedBitmapIdRegistration registration_;
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 71a4179..6f2056a 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -2737,7 +2737,7 @@
     gfx::Vector2dF visibility_offset = layer_start - layer_end;
     visibility_offset.Scale(device_scale_factor / visibility_offset.Length());
     gfx::PointF visibility_point = layer_end + visibility_offset;
-    if (visibility_point.x() <= 0)
+    if (visibility_point.x() < 0)
       visibility_point.set_x(visibility_point.x() + device_scale_factor);
     visibility_point =
         MathUtil::MapPoint(screen_space_transform, visibility_point, &clipped);
diff --git a/cc/trees/layer_tree_impl_unittest.cc b/cc/trees/layer_tree_impl_unittest.cc
index 68bcca35..baa70f4 100644
--- a/cc/trees/layer_tree_impl_unittest.cc
+++ b/cc/trees/layer_tree_impl_unittest.cc
@@ -2174,6 +2174,40 @@
   EXPECT_EQ(gfx::SelectionBound(), output.end);
 }
 
+TEST_F(LayerTreeImplTest, SelectionBoundsForCaretLayer) {
+  LayerImpl* root = root_layer();
+  root->SetDrawsContent(true);
+  root->SetBounds(gfx::Size(100, 100));
+  host_impl().active_tree()->SetDeviceViewportRect(gfx::Rect(root->bounds()));
+
+  gfx::Vector2dF caret_layer_offset(10, 20);
+  LayerImpl* caret_layer = AddLayer<LayerImpl>();
+  caret_layer->SetBounds(gfx::Size(1, 16));
+  caret_layer->SetDrawsContent(true);
+  CopyProperties(root, caret_layer);
+  caret_layer->SetOffsetToTransformParent(caret_layer_offset);
+
+  UpdateDrawProperties(host_impl().active_tree());
+
+  LayerSelection input;
+  input.start.type = gfx::SelectionBound::CENTER;
+  input.start.edge_start = gfx::Point(0, 0);
+  input.start.edge_end = gfx::Point(0, 16);
+  input.start.layer_id = caret_layer->id();
+  input.end = input.start;
+  host_impl().active_tree()->RegisterSelection(input);
+
+  viz::Selection<gfx::SelectionBound> output;
+  host_impl().active_tree()->GetViewportSelection(&output);
+  EXPECT_EQ(gfx::SelectionBound::CENTER, output.start.type());
+  EXPECT_EQ(gfx::PointF(10, 20), output.start.edge_start());
+  EXPECT_EQ(gfx::PointF(10, 36), output.start.edge_end());
+  EXPECT_EQ(gfx::PointF(10, 20), output.start.visible_edge_start());
+  EXPECT_EQ(gfx::PointF(10, 36), output.start.visible_edge_end());
+  EXPECT_TRUE(output.start.visible());
+  EXPECT_EQ(output.end, output.start);
+}
+
 TEST_F(LayerTreeImplTest, NumLayersTestOne) {
   // Root is created by the test harness.
   EXPECT_EQ(1u, host_impl().active_tree()->NumLayers());
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 4b2632d..63c96c71 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1105,8 +1105,6 @@
     ]
 
     configs += [ "//build/config/compiler:wexit_time_destructors" ]
-    configs -= [ "//build/config/compiler:thinlto_optimize_default" ]
-    configs += [ "//build/config/compiler:thinlto_optimize_max" ]
   }
 
   mac_framework_bundle("chrome_framework") {
@@ -1122,8 +1120,8 @@
     if (is_chrome_branded) {
       framework_contents += [ "Default Apps" ]
       if (enable_keystone_registration_framework) {
-        framework_contents +=
-            [ "Frameworks" ]  # For KeystoneRegistration.framework.
+        # For KeystoneRegistration.framework.
+        framework_contents += [ "Frameworks" ]
       }
     }
 
@@ -1132,6 +1130,8 @@
     }
 
     configs += [ "//build/config/compiler:wexit_time_destructors" ]
+    configs -= [ "//build/config/compiler:thinlto_optimize_default" ]
+    configs += [ "//build/config/compiler:thinlto_optimize_max" ]
 
     info_plist_target = ":chrome_framework_plist"
     extra_substitutions = [
diff --git a/chrome/VERSION b/chrome/VERSION
index e15696c..6ea3085 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=99
 MINOR=0
-BUILD=4775
+BUILD=4780
 PATCH=0
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 01a7883..25d5b576 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -81,6 +81,7 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesChrome.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesFactoryChrome.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDialogButton.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantFeedbackUtilChrome.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantInfoPageUtilChrome.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantInfoPopup.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantKeyboardCoordinator.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
index cf8939c..6617051 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
@@ -10,13 +10,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
-import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
-import org.chromium.chrome.browser.feedback.ScreenshotMode;
 import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.ui.base.ActivityKeyboardVisibilityDelegate;
@@ -28,28 +23,22 @@
  * sub-components and shutting down the Autofill Assistant.
  */
 public class AssistantCoordinator {
-    public static final String FEEDBACK_CATEGORY_TAG =
-            "com.android.chrome.USER_INITIATED_FEEDBACK_REPORT_AUTOFILL_ASSISTANT";
-
     private final Activity mActivity;
 
     private final AssistantModel mModel;
     private AssistantBottomBarCoordinator mBottomBarCoordinator;
     private final AssistantKeyboardCoordinator mKeyboardCoordinator;
     private final AssistantOverlayCoordinator mOverlayCoordinator;
-    private final Supplier<Tab> mCurrentTabSupplier;
 
     AssistantCoordinator(Activity activity, BottomSheetController controller,
             @Nullable AssistantTabObscuringUtil tabObscuringUtil,
             @Nullable AssistantOverlayCoordinator overlayCoordinator,
             AssistantKeyboardCoordinator.Delegate keyboardCoordinatorDelegate,
             @NonNull ActivityKeyboardVisibilityDelegate keyboardDelegate, @NonNull View rootView,
-            @NonNull Supplier<Tab> currentTabSupplier,
             @NonNull BrowserControlsManager browserControlsManager,
             @NonNull ApplicationViewportInsetSupplier applicationBottomInsetProvider,
             AccessibilityUtil accessibilityUtil, AssistantInfoPageUtil infoPageUtil) {
         mActivity = activity;
-        mCurrentTabSupplier = currentTabSupplier;
 
         if (overlayCoordinator != null) {
             mModel = new AssistantModel(overlayCoordinator.getModel());
@@ -94,18 +83,6 @@
         return mKeyboardCoordinator;
     }
 
-    /**
-     * Show the Chrome feedback form.
-     */
-    public void showFeedback(String debugContext, @ScreenshotMode int screenshotMode) {
-        Tab currentTab = mCurrentTabSupplier.get();
-        if (currentTab == null) return;
-        Profile profile = Profile.fromWebContents(currentTab.getWebContents());
-
-        HelpAndFeedbackLauncherImpl.getInstance().showFeedback(mActivity, profile,
-                currentTab.getUrl().getSpec(), FEEDBACK_CATEGORY_TAG, screenshotMode, debugContext);
-    }
-
     public void show() {
         // Simulates native's initialization.
         mModel.setVisible(true);
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantFeedbackUtilChrome.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantFeedbackUtilChrome.java
new file mode 100644
index 0000000..69984a2
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantFeedbackUtilChrome.java
@@ -0,0 +1,28 @@
+// 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.
+
+package org.chromium.chrome.browser.autofill_assistant;
+
+import android.app.Activity;
+
+import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
+import org.chromium.chrome.browser.feedback.ScreenshotMode;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.content_public.browser.WebContents;
+
+/**
+ * Implementation of {@link AssistantFeedbackUtil} for Chrome.
+ */
+public class AssistantFeedbackUtilChrome implements AssistantFeedbackUtil {
+    private static final String FEEDBACK_CATEGORY_TAG =
+            "com.android.chrome.USER_INITIATED_FEEDBACK_REPORT_AUTOFILL_ASSISTANT";
+
+    @Override
+    public void showFeedback(Activity activity, WebContents webContents,
+            @ScreenshotMode int screenshotMode, String debugContext) {
+        HelpAndFeedbackLauncherImpl.getInstance().showFeedback(activity,
+                Profile.fromWebContents(webContents), webContents.getVisibleUrl().getSpec(),
+                FEEDBACK_CATEGORY_TAG, screenshotMode, debugContext);
+    }
+}
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java
index 4c9b181..8c9e434 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java
@@ -38,4 +38,9 @@
     default AssistantInfoPageUtil getInfoPageUtil() {
         return new AssistantInfoPageUtilChrome();
     }
+
+    @Override
+    default AssistantFeedbackUtil getFeedbackUtil() {
+        return new AssistantFeedbackUtilChrome();
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index 3eea6504..2745e20 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -24,7 +24,6 @@
 import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
-import org.chromium.chrome.browser.feedback.ScreenshotMode;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabUtils;
@@ -63,6 +62,7 @@
     private WebContents mWebContents;
 
     private final AssistantSnackbarFactory mSnackbarFactory;
+    private final AssistantFeedbackUtil mFeedbackUtil;
     @Nullable
     private AssistantSnackbar mSnackbar;
 
@@ -143,11 +143,12 @@
         @Nullable
         AssistantTabObscuringUtil tabObscuringUtil =
                 dependencies.getTabObscuringUtilOrNull(activity.getWindowAndroid());
+        mFeedbackUtil = dependencies.getFeedbackUtil();
 
         mCoordinator = new AssistantCoordinator(activity, controller, tabObscuringUtil,
                 overlayCoordinator, this::safeNativeOnKeyboardVisibilityChanged,
                 activity.getWindowAndroid().getKeyboardDelegate(), rootView.get(),
-                activity.getActivityTabProvider(), activity.getBrowserControlsManager(),
+                activity.getBrowserControlsManager(),
                 activity.getWindowAndroid().getApplicationBottomInsetProvider(),
                 dependencies.getAccessibilityUtil(), dependencies.getInfoPageUtil());
         mActivityTabObserver = new ActivityTabProvider.ActivityTabTabObserver(
@@ -321,9 +322,12 @@
         mCoordinator.getBottomBarCoordinator().collapse();
     }
 
+    /**
+     * Shows a feedback form.
+     */
     @CalledByNative
-    private void showFeedback(String debugContext, @ScreenshotMode int screenshotMode) {
-        mCoordinator.showFeedback(debugContext, screenshotMode);
+    private void showFeedback(String debugContext, /* @ScreenshotMode */ int screenshotMode) {
+        mFeedbackUtil.showFeedback(mActivity, mWebContents, screenshotMode, debugContext);
     }
 
     @CalledByNative
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java
index 783b7729..83b2a847 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java
@@ -9,12 +9,9 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.chrome.browser.autofill_assistant.AssistantCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.AssistantDependencies;
-import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiController;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
-import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
 import org.chromium.chrome.browser.tab.TabUtils;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.KeyboardVisibilityDelegate;
@@ -59,11 +56,8 @@
 
                     @Override
                     public void onFeedbackButtonClicked() {
-                        HelpAndFeedbackLauncherImpl.getInstance().showFeedback(
-                                TabUtils.getActivity(TabUtils.fromWebContents(webContents)),
-                                AutofillAssistantUiController.getProfile(),
-                                webContents.getVisibleUrl().getSpec(),
-                                AssistantCoordinator.FEEDBACK_CATEGORY_TAG);
+                        dependencies.getFeedbackUtil().showFeedback(dependencies.getActivity(),
+                                webContents, /* screenshotMode */ 0, /* debugContext */ null);
                     }
                 };
 
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
index 5e7c266..cbe146b 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
@@ -115,7 +115,6 @@
                     /* keyboardCoordinatorDelegate= */ null,
                     getActivity().getWindowAndroid().getKeyboardDelegate(),
                     getActivity().getCompositorViewHolderForTesting(),
-                    getActivity().getActivityTabProvider(),
                     getActivity().getBrowserControlsManager(),
                     getActivity().getWindowAndroid().getApplicationBottomInsetProvider(),
                     staticDependencies.getAccessibilityUtil(),
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependencyUtilsChrome.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependencyUtilsChrome.java
index 4982fd1..096b5b0 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependencyUtilsChrome.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependencyUtilsChrome.java
@@ -13,6 +13,8 @@
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.IntentHandler.ExternalAppId;
 import org.chromium.chrome.browser.flags.ActivityType;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.signin.services.UnifiedConsentServiceBridge;
 
 /**
  * Chrome specific dependency methods used by the Autofill Assistant outside of it's module.
@@ -49,5 +51,10 @@
                 && AssistantFeatures.AUTOFILL_ASSISTANT_DIRECT_ACTIONS.isEnabled();
     }
 
+    public static boolean isMakeSearchesAndBrowsingBetterSettingEnabled() {
+        return UnifiedConsentServiceBridge.isUrlKeyedAnonymizedDataCollectionEnabled(
+                Profile.getLastUsedRegularProfile());
+    }
+
     private AssistantDependencyUtilsChrome() {}
 }
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantFeedbackUtil.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantFeedbackUtil.java
new file mode 100644
index 0000000..4f5fa381
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantFeedbackUtil.java
@@ -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.
+
+package org.chromium.chrome.browser.autofill_assistant;
+
+import android.app.Activity;
+
+import org.chromium.content_public.browser.WebContents;
+
+/**
+ * Utility class for showing feedback forms. Implementations might differ depending on where
+ * Autofill Assistant is running (e.g. WebLayer, Chrome).
+ */
+public interface AssistantFeedbackUtil {
+    /**
+     * Shows a feedback form to the user.
+     */
+    void showFeedback(Activity activity, WebContents webContents,
+            /* @ScreenshotMode */ int screenshotMode, String debugContext);
+}
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantIsMsbbEnabledFunction.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantIsMsbbEnabledFunction.java
new file mode 100644
index 0000000..5a98bb7
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantIsMsbbEnabledFunction.java
@@ -0,0 +1,12 @@
+// 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.
+
+package org.chromium.chrome.browser.autofill_assistant;
+
+import org.chromium.base.supplier.BooleanSupplier;
+
+/**
+ * Determines whether 'Make Searches and Browsing Better' is enabled.
+ */
+public interface AssistantIsMsbbEnabledFunction extends BooleanSupplier {}
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java
index d55a75b8..74aeb527 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java
@@ -28,4 +28,6 @@
 
     @CalledByNative
     AssistantInfoPageUtil getInfoPageUtil();
+
+    AssistantFeedbackUtil getFeedbackUtil();
 }
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTabHelper.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTabHelper.java
index 36c955e7..7e454a1 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTabHelper.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTabHelper.java
@@ -17,8 +17,9 @@
      * the tab as observer and connect to its native counterpart in order to fulfill startup
      * requests from either side.
      */
-    public static void createForTab(Tab tab, AssistantIsGsaFunction isGsaFunction) {
-        Starter starter = new Starter(tab, isGsaFunction);
+    public static void createForTab(Tab tab, AssistantIsGsaFunction isGsaFunction,
+            AssistantIsMsbbEnabledFunction isMsbbEnabledFunction) {
+        Starter starter = new Starter(tab, isGsaFunction, isMsbbEnabledFunction);
         tab.addObserver(starter);
         tab.getUserDataHost().setUserData(USER_DATA_KEY, starter);
     }
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/Starter.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/Starter.java
index 096fbe6..234d962 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/Starter.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/Starter.java
@@ -12,8 +12,6 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.autofill_assistant.metrics.FeatureModuleInstallation;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.signin.services.UnifiedConsentServiceBridge;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabUtils;
@@ -33,6 +31,7 @@
     private final Tab mTab;
 
     private final AssistantIsGsaFunction mIsGsaFunction;
+    private final AssistantIsMsbbEnabledFunction mIsMsbbEnabledFunction;
 
     /**
      * The WebContents associated with the Tab which this starter is monitoring, unless detached.
@@ -65,9 +64,11 @@
      *
      * This will wait for dependencies to become available and then create the native-side starter.
      */
-    public Starter(Tab tab, AssistantIsGsaFunction isGsaFunction) {
+    public Starter(Tab tab, AssistantIsGsaFunction isGsaFunction,
+            AssistantIsMsbbEnabledFunction isMsbbEnabledFunction) {
         mTab = tab;
         mIsGsaFunction = isGsaFunction;
+        mIsMsbbEnabledFunction = isMsbbEnabledFunction;
         detectWebContentsChange(tab);
     }
 
@@ -273,10 +274,8 @@
     }
 
     @CalledByNative
-    static boolean getMakeSearchesAndBrowsingBetterSettingEnabled() {
-        // TODO(arbesser): call this from native directly.
-        return UnifiedConsentServiceBridge.isUrlKeyedAnonymizedDataCollectionEnabled(
-                Profile.getLastUsedRegularProfile());
+    private boolean getMakeSearchesAndBrowsingBetterSettingEnabled() {
+        return mIsMsbbEnabledFunction.getAsBoolean();
     }
 
     private AutofillAssistantModuleEntry getModuleOrThrow() {
diff --git a/chrome/android/features/autofill_assistant/public/java_sources.gni b/chrome/android/features/autofill_assistant/public/java_sources.gni
index 4e99ba9d..4ad5a529 100644
--- a/chrome/android/features/autofill_assistant/public/java_sources.gni
+++ b/chrome/android/features/autofill_assistant/public/java_sources.gni
@@ -7,8 +7,10 @@
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesFactory.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependencyUtilsChrome.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantFeatures.java",
+  "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantFeedbackUtil.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantInfoPageUtil.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantIsGsaFunction.java",
+  "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantIsMsbbEnabledFunction.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingHelper.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantSnackbar.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantSnackbarFactory.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRow.java
index a8f3988..dfbc97f5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRow.java
@@ -29,6 +29,7 @@
 import org.chromium.components.payments.CurrencyFormatter;
 import org.chromium.ui.widget.ChipView;
 
+import java.util.Arrays;
 import java.util.Locale;
 
 /** A row view that shows shopping info in the bookmarks UI. */
@@ -74,16 +75,35 @@
     BookmarkItem setBookmarkId(BookmarkId bookmarkId, @Location int location) {
         BookmarkItem bookmarkItem = super.setBookmarkId(bookmarkId, location);
         PowerBookmarkMeta meta = mBookmarkModel.getPowerBookmarkMeta(bookmarkId);
+        assert meta != null;
+
         // TODO(crbug.com/1243383): Pull price updates once they're available.
-        ProductPrice currentPrice = meta.getShoppingSpecifics().getCurrentPrice();
+        ProductPrice originalPrice = meta.getShoppingSpecifics().getCurrentPrice();
         mSubscription = PowerBookmarkUtils.createCommerceSubscriptionForPowerBookmarkMeta(meta);
         mCurrencyFormatter =
-                new CurrencyFormatter(currentPrice.getCurrencyCode(), Locale.getDefault());
+                new CurrencyFormatter(originalPrice.getCurrencyCode(), Locale.getDefault());
 
         boolean mIsPriceTrackingEnabled =
                 meta != null && meta.getShoppingSpecifics().getIsPriceTracked();
         initPriceTrackingUI(meta.getLeadImage().getUrl(), mIsPriceTrackingEnabled,
-                currentPrice.getAmountMicros(), currentPrice.getAmountMicros());
+                originalPrice.getAmountMicros(), originalPrice.getAmountMicros());
+        // Request an updated price then push updates to the UI.
+        mBookmarkModel.getUpdatedProductPrices(
+                Arrays.asList(bookmarkId), (id, url, updatedPrice) -> {
+                    if (!mBookmarkId.equals(id)
+                            || !originalPrice.getCurrencyCode().equals(
+                                    updatedPrice.getCurrencyCode())) {
+                        return;
+                    }
+
+                    if (updatedPrice.getAmountMicros() > originalPrice.getAmountMicros()) {
+                        PowerBookmarkUtils.updatePriceForBookmarkId(
+                                mBookmarkModel, bookmarkId, updatedPrice);
+                    }
+
+                    setPriceInfoChip(
+                            originalPrice.getAmountMicros(), updatedPrice.getAmountMicros());
+                });
         return bookmarkItem;
     }
 
@@ -119,7 +139,8 @@
     /** Sets up the chip that displays product price information. */
     private void setPriceInfoChip(long originalPrice, long currentPrice) {
         String formattedCurrentPrice = getFormattedCurrencyStringForPrice(currentPrice);
-        if (originalPrice == currentPrice) {
+        // Note: chips should only be shown for price drops
+        if (originalPrice <= currentPrice) {
             TextView textView = new TextView(getContext(), null);
             ApiCompatibilityUtils.setTextAppearance(
                     textView, R.styleable.ChipView_primaryTextAppearance);
@@ -128,16 +149,16 @@
         } else {
             ChipView cv = new ChipView(getContext(), null);
             cv.setBorder(0, Color.TRANSPARENT);
-            cv.setBackgroundColor(ApiCompatibilityUtils.getColor(getResources(),
-                    originalPrice > currentPrice ? R.color.google_green_300
-                                                 : R.color.google_red_300));
+            cv.setBackgroundColor(ApiCompatibilityUtils.getColor(
+                    getResources(), R.color.price_drop_annotation_bg_color));
 
             // Primary text displays the current price.
             TextView primaryText = cv.getPrimaryTextView();
+            ApiCompatibilityUtils.setTextAppearance(
+                    primaryText, R.styleable.ChipView_primaryTextAppearance);
             primaryText.setText(formattedCurrentPrice);
-            primaryText.setTextColor(ApiCompatibilityUtils.getColor(getResources(),
-                    originalPrice > currentPrice ? R.color.google_green_600
-                                                 : R.color.google_red_600));
+            primaryText.setTextColor(
+                    ApiCompatibilityUtils.getColor(getResources(), R.color.default_green));
 
             // Secondary text displays the original price with a strikethrough.
             TextView secondaryText = cv.getSecondaryTextView();
@@ -145,7 +166,7 @@
             secondaryText.setPaintFlags(
                     secondaryText.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
             ApiCompatibilityUtils.setTextAppearance(
-                    secondaryText, R.style.TextAppearance_TextSmall_Secondary);
+                    secondaryText, R.styleable.ChipView_primaryTextAppearance);
             setCustomContent(cv);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java
index 0c0b3f6..b87d612 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java
@@ -16,6 +16,7 @@
 import org.chromium.chrome.browser.commerce.shopping_list.ShoppingDataProviderBridge;
 import org.chromium.chrome.browser.power_bookmarks.PowerBookmarkMeta;
 import org.chromium.chrome.browser.power_bookmarks.PowerBookmarkType;
+import org.chromium.chrome.browser.power_bookmarks.ProductPrice;
 import org.chromium.chrome.browser.power_bookmarks.ShoppingSpecifics;
 import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
 import org.chromium.chrome.browser.subscriptions.CommerceSubscription.CommerceSubscriptionType;
@@ -150,6 +151,31 @@
     }
 
     /**
+     * Update to the given price for the given bookmark id.
+     *
+     * @param bookmarkBridge Used to read/write bookmark data.
+     * @param bookmarkId The bookmark id to update.
+     * @param price The price to update to.
+     */
+    public static void updatePriceForBookmarkId(@NonNull BookmarkBridge bookmarkBridge,
+            @NonNull BookmarkId bookmarkId,
+            @NonNull org.chromium.components.commerce.PriceTracking.ProductPrice price) {
+        PowerBookmarkMeta meta = bookmarkBridge.getPowerBookmarkMeta(bookmarkId);
+        if (meta == null) return;
+        ProductPrice newPrice = ProductPrice.newBuilder()
+                                        .setCurrencyCode(price.getCurrencyCode())
+                                        .setAmountMicros(price.getAmountMicros())
+                                        .build();
+        bookmarkBridge.setPowerBookmarkMeta(bookmarkId,
+                PowerBookmarkMeta.newBuilder(meta)
+                        .setShoppingSpecifics(
+                                ShoppingSpecifics.newBuilder(meta.getShoppingSpecifics())
+                                        .setCurrentPrice(newPrice)
+                                        .build())
+                        .build());
+    }
+
+    /**
      * Gets the power bookmark associated with the given tab.
      * @param bookmarkBridge The {@link BookmarkBridge} to retrieve bookmark info.
      * @param tab The current {@link Tab} to check.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java
index 8292271..afe5cd5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java
@@ -18,7 +18,6 @@
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
-import org.chromium.chrome.browser.share.LensUtils;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.ui.text.SpanApplier;
@@ -159,9 +158,7 @@
             R.string.contextmenu_open_image_in_ephemeral_tab, // Item.OPEN_IMAGE_IN_EPHEMERAL_TAB:
             R.string.contextmenu_copy_image, // Item.COPY_IMAGE:
             R.string.contextmenu_search_web_for_image, // Item.SEARCH_BY_IMAGE:
-            LensUtils.useSearchImageWithGoogleLensItemName()
-                    ? R.string.contextmenu_search_image_with_google_lens
-                    : R.string.contextmenu_search_with_google_lens, // Item.SEARCH_WITH_GOOGLE_LENS:
+            R.string.contextmenu_search_image_with_google_lens, // Item.SEARCH_WITH_GOOGLE_LENS:
             R.string.contextmenu_shop_image_with_google_lens, // Item.SHOP_IMAGE_WITH_GOOGLE_LENS:
             R.string.contextmenu_share_image, // Item.SHARE_IMAGE
             0, // Item.DIRECT_SHARE_IMAGE is not handled by this mapping.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerLauncher.java
index 3150a89..4199ef0a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerLauncher.java
@@ -29,7 +29,8 @@
     public static void showPasswordSettings(
             Activity activity, @ManagePasswordsReferrer int referrer) {
         SyncService syncService = SyncService.get();
-        if (PasswordManagerHelper.hasChosenToSyncPasswordsWithNoCustomPassphrase(syncService)
+        if (syncService.isEngineInitialized()
+                && PasswordManagerHelper.hasChosenToSyncPasswordsWithNoCustomPassphrase(syncService)
                 && ChromeFeatureList.isEnabled(ChromeFeatureList.PASSWORD_SCRIPTS_FETCHING)) {
             PasswordScriptsFetcherBridge.prewarmCache();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
index d913605..29beb94 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
@@ -16,7 +16,9 @@
 import android.view.MenuItem;
 import android.view.View;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.widget.Toolbar;
 import androidx.fragment.app.FragmentManager;
@@ -48,9 +50,12 @@
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.prefs.PrefService;
 import org.chromium.components.signin.base.CoreAccountInfo;
+import org.chromium.components.sync.PassphraseType;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.ui.text.SpanApplier;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Locale;
 
 /**
@@ -60,6 +65,15 @@
 public class PasswordSettings extends PreferenceFragmentCompat
         implements PasswordManagerHandler.PasswordListObserver,
                    Preference.OnPreferenceClickListener, SyncService.SyncStateChangedListener {
+    @IntDef({TrustedVaultBannerState.NOT_SHOWN, TrustedVaultBannerState.OFFER_OPT_IN,
+            TrustedVaultBannerState.OPTED_IN})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface TrustedVaultBannerState {
+        int NOT_SHOWN = 0;
+        int OFFER_OPT_IN = 1;
+        int OPTED_IN = 2;
+    }
+
     // Keys for name/password dictionaries.
     public static final String PASSWORD_LIST_URL = "url";
     public static final String PASSWORD_LIST_NAME = "name";
@@ -74,7 +88,7 @@
     public static final String PREF_SAVE_PASSWORDS_SWITCH = "save_passwords_switch";
     public static final String PREF_AUTOSIGNIN_SWITCH = "autosignin_switch";
     public static final String PREF_CHECK_PASSWORDS = "check_passwords";
-    public static final String PREF_TRUSTED_VAULT_OPT_IN = "trusted_vault_opt_in";
+    public static final String PREF_TRUSTED_VAULT_BANNER = "trusted_vault_banner";
     public static final String PREF_KEY_MANAGE_ACCOUNT_LINK = "manage_account_link";
 
     // A PasswordEntryViewer receives a boolean value with this key. If set true, the the entry was
@@ -89,7 +103,7 @@
     private static final int ORDER_SWITCH = 0;
     private static final int ORDER_AUTO_SIGNIN_CHECKBOX = 1;
     private static final int ORDER_CHECK_PASSWORDS = 2;
-    private static final int ORDER_TRUSTED_VAULT_OPT_IN = 3;
+    private static final int ORDER_TRUSTED_VAULT_BANNER = 3;
     private static final int ORDER_MANAGE_ACCOUNT_LINK = 4;
     private static final int ORDER_SECURITY_KEY = 5;
     private static final int ORDER_SAVED_PASSWORDS = 6;
@@ -102,7 +116,8 @@
 
     private boolean mNoPasswords;
     private boolean mNoPasswordExceptions;
-    private boolean mShouldShowTrustedVaultOptIn;
+    private @TrustedVaultBannerState int mTrustedVaultBannerState =
+            TrustedVaultBannerState.NOT_SHOWN;
 
     private MenuItem mHelpItem;
     private MenuItem mSearchItem;
@@ -112,7 +127,7 @@
     private ChromeSwitchPreference mSavePasswordsSwitch;
     private ChromeSwitchPreference mAutoSignInSwitch;
     private ChromeBasePreference mCheckPasswords;
-    private ChromeBasePreference mTrustedVaultOptIn;
+    private ChromeBasePreference mTrustedVaultBanner;
     private TextMessagePreference mEmptyView;
     private boolean mSearchRecorded;
     private Menu mMenu;
@@ -185,8 +200,7 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mPasswordCheck = PasswordCheckFactory.getOrCreate(new SettingsLauncherImpl());
-        mShouldShowTrustedVaultOptIn =
-                SyncService.get() != null && SyncService.get().shouldOfferTrustedVaultOptIn();
+        computeTrustedVaultBannerState();
     }
 
     @Override
@@ -277,15 +291,26 @@
         mNoPasswords = false;
         mNoPasswordExceptions = false;
         getPreferenceScreen().removeAll();
-        if (mSearchQuery == null) {
-            createSavePasswordsSwitch();
-            createAutoSignInCheckbox();
-            if (mPasswordCheck != null) {
-                createCheckPasswords();
-            }
-            if (mShouldShowTrustedVaultOptIn) {
-                createTrustedVaultOptIn();
-            }
+        if (mSearchQuery != null) {
+            // Only the filtered passwords and exceptions should be shown.
+            PasswordManagerHandlerProvider.getInstance()
+                    .getPasswordManagerHandler()
+                    .updatePasswordLists();
+            return;
+        }
+
+        createSavePasswordsSwitch();
+        createAutoSignInCheckbox();
+        if (mPasswordCheck != null) {
+            createCheckPasswords();
+        }
+
+        if (mTrustedVaultBannerState == TrustedVaultBannerState.OPTED_IN) {
+            createTrustedVaultBanner(R.string.android_trusted_vault_banner_sub_label_opted_in,
+                    this::openTrustedVaultInfoPage);
+        } else if (mTrustedVaultBannerState == TrustedVaultBannerState.OFFER_OPT_IN) {
+            createTrustedVaultBanner(R.string.android_trusted_vault_banner_sub_label_offer_opt_in,
+                    this::openTrustedVaultOptInDialog);
         }
         PasswordManagerHandlerProvider.getInstance()
                 .getPasswordManagerHandler()
@@ -555,25 +580,22 @@
         getPreferenceScreen().addPreference(mCheckPasswords);
     }
 
-    private void createTrustedVaultOptIn() {
-        mTrustedVaultOptIn = new ChromeBasePreference(getStyledContext());
-        mTrustedVaultOptIn.setKey(PREF_TRUSTED_VAULT_OPT_IN);
-        mTrustedVaultOptIn.setTitle(R.string.android_trusted_vault_opt_in_label);
-        mTrustedVaultOptIn.setOrder(ORDER_TRUSTED_VAULT_OPT_IN);
-        mTrustedVaultOptIn.setSummary(R.string.android_trusted_vault_opt_in_sub_label);
-        mTrustedVaultOptIn.setOnPreferenceClickListener(preference -> {
-            assert SyncService.get() != null;
-            CoreAccountInfo accountInfo = SyncService.get().getAccountInfo();
-            assert accountInfo != null;
-            SyncSettingsUtils.openTrustedVaultOptInDialog(
-                    this, accountInfo, REQUEST_CODE_TRUSTED_VAULT_OPT_IN);
-            // Return true to notify the click was handled.
-            return true;
-        });
-        getPreferenceScreen().addPreference(mTrustedVaultOptIn);
+    private void createTrustedVaultBanner(
+            @StringRes int subLabel, Preference.OnPreferenceClickListener listener) {
+        mTrustedVaultBanner = new ChromeBasePreference(getStyledContext());
+        mTrustedVaultBanner.setKey(PREF_TRUSTED_VAULT_BANNER);
+        mTrustedVaultBanner.setTitle(R.string.android_trusted_vault_banner_label);
+        mTrustedVaultBanner.setOrder(ORDER_TRUSTED_VAULT_BANNER);
+        mTrustedVaultBanner.setSummary(subLabel);
+        mTrustedVaultBanner.setOnPreferenceClickListener(listener);
+        getPreferenceScreen().addPreference(mTrustedVaultBanner);
     }
 
     private void displayManageAccountLink() {
+        SyncService syncService = SyncService.get();
+        if (syncService == null || !syncService.isEngineInitialized()) {
+            return;
+        }
         if (!PasswordManagerHelper.isSyncingPasswordsWithNoCustomPassphrase(SyncService.get())) {
             return;
         }
@@ -610,13 +632,55 @@
 
     @Override
     public void syncStateChanged() {
-        boolean shouldShowTrustedVaultOptIn = SyncService.get().shouldOfferTrustedVaultOptIn();
-        if (mShouldShowTrustedVaultOptIn != shouldShowTrustedVaultOptIn) {
-            mShouldShowTrustedVaultOptIn = shouldShowTrustedVaultOptIn;
+        final @TrustedVaultBannerState int oldTrustedVaultBannerState = mTrustedVaultBannerState;
+        computeTrustedVaultBannerState();
+        if (oldTrustedVaultBannerState != mTrustedVaultBannerState) {
             rebuildPasswordLists();
         }
     }
 
+    private void computeTrustedVaultBannerState() {
+        final SyncService syncService = SyncService.get();
+        if (syncService == null) {
+            mTrustedVaultBannerState = TrustedVaultBannerState.NOT_SHOWN;
+            return;
+        }
+        if (!syncService.isEngineInitialized()) {
+            // Can't call getPassphraseType() yet.
+            mTrustedVaultBannerState = TrustedVaultBannerState.NOT_SHOWN;
+            return;
+        }
+        if (syncService.getPassphraseType() == PassphraseType.TRUSTED_VAULT_PASSPHRASE) {
+            mTrustedVaultBannerState = TrustedVaultBannerState.OPTED_IN;
+            return;
+        }
+        if (syncService.shouldOfferTrustedVaultOptIn()) {
+            mTrustedVaultBannerState = TrustedVaultBannerState.OFFER_OPT_IN;
+            return;
+        }
+        mTrustedVaultBannerState = TrustedVaultBannerState.NOT_SHOWN;
+    }
+
+    private boolean openTrustedVaultOptInDialog(Preference unused) {
+        assert SyncService.get() != null;
+        CoreAccountInfo accountInfo = SyncService.get().getAccountInfo();
+        assert accountInfo != null;
+        SyncSettingsUtils.openTrustedVaultOptInDialog(
+                this, accountInfo, REQUEST_CODE_TRUSTED_VAULT_OPT_IN);
+        // Return true to notify the click was handled.
+        return true;
+    }
+
+    private boolean openTrustedVaultInfoPage(Preference unused) {
+        // TODO(crbug.com/1202088): Use correct help center URL here.
+        Intent intent =
+                new Intent(Intent.ACTION_VIEW, Uri.parse(PasswordUIView.getAccountDashboardURL()));
+        intent.setPackage(getActivity().getPackageName());
+        getActivity().startActivity(intent);
+        // Return true to notify the click was handled.
+        return true;
+    }
+
     @VisibleForTesting
     Menu getMenuForTesting() {
         return mMenu;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java
index d7514a6c..bcb6418 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java
@@ -30,8 +30,6 @@
     private static final String LENS_SHOPPING_URL_PATTERNS_FEATURE_PARAM_NAME =
             "shoppingUrlPatterns";
     private static final String LOG_UKM_PARAM_NAME = "logUkm";
-    private static final String USE_SEARCH_IMAGE_WITH_GOOGLE_LENS_ITEM_NAME_PARAM_NAME =
-            "useSearchImageWithGoogleLensItemName";
     private static final String ENABLE_ON_TABLET_PARAM_NAME = "enableContextMenuSearchOnTablet";
     private static final String DISABLE_ON_INCOGNITO_PARAM_NAME = "disableOnIncognito";
     private static final String ORDER_SHARE_IMAGE_BEFORE_LENS_PARAM_NAME =
@@ -382,13 +380,4 @@
 
         return false;
     }
-
-    /**
-     * Check if experiment to change Lens context menu item name is enabled.
-     */
-    public static boolean useSearchImageWithGoogleLensItemName() {
-        return ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
-                ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS,
-                USE_SEARCH_IMAGE_WITH_GOOGLE_LENS_ITEM_NAME_PARAM_NAME, true);
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java
index 761dc7e..9262692c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java
@@ -38,7 +38,8 @@
         TabBrowserControlsConstraintsHelper.createForTab(tab);
         ContinuousSearchTabHelper.createForTab(tab);
         if (ReaderModeManager.isEnabled()) ReaderModeManager.createForTab(tab);
-        AutofillAssistantTabHelper.createForTab(tab, AssistantDependencyUtilsChrome::isGsa);
+        AutofillAssistantTabHelper.createForTab(tab, AssistantDependencyUtilsChrome::isGsa,
+                AssistantDependencyUtilsChrome::isMakeSearchesAndBrowsingBetterSettingEnabled);
 
         // The following will start prefetching data for the price drops feature, so
         // we should only do it if the user is eligible for the feature (e.g. has sync enabled).
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
index 5b63bafb..5e0f633 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
@@ -518,6 +518,7 @@
      *                     loaded asynchronously.
      */
     public void restoreTabs(boolean setActiveTab) {
+        Log.i(TAG, "restoreTabs starts, setActiveTab = " + setActiveTab);
         if (setActiveTab) {
             // Restore and select the active tab, which is first in the restore list.
             // If the active tab can't be restored, restore and select another tab. Otherwise, the
@@ -532,6 +533,7 @@
             }
         }
         loadNextTab();
+        Log.i(TAG, "restoreTabs ends, setActiveTab = " + setActiveTab);
     }
 
     /**
@@ -593,11 +595,14 @@
             int restoredTabId = SharedPreferencesManager.getInstance().readInt(
                     ChromePreferenceKeys.TABMODEL_ACTIVE_TAB_ID, Tab.INVALID_TAB_ID);
             if (restoredTabId == tabToRestore.id && mPrefetchActiveTabTask != null) {
+                Log.i(TAG, "restoreTab from mPrefetchActiveTabTask");
                 state = mPrefetchActiveTabTask.get();
             } else {
                 // Necessary to do on the UI thread as a last resort.
+                Log.i(TAG, "restoreTab on the UI thread");
                 state = TabStateFileManager.restoreTabState(getStateDirectory(), tabToRestore.id);
             }
+            Log.i(TAG, "restoreTab with state " + state);
 
             restoreTab(tabToRestore, state,
                     maybeRestoreCriticalPersistedTabDataSynchronously(tabToRestore), setAsActive);
@@ -726,6 +731,7 @@
             }
         }
         restoredTabs.put(tabToRestore.originalIndex, tabId);
+        Log.i(TAG, "Done restoreTab of tabId " + tabId);
     }
 
     /**
@@ -1356,9 +1362,11 @@
     }
 
     private void loadNextTab() {
+        Log.i(TAG, "loadNextTab starts");
         if (mDestroyed) return;
 
         if (mTabsToRestore.isEmpty()) {
+            Log.i(TAG, "loadNextTab: mTabsToRestore is Empty");
             mNormalTabsRestored = null;
             mIncognitoTabsRestored = null;
             mLoadInProgress = false;
@@ -1393,10 +1401,12 @@
                     "Loaded tab lists; counts: " + mTabModelSelector.getModel(false).getCount()
                             + "," + mTabModelSelector.getModel(true).getCount());
         } else {
+            Log.i(TAG, "loadNextTab: mTabsToRestore is NOT Empty");
             TabRestoreDetails tabToRestore = mTabsToRestore.removeFirst();
             mTabLoader = new TabLoader(tabToRestore);
             mTabLoader.load();
         }
+        Log.i(TAG, "loadNextTab ends");
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkTest.java
index 2c0c285..7f4953d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkTest.java
@@ -14,6 +14,7 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.ColorInt;
 import androidx.test.filters.MediumTest;
 
 import org.junit.Rule;
@@ -27,27 +28,37 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.FeatureList;
-import org.chromium.base.test.util.DisabledTest;
+import org.chromium.base.test.params.ParameterAnnotations;
+import org.chromium.base.test.params.ParameterSet;
+import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.subscriptions.SubscriptionsManager;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.util.ChromeRenderTestRule;
 import org.chromium.components.image_fetcher.ImageFetcher;
 import org.chromium.components.payments.CurrencyFormatter;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.DummyUiActivityTestCase;
+import org.chromium.ui.test.util.NightModeTestUtils;
+import org.chromium.ui.test.util.NightModeTestUtils.NightModeParams;
 
 import java.io.IOException;
+import java.util.List;
 
 /**
  * Tests for the power bookmark experience.
  */
-@RunWith(ChromeJUnit4ClassRunner.class)
+@RunWith(ParameterizedRunner.class)
+@ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 public class PowerBookmarkTest extends DummyUiActivityTestCase {
+    @ParameterAnnotations.ClassParameter
+    private static List<ParameterSet> sClassParams = new NightModeParams().getParameters();
+
     private static final long CURRENCY_MUTLIPLIER = 1000000;
+    private final @ColorInt int mFakeBgColor;
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
@@ -71,6 +82,13 @@
     private PowerBookmarkShoppingItemRow mPowerBookmarkShoppingItemRow;
     private ViewGroup mContentView;
 
+    public PowerBookmarkTest(boolean nightModeEnabled) {
+        // Sets a fake background color to make the screenshots easier to compare with bare eyes.
+        mFakeBgColor = nightModeEnabled ? Color.BLACK : Color.WHITE;
+        NightModeTestUtils.setUpNightModeForDummyUiActivity(nightModeEnabled);
+        mRenderTestRule.setNightModeEnabled(nightModeEnabled);
+    }
+
     public void setupFeatureOverrides() {
         FeatureList.TestValues testValuesOverride = new FeatureList.TestValues();
         testValuesOverride.addFeatureFlagOverride(ChromeFeatureList.BOOKMARKS_REFRESH, true);
@@ -116,6 +134,7 @@
                             .getLayoutInflater()
                             .inflate(R.layout.power_bookmark_shopping_item_row, mContentView, true)
                             .findViewById(R.id.power_bookmark_shopping_row);
+            mPowerBookmarkShoppingItemRow.setBackgroundColor(mFakeBgColor);
             ((TextView) mPowerBookmarkShoppingItemRow.findViewById(R.id.title))
                     .setText("Test Bookmark");
             ((TextView) mPowerBookmarkShoppingItemRow.findViewById(R.id.description))
@@ -150,7 +169,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    @DisabledTest(message = "https://crbug.com/1279804")
     public void testShoppingPriceDrop() throws IOException {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mPowerBookmarkShoppingItemRow.initPriceTrackingUI("http://foo.com/img", false,
@@ -162,17 +180,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    public void testShoppingPriceIncrease() throws IOException {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mPowerBookmarkShoppingItemRow.initPriceTrackingUI("http://foo.com/img", false,
-                    50 * CURRENCY_MUTLIPLIER, 100 * CURRENCY_MUTLIPLIER);
-        });
-        mRenderTestRule.render(mContentView, "shopping_price_increase");
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"RenderTest"})
     public void testShoppingRebindUI() throws IOException {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mPowerBookmarkShoppingItemRow.initPriceTrackingUI("http://foo.com/img", false,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
index 05b4f89..b8cab80 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
@@ -842,41 +842,9 @@
     @Test
     @SmallTest
     @Feature({"Browser", "ContextMenu"})
-    @CommandLineFlags.Add({"enable-features="
-                    + ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS + "<FakeStudyName",
-            "force-fieldtrials=FakeStudyName/Enabled",
-            "force-fieldtrial-params=FakeStudyName.Enabled:useSearchImageWithGoogleLensItemName/false"})
+    @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS})
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    public void
-    testSearchWithGoogleLensMenuItemName() throws Throwable {
-        Tab tab = mDownloadTestRule.getActivity().getActivityTab();
-
-        LensUtils.setFakePassableLensEnvironmentForTesting(true);
-        ShareHelper.setIgnoreActivityNotFoundExceptionForTesting(true);
-        hardcodeTestImageForSharing(TEST_JPG_IMAGE_FILE_EXTENSION);
-
-        ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImage");
-        Integer[] expectedItems = {R.id.contextmenu_save_image,
-                R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_share_image,
-                R.id.contextmenu_copy_image, R.id.contextmenu_search_with_google_lens};
-        expectedItems = addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems,
-                new Integer[] {R.id.contextmenu_open_image_in_ephemeral_tab});
-        String title = getMenuTitleFromItem(menu, R.id.contextmenu_search_with_google_lens);
-        Assert.assertTrue("Context menu item name should be \'Search with Google Lens\'.",
-                title.startsWith("Search with Google Lens"));
-        assertMenuItemsAreEqual(menu, expectedItems);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Browser", "ContextMenu"})
-    @CommandLineFlags.Add({"enable-features="
-                    + ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS + "<FakeStudyName",
-            "force-fieldtrials=FakeStudyName/Enabled",
-            "force-fieldtrial-params=FakeStudyName.Enabled:useSearchImageWithGoogleLensItemName/true"})
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    public void
-    testSearchImageWithGoogleLensMenuItemName() throws Throwable {
+    public void testSearchImageWithGoogleLensMenuItemName() throws Throwable {
         Tab tab = mDownloadTestRule.getActivity().getActivityTab();
 
         LensUtils.setFakePassableLensEnvironmentForTesting(true);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettingsTest.java
index e719a68..1c285e88 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettingsTest.java
@@ -47,15 +47,16 @@
 import org.chromium.chrome.browser.password_check.PasswordCheckFactory;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.settings.DummySettingsForTest;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsActivityTestRule;
 import org.chromium.chrome.browser.sync.SyncService;
-import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 import org.chromium.components.prefs.PrefService;
 import org.chromium.components.sync.ModelType;
+import org.chromium.components.sync.PassphraseType;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
@@ -67,10 +68,11 @@
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class PasswordSettingsTest {
     @Rule
-    public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
+    public SettingsActivityTestRule<DummySettingsForTest> mDummySettingsActivityTestRule =
+            new SettingsActivityTestRule<>(DummySettingsForTest.class);
 
     @Rule
-    public SettingsActivityTestRule<PasswordSettings> mSettingsActivityTestRule =
+    public SettingsActivityTestRule<PasswordSettings> mPasswordSettingsActivityTestRule =
             new SettingsActivityTestRule<>(PasswordSettings.class);
 
     @Mock
@@ -85,6 +87,17 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         PasswordCheckFactory.setPasswordCheckForTesting(mPasswordCheck);
+
+        // By default sync is off. Tests can override this later.
+        setSyncServiceState(false, false);
+        TestThreadUtils.runOnUiThreadBlocking(() -> SyncService.overrideForTests(mMockSyncService));
+
+        // This initializes the browser, so some tests can do setup before PasswordSettings is
+        // launched. ChromeTabbedActivityTestRule.startMainActivityOnBlankPage() is more commonly
+        // used for this end, but using another settings activity instead makes these tests more
+        // isolated, i.e. avoids exercising unnecessary logic. DummyUiActivityTestCase also won't
+        // fit here, it doesn't initialize enough of the browser.
+        mDummySettingsActivityTestRule.startSettingsActivity(null);
     }
 
     @After
@@ -100,11 +113,12 @@
     @Feature({"Preferences"})
     public void testResetListEmpty() {
         // Load the preferences, they should show the empty list.
-        mTestHelper.startPasswordSettingsFromMainSettings(mSettingsActivityTestRule);
+        mTestHelper.startPasswordSettingsFromMainSettings(mPasswordSettingsActivityTestRule);
         onViewWaiting(withText(R.string.password_settings_title));
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            PasswordSettings savePasswordPreferences = mSettingsActivityTestRule.getFragment();
+            PasswordSettings savePasswordPreferences =
+                    mPasswordSettingsActivityTestRule.getFragment();
             // Emulate an update from PasswordStore. This should not crash.
             savePasswordPreferences.passwordListAvailable(0);
         });
@@ -121,12 +135,12 @@
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> { getPrefService().setBoolean(Pref.CREDENTIALS_ENABLE_SERVICE, true); });
 
-        final SettingsActivity settingsActivity =
-                mTestHelper.startPasswordSettingsFromMainSettings(mSettingsActivityTestRule);
+        final SettingsActivity settingsActivity = mTestHelper.startPasswordSettingsFromMainSettings(
+                mPasswordSettingsActivityTestRule);
         onViewWaiting(withText(R.string.password_settings_title));
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            PasswordSettings savedPasswordPrefs = mSettingsActivityTestRule.getFragment();
+            PasswordSettings savedPasswordPrefs = mPasswordSettingsActivityTestRule.getFragment();
             ChromeSwitchPreference onOffSwitch =
                     (ChromeSwitchPreference) savedPasswordPrefs.findPreference(
                             PasswordSettings.PREF_SAVE_PASSWORDS_SWITCH);
@@ -142,10 +156,10 @@
             getPrefService().setBoolean(Pref.CREDENTIALS_ENABLE_SERVICE, false);
         });
 
-        mTestHelper.startPasswordSettingsFromMainSettings(mSettingsActivityTestRule);
+        mTestHelper.startPasswordSettingsFromMainSettings(mPasswordSettingsActivityTestRule);
         onViewWaiting(withText(R.string.password_settings_title));
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            PasswordSettings savedPasswordPrefs = mSettingsActivityTestRule.getFragment();
+            PasswordSettings savedPasswordPrefs = mPasswordSettingsActivityTestRule.getFragment();
             ChromeSwitchPreference onOffSwitch =
                     (ChromeSwitchPreference) savedPasswordPrefs.findPreference(
                             PasswordSettings.PREF_SAVE_PASSWORDS_SWITCH);
@@ -165,8 +179,8 @@
         // empty.
         mTestHelper.setPasswordSource(
                 new SavedPasswordEntry("https://example.com", "test user", "password"));
-        mTestHelper.startPasswordSettingsFromMainSettings(mSettingsActivityTestRule);
-        PasswordSettings savedPasswordPrefs = mSettingsActivityTestRule.getFragment();
+        mTestHelper.startPasswordSettingsFromMainSettings(mPasswordSettingsActivityTestRule);
+        PasswordSettings savedPasswordPrefs = mPasswordSettingsActivityTestRule.getFragment();
         Assert.assertNull(
                 savedPasswordPrefs.findPreference(PasswordSettings.PREF_KEY_MANAGE_ACCOUNT_LINK));
     }
@@ -183,11 +197,10 @@
         // empty.
         mTestHelper.setPasswordSource(
                 new SavedPasswordEntry("https://example.com", "test user", "password"));
-        overrideSyncService(false, false);
-        mBrowserTestRule.addTestAccountThenSigninAndEnableSync();
+        setSyncServiceState(false, false);
 
-        mTestHelper.startPasswordSettingsFromMainSettings(mSettingsActivityTestRule);
-        PasswordSettings savedPasswordPrefs = mSettingsActivityTestRule.getFragment();
+        mTestHelper.startPasswordSettingsFromMainSettings(mPasswordSettingsActivityTestRule);
+        PasswordSettings savedPasswordPrefs = mPasswordSettingsActivityTestRule.getFragment();
 
         Assert.assertNull(
                 savedPasswordPrefs.findPreference(PasswordSettings.PREF_KEY_MANAGE_ACCOUNT_LINK));
@@ -205,11 +218,10 @@
         // empty.
         mTestHelper.setPasswordSource(
                 new SavedPasswordEntry("https://example.com", "test user", "password"));
-        overrideSyncService(false, true);
-        mBrowserTestRule.addTestAccountThenSigninAndEnableSync();
+        setSyncServiceState(false, true);
 
-        mTestHelper.startPasswordSettingsFromMainSettings(mSettingsActivityTestRule);
-        PasswordSettings savedPasswordPrefs = mSettingsActivityTestRule.getFragment();
+        mTestHelper.startPasswordSettingsFromMainSettings(mPasswordSettingsActivityTestRule);
+        PasswordSettings savedPasswordPrefs = mPasswordSettingsActivityTestRule.getFragment();
 
         Assert.assertNotNull(
                 savedPasswordPrefs.findPreference(PasswordSettings.PREF_KEY_MANAGE_ACCOUNT_LINK));
@@ -227,11 +239,10 @@
         // empty.
         mTestHelper.setPasswordSource(
                 new SavedPasswordEntry("https://example.com", "test user", "password"));
-        overrideSyncService(true, true);
-        mBrowserTestRule.addTestAccountThenSigninAndEnableSync();
+        setSyncServiceState(true, true);
 
-        mTestHelper.startPasswordSettingsFromMainSettings(mSettingsActivityTestRule);
-        PasswordSettings savedPasswordPrefs = mSettingsActivityTestRule.getFragment();
+        mTestHelper.startPasswordSettingsFromMainSettings(mPasswordSettingsActivityTestRule);
+        PasswordSettings savedPasswordPrefs = mPasswordSettingsActivityTestRule.getFragment();
 
         Assert.assertNull(
                 savedPasswordPrefs.findPreference(PasswordSettings.PREF_KEY_MANAGE_ACCOUNT_LINK));
@@ -248,11 +259,11 @@
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> { getPrefService().setBoolean(Pref.CREDENTIALS_ENABLE_AUTOSIGNIN, true); });
 
-        final SettingsActivity settingsActivity =
-                mTestHelper.startPasswordSettingsFromMainSettings(mSettingsActivityTestRule);
+        final SettingsActivity settingsActivity = mTestHelper.startPasswordSettingsFromMainSettings(
+                mPasswordSettingsActivityTestRule);
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            PasswordSettings passwordPrefs = mSettingsActivityTestRule.getFragment();
+            PasswordSettings passwordPrefs = mPasswordSettingsActivityTestRule.getFragment();
             ChromeSwitchPreference onOffSwitch =
                     (ChromeSwitchPreference) passwordPrefs.findPreference(
                             PasswordSettings.PREF_AUTOSIGNIN_SWITCH);
@@ -268,9 +279,9 @@
             getPrefService().setBoolean(Pref.CREDENTIALS_ENABLE_AUTOSIGNIN, false);
         });
 
-        mTestHelper.startPasswordSettingsFromMainSettings(mSettingsActivityTestRule);
+        mTestHelper.startPasswordSettingsFromMainSettings(mPasswordSettingsActivityTestRule);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            PasswordSettings passwordPrefs = mSettingsActivityTestRule.getFragment();
+            PasswordSettings passwordPrefs = mPasswordSettingsActivityTestRule.getFragment();
             ChromeSwitchPreference onOffSwitch =
                     (ChromeSwitchPreference) passwordPrefs.findPreference(
                             PasswordSettings.PREF_AUTOSIGNIN_SWITCH);
@@ -285,9 +296,9 @@
     @SmallTest
     @Feature({"Preferences"})
     public void testCheckPasswordsEnabled() {
-        mTestHelper.startPasswordSettingsFromMainSettings(mSettingsActivityTestRule);
+        mTestHelper.startPasswordSettingsFromMainSettings(mPasswordSettingsActivityTestRule);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            PasswordSettings passwordPrefs = mSettingsActivityTestRule.getFragment();
+            PasswordSettings passwordPrefs = mPasswordSettingsActivityTestRule.getFragment();
             Assert.assertNotNull(
                     passwordPrefs.findPreference(PasswordSettings.PREF_CHECK_PASSWORDS));
         });
@@ -308,8 +319,8 @@
         ReauthenticationManager.setScreenLockSetUpOverride(
                 ReauthenticationManager.OverrideState.UNAVAILABLE);
 
-        final SettingsActivity settingsActivity =
-                mTestHelper.startPasswordSettingsFromMainSettings(mSettingsActivityTestRule);
+        final SettingsActivity settingsActivity = mTestHelper.startPasswordSettingsFromMainSettings(
+                mPasswordSettingsActivityTestRule);
 
         View mainDecorView = settingsActivity.getWindow().getDecorView();
         onView(withId(R.id.recycler_view))
@@ -337,7 +348,7 @@
         ReauthenticationManager.setScreenLockSetUpOverride(
                 ReauthenticationManager.OverrideState.AVAILABLE);
 
-        mTestHelper.startPasswordSettingsFromMainSettings(mSettingsActivityTestRule);
+        mTestHelper.startPasswordSettingsFromMainSettings(mPasswordSettingsActivityTestRule);
         onView(withId(R.id.recycler_view))
                 .perform(scrollToHolder(hasTextInViewHolder("test user")));
         onView(withText(containsString("test user"))).perform(click());
@@ -355,9 +366,9 @@
     @MediumTest
     @Feature({"Preferences"})
     public void testDestroysPasswordCheckIfFirstInSettingsStack() {
-        mBrowserTestRule.addTestAccountThenSigninAndEnableSync();
+        setSyncServiceState(true, true);
         SettingsActivity activity =
-                mTestHelper.startPasswordSettingsDirectly(mSettingsActivityTestRule);
+                mTestHelper.startPasswordSettingsDirectly(mPasswordSettingsActivityTestRule);
         activity.finish();
         CriteriaHelper.pollUiThread(() -> activity.isDestroyed());
         Assert.assertNull(PasswordCheckFactory.getPasswordCheckInstance());
@@ -367,9 +378,9 @@
     @MediumTest
     @Feature({"Preferences"})
     public void testDoesNotDestroyPasswordCheckIfNotFirstInSettingsStack() {
-        mBrowserTestRule.addTestAccountThenSigninAndEnableSync();
-        SettingsActivity activity =
-                mTestHelper.startPasswordSettingsFromMainSettings(mSettingsActivityTestRule);
+        setSyncServiceState(true, true);
+        SettingsActivity activity = mTestHelper.startPasswordSettingsFromMainSettings(
+                mPasswordSettingsActivityTestRule);
         activity.finish();
         CriteriaHelper.pollUiThread(() -> activity.isDestroyed());
         Assert.assertNotNull(PasswordCheckFactory.getPasswordCheckInstance());
@@ -381,16 +392,18 @@
         return UserPrefs.get(Profile.getLastUsedRegularProfile());
     }
 
-    private void overrideSyncService(
-            final boolean usingPassphrase, final boolean syncingPasswords) {
+    private void setSyncServiceState(
+            final boolean usingCustomPassphrase, final boolean syncingPasswords) {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             when(mMockSyncService.hasSyncConsent()).thenReturn(syncingPasswords);
             when(mMockSyncService.isEngineInitialized()).thenReturn(true);
-            when(mMockSyncService.isUsingExplicitPassphrase()).thenReturn(usingPassphrase);
+            when(mMockSyncService.isUsingExplicitPassphrase()).thenReturn(usingCustomPassphrase);
+            when(mMockSyncService.getPassphraseType())
+                    .thenReturn(usingCustomPassphrase ? PassphraseType.CUSTOM_PASSPHRASE
+                                                      : PassphraseType.KEYSTORE_PASSPHRASE);
             when(mMockSyncService.getActiveDataTypes())
                     .thenReturn(CollectionUtil.newHashSet(
                             syncingPasswords ? ModelType.PASSWORDS : ModelType.AUTOFILL));
-            SyncService.overrideForTests(mMockSyncService);
         });
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java
index b94fcafb..b1c2d8f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java
@@ -591,12 +591,17 @@
     @SmallTest
     @Test
     public void testConvertTabUserAgentToProtoUserAgentType() {
-        for (@TabUserAgent Integer tabUserAgent = 0; tabUserAgent <= TabUserAgent.SIZE;
+        for (@TabUserAgent int tabUserAgent = 0; tabUserAgent <= TabUserAgent.SIZE;
                 tabUserAgent++) {
             CriticalPersistedTabDataProto.UserAgentType protoUserAgentType =
                     CriticalPersistedTabData.getUserAgentType(tabUserAgent);
-            Assert.assertNotEquals("TabUserAgent value is invalid.", protoUserAgentType,
-                    CriticalPersistedTabDataProto.UserAgentType.USER_AGENT_UNKNOWN);
+            if (tabUserAgent == TabUserAgent.DEFAULT) {
+                Assert.assertEquals("TabUserAgent value is mapped incorrectly.", protoUserAgentType,
+                        CriticalPersistedTabDataProto.UserAgentType.DEFAULT);
+            } else {
+                Assert.assertNotEquals("TabUserAgent value is invalid.", protoUserAgentType,
+                        CriticalPersistedTabDataProto.UserAgentType.DEFAULT);
+            }
             if (tabUserAgent != TabUserAgent.SIZE) continue;
             Assert.assertEquals("TabUserAgent and ProtoUserAgentType should have the same size.",
                     protoUserAgentType,
@@ -609,13 +614,18 @@
     public void testConvertProtoUserAgentTypeToTabUserAgent() {
         for (CriticalPersistedTabDataProto.UserAgentType type :
                 CriticalPersistedTabDataProto.UserAgentType.values()) {
-            if (type == CriticalPersistedTabDataProto.UserAgentType.USER_AGENT_UNKNOWN) continue;
             @TabUserAgent
-            Integer tabUserAgent = CriticalPersistedTabData.getUserAgentType(type);
-            Assert.assertNotNull("ProtoUserAgentType value is invalid.", tabUserAgent);
+            int tabUserAgent = CriticalPersistedTabData.getUserAgentType(type);
+            if (type == CriticalPersistedTabDataProto.UserAgentType.DEFAULT) {
+                Assert.assertEquals("ProtoUserAgentType value is mapped incorrectly.", tabUserAgent,
+                        TabUserAgent.DEFAULT);
+            } else {
+                Assert.assertNotEquals(
+                        "ProtoUserAgentType value is invalid.", tabUserAgent, TabUserAgent.DEFAULT);
+            }
             if (type != CriticalPersistedTabDataProto.UserAgentType.USER_AGENT_SIZE) continue;
             Assert.assertEquals("TabUserAgent and ProtoUserAgentType should have the same size.",
-                    (int) tabUserAgent, TabUserAgent.SIZE);
+                    tabUserAgent, TabUserAgent.SIZE);
         }
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarMediatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarMediatorUnitTest.java
index 4225629..9d9adb1b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarMediatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarMediatorUnitTest.java
@@ -85,6 +85,8 @@
     @Mock
     private TabModelSelector mTabModelSelector;
     @Mock
+    private TabModel mIncognitoTabModel;
+    @Mock
     Runnable mDismissedCallback;
     @Mock
     View.OnClickListener mOnClickListener;
@@ -168,7 +170,10 @@
         when(mTemplateUrlService.doesDefaultSearchEngineHaveLogo()).thenReturn(true);
 
         doReturn(false).when(mTabModelSelector).isIncognitoSelected();
+        doReturn(mIncognitoTabModel).when(mTabModelSelector).getModel(true);
+        doReturn(mMockIncognitoTab).when(mIncognitoTabModel).getTabAt(0);
         doReturn(false).when(mMockIncognitoTab).isClosing();
+        doReturn(0).when(mIncognitoTabModel).getCount();
     }
 
     @After
@@ -180,7 +185,7 @@
     public void testShowAndHideHomePage() {
         createMediator(false);
 
-        mIncognitoTabModelObserver.getValue().didBecomeEmpty();
+        doReturn(0).when(mIncognitoTabModel).getCount();
         assertFalse(mPropertyModel.get(LOGO_IS_VISIBLE));
         assertFalse(mPropertyModel.get(IDENTITY_DISC_IS_VISIBLE));
         assertFalse(mPropertyModel.get(IDENTITY_DISC_AT_START));
@@ -201,7 +206,7 @@
         assertFalse(mPropertyModel.get(INCOGNITO_SWITCHER_VISIBLE));
         assertTrue(mPropertyModel.get(IS_VISIBLE));
 
-        mIncognitoTabModelObserver.getValue().wasFirstTabCreated();
+        doReturn(1).when(mIncognitoTabModel).getCount();
         mMediator.onStartSurfaceStateChanged(StartSurfaceState.SHOWN_HOMEPAGE, true);
         assertTrue(mPropertyModel.get(LOGO_IS_VISIBLE));
         assertFalse(mPropertyModel.get(IDENTITY_DISC_IS_VISIBLE));
@@ -217,7 +222,7 @@
     public void testShowAndHideTabSwitcher() {
         createMediator(false);
 
-        mIncognitoTabModelObserver.getValue().didBecomeEmpty();
+        doReturn(0).when(mIncognitoTabModel).getCount();
         assertFalse(mPropertyModel.get(LOGO_IS_VISIBLE));
         assertFalse(mPropertyModel.get(IDENTITY_DISC_IS_VISIBLE));
         assertFalse(mPropertyModel.get(IDENTITY_DISC_AT_START));
@@ -240,7 +245,7 @@
         mMediator.updateIdentityDisc(mButtonData);
         assertFalse(mPropertyModel.get(IDENTITY_DISC_IS_VISIBLE));
 
-        mIncognitoTabModelObserver.getValue().wasFirstTabCreated();
+        doReturn(1).when(mIncognitoTabModel).getCount();
         mMediator.onStartSurfaceStateChanged(StartSurfaceState.SHOWN_TABSWITCHER, true);
         assertFalse(mPropertyModel.get(LOGO_IS_VISIBLE));
         assertFalse(mPropertyModel.get(IDENTITY_DISC_IS_VISIBLE));
@@ -287,22 +292,38 @@
     }
 
     @Test
-    public void testHidingIncognitoSwitchWithoutIncognitoTabs() {
+    public void testHidingIncognitoToggleWithoutIncognitoTabs() {
         createMediator(true);
 
-        mIncognitoTabModelObserver.getValue().didBecomeEmpty();
+        doReturn(0).when(mIncognitoTabModel).getCount();
         mMediator.onStartSurfaceStateChanged(StartSurfaceState.SHOWN_TABSWITCHER, true);
+        assertFalse(mPropertyModel.get(INCOGNITO_SWITCHER_VISIBLE));
         assertTrue(mPropertyModel.get(NEW_TAB_VIEW_IS_VISIBLE));
         assertTrue(mPropertyModel.get(NEW_TAB_VIEW_AT_START));
         assertTrue(mPropertyModel.get(NEW_TAB_VIEW_TEXT_IS_VISIBLE));
-        assertFalse(mPropertyModel.get(INCOGNITO_SWITCHER_VISIBLE));
 
-        mIncognitoTabModelObserver.getValue().wasFirstTabCreated();
+        doReturn(1).when(mIncognitoTabModel).getCount();
         mMediator.onStartSurfaceStateChanged(StartSurfaceState.SHOWN_TABSWITCHER, true);
+        assertTrue(mPropertyModel.get(INCOGNITO_SWITCHER_VISIBLE));
         assertTrue(mPropertyModel.get(NEW_TAB_VIEW_IS_VISIBLE));
         assertTrue(mPropertyModel.get(NEW_TAB_VIEW_AT_START));
         assertFalse(mPropertyModel.get(NEW_TAB_VIEW_TEXT_IS_VISIBLE));
+    }
+
+    @Test
+    public void testIncognitoTabModelObserverUpdatesIncognitoToggle() {
+        createMediator(true);
+        mMediator.onStartSurfaceStateChanged(StartSurfaceState.SHOWN_TABSWITCHER, true);
+
+        doReturn(0).when(mIncognitoTabModel).getCount();
+        mIncognitoTabModelObserver.getValue().didBecomeEmpty();
+        assertFalse(mPropertyModel.get(INCOGNITO_SWITCHER_VISIBLE));
+        assertTrue(mPropertyModel.get(NEW_TAB_VIEW_TEXT_IS_VISIBLE));
+
+        doReturn(1).when(mIncognitoTabModel).getCount();
+        mIncognitoTabModelObserver.getValue().wasFirstTabCreated();
         assertTrue(mPropertyModel.get(INCOGNITO_SWITCHER_VISIBLE));
+        assertFalse(mPropertyModel.get(NEW_TAB_VIEW_TEXT_IS_VISIBLE));
     }
 
     @Test
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 9010a5d..c6dcbd27 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-98.0.4758.14_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-99.0.4768.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn
index 2f869e2..5ab3dbd 100644
--- a/chrome/android/webapk/shell_apk/BUILD.gn
+++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -209,8 +209,14 @@
       "res/mipmap-nodpi/maskable_app_icon_xhdpi.png",
       "res/mipmap-nodpi/maskable_app_icon_xxhdpi.png",
       "res/mipmap-nodpi/maskable_app_icon_xxxhdpi.png",
+      "res/mipmap-nodpi/maskable_splash_icon_hdpi.png",
+      "res/mipmap-nodpi/maskable_splash_icon_mdpi.png",
+      "res/mipmap-nodpi/maskable_splash_icon_xhdpi.png",
       "res/mipmap-nodpi/maskable_splash_icon_xxhdpi.png",
       "res/mipmap-nodpi/maskable_splash_icon_xxxhdpi.png",
+      "res/mipmap-nodpi/splash_icon_hdpi.png",
+      "res/mipmap-nodpi/splash_icon_mdpi.png",
+      "res/mipmap-nodpi/splash_icon_xhdpi.png",
       "res/mipmap-nodpi/splash_icon_xxhdpi.png",
       "res/mipmap-nodpi/splash_icon_xxxhdpi.png",
       "res/mipmap-xhdpi/ic_launcher.xml",
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
index 82e4494..7a5b1690 100644
--- a/chrome/android/webapk/shell_apk/current_version/current_version.gni
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@
 # //chrome/android/webapk/shell_apk:webapk is changed. This includes
 # Java files, Android resource files and AndroidManifest.xml. Does not affect
 # Chrome.apk
-current_shell_apk_version = 146
+current_shell_apk_version = 147
diff --git a/chrome/android/webapk/shell_apk/res/drawable-hdpi/splash_icon.xml b/chrome/android/webapk/shell_apk/res/drawable-hdpi/splash_icon.xml
index 0ea2a6b..0f6577e0 100644
--- a/chrome/android/webapk/shell_apk/res/drawable-hdpi/splash_icon.xml
+++ b/chrome/android/webapk/shell_apk/res/drawable-hdpi/splash_icon.xml
@@ -4,4 +4,4 @@
      found in the LICENSE file. -->
 
 <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@mipmap/app_icon_xxhdpi"/>
+    android:src="@mipmap/splash_icon_hdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/drawable-mdpi/splash_icon.xml b/chrome/android/webapk/shell_apk/res/drawable-mdpi/splash_icon.xml
index 5e65be53..10e640e0 100644
--- a/chrome/android/webapk/shell_apk/res/drawable-mdpi/splash_icon.xml
+++ b/chrome/android/webapk/shell_apk/res/drawable-mdpi/splash_icon.xml
@@ -4,4 +4,4 @@
      found in the LICENSE file. -->
 
 <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@mipmap/app_icon_xhdpi"/>
+    android:src="@mipmap/splash_icon_mdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xhdpi/splash_icon.xml b/chrome/android/webapk/shell_apk/res/drawable-xhdpi/splash_icon.xml
index ab81c74..0889ee2 100644
--- a/chrome/android/webapk/shell_apk/res/drawable-xhdpi/splash_icon.xml
+++ b/chrome/android/webapk/shell_apk/res/drawable-xhdpi/splash_icon.xml
@@ -4,4 +4,4 @@
      found in the LICENSE file. -->
 
 <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@mipmap/app_icon_xxxhdpi"/>
+    android:src="@mipmap/splash_icon_xhdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-nodpi/maskable_splash_icon_hdpi.png b/chrome/android/webapk/shell_apk/res/mipmap-nodpi/maskable_splash_icon_hdpi.png
new file mode 100644
index 0000000..31cc841
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/mipmap-nodpi/maskable_splash_icon_hdpi.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-nodpi/maskable_splash_icon_mdpi.png b/chrome/android/webapk/shell_apk/res/mipmap-nodpi/maskable_splash_icon_mdpi.png
new file mode 100644
index 0000000..a114db5f
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/mipmap-nodpi/maskable_splash_icon_mdpi.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-nodpi/maskable_splash_icon_xhdpi.png b/chrome/android/webapk/shell_apk/res/mipmap-nodpi/maskable_splash_icon_xhdpi.png
new file mode 100644
index 0000000..9e626819
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/mipmap-nodpi/maskable_splash_icon_xhdpi.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-nodpi/splash_icon_hdpi.png b/chrome/android/webapk/shell_apk/res/mipmap-nodpi/splash_icon_hdpi.png
new file mode 100644
index 0000000..c597242
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/mipmap-nodpi/splash_icon_hdpi.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-nodpi/splash_icon_mdpi.png b/chrome/android/webapk/shell_apk/res/mipmap-nodpi/splash_icon_mdpi.png
new file mode 100644
index 0000000..e45b2281
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/mipmap-nodpi/splash_icon_mdpi.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-nodpi/splash_icon_xhdpi.png b/chrome/android/webapk/shell_apk/res/mipmap-nodpi/splash_icon_xhdpi.png
new file mode 100644
index 0000000..5deb8ec5
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/mipmap-nodpi/splash_icon_xhdpi.png
Binary files differ
diff --git a/chrome/app/chromeos_shared_strings.grdp b/chrome/app/chromeos_shared_strings.grdp
index f4eced8..46ab094 100644
--- a/chrome/app/chromeos_shared_strings.grdp
+++ b/chrome/app/chromeos_shared_strings.grdp
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- Chrome OS-specific strings (included from generated_resources.grd).
      Shared between both Ash and Lacros.
-     Everything in this file is wrapped in <if expr="chromeos or lacros">. -->
+     Everything in this file is wrapped in <if expr="chromeos_ash or chromeos_lacros">. -->
 <grit-part>
 
   <!-- Settings -->
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 1f084bd3e..3094f43 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -854,6 +854,39 @@
   <message name="IDS_BLUETOOTH_PAIRING_ENTER_KEYS" desc="Bluetooth pairing dialog: Message displayed informing the user to enter the keys below on the device being paired.">
     Enter these keys on <ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>
   </message>
+    <message name="IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_UNKNOWN" desc="Bluetooth pairing UI: Accessibility label used for a device of unknown type in a list of devices which can be paired.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, Unknown device named <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>
+  </message>
+  <message name="IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_COMPUTER" desc="Bluetooth pairing UI: Accessibility label used for a computer device in a list of devices which can be paired.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, Computer named <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>
+  </message>
+  <message name="IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_PHONE" desc="Bluetooth pairing UI: Accessibility label used for a phone device in a list of devices which can be paired.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, Phone named <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>
+  </message>
+  <message name="IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_HEADSET" desc="Bluetooth pairing UI: Accessibility label used for an audio device in a list of devices which can be paired.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, Audio device named <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>
+  </message>
+  <message name="IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_VIDEO_CAMERA" desc="Bluetooth pairing UI: Accessibility label used for a video camera device in a list of devices which can be paired.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, Video camera named <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>
+  </message>
+  <message name="IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_GAME_CONTROLLER" desc="Bluetooth pairing UI: Accessibility label used for a game controller device in a list of devices which can be paired.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, Game controller named <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>
+  </message>
+  <message name="IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_KEYBOARD" desc="Bluetooth pairing UI: Accessibility label used for a keyboard device in a list of devices which can be paired.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, Keyboard named <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>
+  </message>
+  <message name="IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_MOUSE" desc="Bluetooth pairing UI: Accessibility label used for a mouse device in a list of devices which can be paired.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, Mouse named <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>
+  </message>
+  <message name="IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_TABLET" desc="Bluetooth pairing UI: Accessibility label used for a tablet device in a list of devices which can be paired.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, Tablet named <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>
+  </message>
+  <message name="IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_SECONDARY_ERROR_A11Y_LABEL" desc="Bluetooth pairing dialog: Accessibility label for unpaired Bluetooth device list item, when pairing to a device fails.">
+    Couldn't pair to device <ph name="DEVICE_NAME">$1<ex>Beats</ex></ph>; select device to try again
+  </message>
+  <message name="IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_SECONDARY_PAIRING_A11Y_LABEL" desc="Bluetooth pairing dialog: Accessibility label for unpaired Bluetooth device list item, when pairing to the a device is in progress.">
+    Pairing to <ph name="DEVICE_NAME">$1<ex>Beats</ex></ph>
+  </message>
 
   <!-- Strings for the OOBE packaged license screen -->
   <message name="IDS_OOBE_PACKAGED_LICENSE_TITLE" desc="Title of the screen which advertises use of packaged license.">
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_COMPUTER.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_COMPUTER.png.sha1
new file mode 100644
index 0000000..697c12c
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_COMPUTER.png.sha1
@@ -0,0 +1 @@
+384318d0fe4e3716ffb32ae3c5ad279fc246acbb
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_GAME_CONTROLLER.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_GAME_CONTROLLER.png.sha1
new file mode 100644
index 0000000..697c12c
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_GAME_CONTROLLER.png.sha1
@@ -0,0 +1 @@
+384318d0fe4e3716ffb32ae3c5ad279fc246acbb
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_HEADSET.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_HEADSET.png.sha1
new file mode 100644
index 0000000..697c12c
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_HEADSET.png.sha1
@@ -0,0 +1 @@
+384318d0fe4e3716ffb32ae3c5ad279fc246acbb
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_KEYBOARD.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_KEYBOARD.png.sha1
new file mode 100644
index 0000000..697c12c
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_KEYBOARD.png.sha1
@@ -0,0 +1 @@
+384318d0fe4e3716ffb32ae3c5ad279fc246acbb
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_MOUSE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_MOUSE.png.sha1
new file mode 100644
index 0000000..697c12c
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_MOUSE.png.sha1
@@ -0,0 +1 @@
+384318d0fe4e3716ffb32ae3c5ad279fc246acbb
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_PHONE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_PHONE.png.sha1
new file mode 100644
index 0000000..697c12c
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_PHONE.png.sha1
@@ -0,0 +1 @@
+384318d0fe4e3716ffb32ae3c5ad279fc246acbb
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_TABLET.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_TABLET.png.sha1
new file mode 100644
index 0000000..697c12c
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_TABLET.png.sha1
@@ -0,0 +1 @@
+384318d0fe4e3716ffb32ae3c5ad279fc246acbb
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_UNKNOWN.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_UNKNOWN.png.sha1
new file mode 100644
index 0000000..697c12c
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_UNKNOWN.png.sha1
@@ -0,0 +1 @@
+384318d0fe4e3716ffb32ae3c5ad279fc246acbb
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_VIDEO_CAMERA.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_VIDEO_CAMERA.png.sha1
new file mode 100644
index 0000000..697c12c
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_VIDEO_CAMERA.png.sha1
@@ -0,0 +1 @@
+384318d0fe4e3716ffb32ae3c5ad279fc246acbb
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_SECONDARY_ERROR_A11Y_LABEL.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_SECONDARY_ERROR_A11Y_LABEL.png.sha1
new file mode 100644
index 0000000..d135cad
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_SECONDARY_ERROR_A11Y_LABEL.png.sha1
@@ -0,0 +1 @@
+abd68cb9e244d8a82451b5f4567a847b2d4e3def
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_SECONDARY_PAIRING_A11Y_LABEL.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_SECONDARY_PAIRING_A11Y_LABEL.png.sha1
new file mode 100644
index 0000000..ea08739
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_SECONDARY_PAIRING_A11Y_LABEL.png.sha1
@@ -0,0 +1 @@
+18fe36a18e4e679249ef2ae6ea6490cb8bacf28d
\ No newline at end of file
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 55dbaf9..16341121 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -38,7 +38,7 @@
       <output filename="chromium_strings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="chromium_strings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="chromium_strings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="chromium_strings_am.pak" type="data_package" lang="am" />
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 09f541f..f243cd6 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -43,7 +43,7 @@
       <output filename="generated_resources_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="generated_resources_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="generated_resources_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="generated_resources_am.pak" type="data_package" lang="am" />
@@ -301,7 +301,7 @@
       </if>
 
       <!-- Chrome-OS-specific strings -->
-      <if expr="chromeos or lacros">
+      <if expr="chromeos_ash or chromeos_lacros">
         <part file="chromeos_shared_strings.grdp" />
       </if>
       <if expr="chromeos">
@@ -1381,7 +1381,7 @@
       </if>
 
       <!-- App menu -->
-      <if expr="chromeos or lacros">
+      <if expr="chromeos_ash or chromeos_lacros">
         <message name="IDS_TOGGLE_REQUEST_TABLET_SITE" desc="The toggle to request mobile version site on a touch dev.">
           Request mobile site
         </message>
@@ -1772,7 +1772,7 @@
                desc="When starting a download, let the user know we're starting the download.">
         Starting...
       </message>
-      <if expr="chromeos or lacros">
+      <if expr="chromeos_ash or chromeos_lacros">
         <message name="IDS_DOWNLOAD_STATUS_IN_PROGRESS_TITLE"
                  desc="The title of a download notification: the current download status is in progress.">
                  Downloading <ph name="FILE_NAME">$1<ex>somedocument.pdf</ex></ph>
@@ -1996,21 +1996,21 @@
         This file is encrypted. Ask its owner to decrypt.
       </message>
 
-      <if expr="chromeos or lacros">
+      <if expr="chromeos_ash or chromeos_lacros">
         <message name="IDS_PROMPT_BLOCKED_MIXED_DOWNLOAD_TITLE"
                  desc="In the download notification, a title of the message shown on a download blocked for being insecure.">
           Insecure download blocked
         </message>
       </if>
 
-      <if expr="chromeos or lacros">
+      <if expr="chromeos_ash or chromeos_lacros">
         <message name="IDS_PROMPT_BLOCKED_MALICIOUS_DOWNLOAD_TITLE"
                  desc="In the download notification, a title of message shown for blocked download">
           Dangerous download blocked
         </message>
       </if>
 
-      <if expr="chromeos or lacros">
+      <if expr="chromeos_ash or chromeos_lacros">
         <message name="IDS_PROMPT_SEND_TO_SAFEBROWSING_DOWNLOAD_TITLE"
                  desc="In the download notification, a title of message shown indicating that the file may be malicious and Safe Browsing recommends sending the file to Google">
           Scan file before opening?
@@ -6326,6 +6326,10 @@
       <message name="IDS_ACCDESCRIPTION_FORWARD" desc="Help text that explains to keyboard users how to interact with the forward button, similar to the tooltip presented to mouse users.">
         Press to go forward, context menu to see history
       </message>
+      <message name="IDS_TOOLTIP_DOWNLOAD_ICON"
+          desc="The tooltip for download toolbar button icon">
+          Downloads
+      </message>
       <message name="IDS_TOOLTIP_HOME" desc="The tooltip for the home button">
         Open the home page
       </message>
@@ -7406,7 +7410,7 @@
         <message name="IDS_TAB_CXMENU_MOVETOANOTHERNEWWINDOW" desc="The label for the Tab context item to move items to a new window from the 'Move tabs to another window' submenu.">
           New window
         </message>
-        <if expr="chromeos or lacros">
+        <if expr="chromeos_ash or chromeos_lacros">
           <message name="IDS_TAB_CXMENU_GROUPED_BY_DESK_MENU_ITEM_A11Y" desc="The a11y message for menu items in 'Move tabs to another window' when grouped by desk.">
             <ph name="TAB_TITLE">$1<ex>Google News</ex></ph> belongs to desk <ph name="DESK_TITLE">$2<ex>School</ex></ph>
           </message>
@@ -7817,7 +7821,7 @@
         Send feedback
       </message>
       <!-- Killed Tab Strings -->
-      <if expr="chromeos or lacros">
+      <if expr="chromeos_ash or chromeos_lacros">
         <message name="IDS_KILLED_TAB_BY_OOM_MESSAGE" desc="The message displayed on the killed tab page when the page was killed by an out of memory error.">
           Either Chrome ran out of memory or the process for the webpage was terminated for some other reason.  To continue, reload or go to another page.
         </message>
diff --git a/chrome/app/generated_resources_grd/IDS_TOOLTIP_DOWNLOAD_ICON.png.sha1 b/chrome/app/generated_resources_grd/IDS_TOOLTIP_DOWNLOAD_ICON.png.sha1
new file mode 100644
index 0000000..af7c295
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_TOOLTIP_DOWNLOAD_ICON.png.sha1
@@ -0,0 +1 @@
+7601071f126bfc03cfd3a946a94483e712da20be
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 365fe6b..d869918 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -40,7 +40,7 @@
       <output filename="google_chrome_strings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="google_chrome_strings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="google_chrome_strings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="google_chrome_strings_am.pak" type="data_package" lang="am" />
diff --git a/chrome/app/printing_strings.grdp b/chrome/app/printing_strings.grdp
index 76b5fdef..20fdaa9 100644
--- a/chrome/app/printing_strings.grdp
+++ b/chrome/app/printing_strings.grdp
@@ -292,7 +292,7 @@
     <message name="IDS_PRINT_PREVIEW_MANAGED_SETTINGS_TEXT" desc="Text shown when hovering over the enterprise managed icon in the header">
       These settings are enforced by your administrator
     </message>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <message name="IDS_PRINT_PREVIEW_PRINT_TO_GOOGLE_DRIVE"
                desc="Option shown on printer drop-down list for saving previewed document to Google Drive.">
         Save to Google Drive
diff --git a/chrome/app/resources/chromium_strings_ar.xtb b/chrome/app/resources/chromium_strings_ar.xtb
index adddcea..50a88a3 100644
--- a/chrome/app/resources/chromium_strings_ar.xtb
+++ b/chrome/app/resources/chromium_strings_ar.xtb
@@ -238,6 +238,7 @@
 <translation id="7561906087460245826">‏محو البيانات من Chromium أيضًا (<ph name="URL" />)</translation>
 <translation id="7585853947355360626">‏في حال عدم عرض إعداد على هذه الصفحة، انتقل إلى <ph name="LINK_BEGIN" />
     إعدادات نظام التشغيل Chromium<ph name="LINK_END" /></translation>
+<translation id="7597596667193879455">‏تم تفعيل ميزة "الحماية المُحسّنة للتصفّح الآمن" في حسابك. ويمكنك استخدامها الآن في Chromium.</translation>
 <translation id="761356813943268536">‏يستخدم Chromium الكاميرا والميكروفون.</translation>
 <translation id="7617377681829253106">‏Chromium أصبح أفضل</translation>
 <translation id="7686590090926151193">‏Chromium ليس متصفحك التلقائي.</translation>
diff --git a/chrome/app/resources/chromium_strings_bn.xtb b/chrome/app/resources/chromium_strings_bn.xtb
index 8bbaa84..8377e37 100644
--- a/chrome/app/resources/chromium_strings_bn.xtb
+++ b/chrome/app/resources/chromium_strings_bn.xtb
@@ -236,6 +236,7 @@
 <translation id="7561906087460245826">এর পাশাপাশি, Chromium থেকেও ডেটা সরিয়ে দিন (<ph name="URL" />)</translation>
 <translation id="7585853947355360626">এই পৃষ্ঠাতে কোনও একটি সেটিং দেখতে না পেলে আপনার <ph name="LINK_BEGIN" />
       Chromium OS সেটিংস<ph name="LINK_END" /> বিকল্পটি দেখুন</translation>
+<translation id="7597596667193879455">আপনার অ্যাকাউন্টে উন্নত নিরাপদ ব্রাউজিং চালু করেছেন। এখন Chromium-এ চালু করুন।</translation>
 <translation id="761356813943268536">Chromium আপনার ক্যামেরা এবং মাইক্রোফোন ব্যবহার করছে৷</translation>
 <translation id="7617377681829253106">Chromium আরও ভাল হয়ে উঠেছে</translation>
 <translation id="7686590090926151193">Chromium আপনার ডিফল্ট ব্রাউজার নয়</translation>
diff --git a/chrome/app/resources/chromium_strings_cs.xtb b/chrome/app/resources/chromium_strings_cs.xtb
index 57d25c7..29ffd87b 100644
--- a/chrome/app/resources/chromium_strings_cs.xtb
+++ b/chrome/app/resources/chromium_strings_cs.xtb
@@ -239,6 +239,7 @@
 <translation id="7549178288319965365">O systému Chromium OS</translation>
 <translation id="7561906087460245826">Také vymazat data z prohlížeče Chromium (<ph name="URL" />)</translation>
 <translation id="7585853947355360626">Pokud některé nastavení na této stránce není zobrazeno, podívejte se do <ph name="LINK_BEGIN" />nastavení systému Chromium OS<ph name="LINK_END" /></translation>
+<translation id="7597596667193879455">Ve svém účtu jste zapnuli Vylepšené Bezpečné prohlížení. Teď ho můžete zapnout v prohlížeči Chromium.</translation>
 <translation id="761356813943268536">Chromium používá vaši kameru a mikrofon.</translation>
 <translation id="7617377681829253106">Prohlížeč Chromium je opět o něco lepší</translation>
 <translation id="7686590090926151193">Chromium není vaším výchozím prohlížečem</translation>
diff --git a/chrome/app/resources/chromium_strings_fa.xtb b/chrome/app/resources/chromium_strings_fa.xtb
index c178d5a8..7ed1913 100644
--- a/chrome/app/resources/chromium_strings_fa.xtb
+++ b/chrome/app/resources/chromium_strings_fa.xtb
@@ -236,6 +236,7 @@
 <translation id="7561906087460245826">‏همچنین داده‌ها از Chromium پاک شود (<ph name="URL" />)</translation>
 <translation id="7585853947355360626">‏اگر تنظیمی در این صفحه نشان داده نمی‌شود، در <ph name="LINK_BEGIN" />
       تنظیمات مرورگر Chromium<ph name="LINK_END" /> آن را کنید</translation>
+<translation id="7597596667193879455">‏«مرور ایمن پیشرفته» را در حسابتان روشن کرده‌اید. اکنون آن را برای Chromium دریافت کنید.</translation>
 <translation id="761356813943268536">‏Chromium درحال استفاده از دوربین و میکروفون شما است.</translation>
 <translation id="7617377681829253106">‏Chromium اکنون بهتر شده است</translation>
 <translation id="7686590090926151193">‏Chromium مرورگر پیش‌فرض شما نیست</translation>
diff --git a/chrome/app/resources/chromium_strings_gu.xtb b/chrome/app/resources/chromium_strings_gu.xtb
index 8ef51b0f..00a746a 100644
--- a/chrome/app/resources/chromium_strings_gu.xtb
+++ b/chrome/app/resources/chromium_strings_gu.xtb
@@ -240,6 +240,7 @@
 <translation id="7561906087460245826">Chromiumમાંથી પણ ડેટા કાઢી નાખો (<ph name="URL" />)</translation>
 <translation id="7585853947355360626">જો આ પેજ પર સેટિંગ બતાવેલું ન હોય, તો તમારા <ph name="LINK_BEGIN" />
       Chromium OS સેટિંગ<ph name="LINK_END" />માં જુઓ</translation>
+<translation id="7597596667193879455">તમે તમારા એકાઉન્ટમાં Safe Browsingમાં વધારેલી સુરક્ષાની સુવિધા ચાલુ કરી છે. હવે આ સુવિધા Chromium માટે મેળવો.</translation>
 <translation id="761356813943268536">Chromium તમારા કૅમેરા અને માઇક્રોફોનનો ઉપયોગ કરી રહ્યું છે.</translation>
 <translation id="7617377681829253106">Chromium હવે પહેલાંથી બહેતર બન્યું છે</translation>
 <translation id="7686590090926151193">Chromium તમારું ડિફૉલ્ટ બ્રાઉઝર નથી</translation>
diff --git a/chrome/app/resources/chromium_strings_hy.xtb b/chrome/app/resources/chromium_strings_hy.xtb
index 8ae8de3..c51cc2f 100644
--- a/chrome/app/resources/chromium_strings_hy.xtb
+++ b/chrome/app/resources/chromium_strings_hy.xtb
@@ -239,6 +239,7 @@
 <translation id="7561906087460245826">Ջնջել նաև Chromium-ի տվյալները (<ph name="URL" />)</translation>
 <translation id="7585853947355360626">Եթե որևէ կարգավորում չտեսնեք այս էջում, փնտրեք այն ձեր <ph name="LINK_BEGIN" />
       Chromium OS-ի կարգավորումներում<ph name="LINK_END" /></translation>
+<translation id="7597596667193879455">Դուք ձեր հաշվում միացրել եք բարելավված Ապահով դիտարկումը։ Այժմ միացրեք այն Chromium-ի համար։</translation>
 <translation id="761356813943268536">Chromium-ն օգտագործում է ձեր տեսախցիկն ու խոսափողը:</translation>
 <translation id="7617377681829253106">Chromium-ը կատարելագործվել է</translation>
 <translation id="7686590090926151193">Chromium-ը ձեր կանխադրված դիտարկիչը չէ</translation>
diff --git a/chrome/app/resources/chromium_strings_lo.xtb b/chrome/app/resources/chromium_strings_lo.xtb
index c67b12c..253df08 100644
--- a/chrome/app/resources/chromium_strings_lo.xtb
+++ b/chrome/app/resources/chromium_strings_lo.xtb
@@ -240,6 +240,7 @@
 <translation id="7561906087460245826">ລຶບລ້າງຂໍ້ມູນອອກຈາກ Chromium (<ph name="URL" />) ນຳ</translation>
 <translation id="7585853947355360626">ຖ້າການຕັ້ງຄ່າບໍ່ສະແດງໃນໜ້ານີ້, ກະລຸນາກວດເບິ່ງໃນ <ph name="LINK_BEGIN" />
     ການຕັ້ງຄ່າ Chromium OS<ph name="LINK_END" /> ຂອງທ່ານ</translation>
+<translation id="7597596667193879455">ທ່ານເປີດໃຊ້ Safe Browsing ທີ່ປັບປຸງດີຂຶ້ນໃນບັນຊີຂອງທ່ານແລ້ວ. ຕອນນີ້ສາມາດໃຊ້ໄດ້ກັບ Chromium ແລ້ວ.</translation>
 <translation id="761356813943268536">Chromium ກໍາລັງໃຊ້​ກ້ອງ​ຖ່າຍ​ຮູບ​ ແລະໄມໂຄຣໂຟນຂອງ​ທ່ານ​.</translation>
 <translation id="7617377681829253106">Chromium ​ດີກ​ວ່າແລ້ວ</translation>
 <translation id="7686590090926151193">Chromium ບໍ່ແມ່ນໂປຣແກຣມທ່ອງເວັບເລີ່ມຕົ້ນຂອງທ່ານ</translation>
diff --git a/chrome/app/resources/chromium_strings_mr.xtb b/chrome/app/resources/chromium_strings_mr.xtb
index 1258035..aa948fe 100644
--- a/chrome/app/resources/chromium_strings_mr.xtb
+++ b/chrome/app/resources/chromium_strings_mr.xtb
@@ -239,6 +239,7 @@
 <translation id="7561906087460245826">तसेच Chromium (<ph name="URL" />) वरील डेटा साफ करा</translation>
 <translation id="7585853947355360626">या पेजवर सेटिंग दिसत नसल्यास, तुमच्या <ph name="LINK_BEGIN" />
       Chromium OS सेटिंग्ज<ph name="LINK_END" /> मध्ये पहा</translation>
+<translation id="7597596667193879455">तुम्ही तुमच्या खात्यामध्ये वर्धित सुरक्षित ब्राउझिंग सुरू केले. ते आता Chromium साठी मिळवा.</translation>
 <translation id="761356813943268536">Chromium तुमचा कॅमेरा आणि मायक्रोफोन वापरत आहे.</translation>
 <translation id="7617377681829253106">Chromium आता उत्कृष्ट झाले आहे</translation>
 <translation id="7686590090926151193">Chromium तुमचा डीफॉल्ट ब्राउझर नाही</translation>
diff --git a/chrome/app/resources/chromium_strings_ms.xtb b/chrome/app/resources/chromium_strings_ms.xtb
index 8c0b0ad5..411c446e 100644
--- a/chrome/app/resources/chromium_strings_ms.xtb
+++ b/chrome/app/resources/chromium_strings_ms.xtb
@@ -238,6 +238,7 @@
 <translation id="7561906087460245826">Kosongkan data daripada Chromium (<ph name="URL" />) juga</translation>
 <translation id="7585853947355360626">Jika tetapan tidak ditunjukkan pada halaman ini, lihat dalam <ph name="LINK_BEGIN" />
       tetapan OS Chromium anda<ph name="LINK_END" /></translation>
+<translation id="7597596667193879455">Anda menghidupkan Penyemakan Imbas Selamat Dipertingkat dalam akaun anda. Sekarang dapatkan ciri ini untuk Chromium.</translation>
 <translation id="761356813943268536">Chromium menggunakan kamera dan mikrofon anda.</translation>
 <translation id="7617377681829253106">Chromium kini lebih baik</translation>
 <translation id="7686590090926151193">Chromium bukan penyemak imbas lalai anda</translation>
diff --git a/chrome/app/resources/chromium_strings_ne.xtb b/chrome/app/resources/chromium_strings_ne.xtb
index 1ecd53ad..e28530b4 100644
--- a/chrome/app/resources/chromium_strings_ne.xtb
+++ b/chrome/app/resources/chromium_strings_ne.xtb
@@ -238,6 +238,7 @@
 <translation id="7561906087460245826">Chromium (<ph name="URL" />) को डेटा पनि खाली गर्नुहोस्</translation>
 <translation id="7585853947355360626">यो पृष्ठमा कुनै सेटिङ देखिएन भने आफ्नो <ph name="LINK_BEGIN" />
     Chromium OS का सेटिङ<ph name="LINK_END" />मा गई हेर्नुहोस्</translation>
+<translation id="7597596667193879455">तपाईंले आफ्नो खातामा परिष्कृत Safe Browsing अन गर्नुभयो। अब Chromium मा पनि यो सुविधा अन गर्नुहोस्।</translation>
 <translation id="761356813943268536">Chromium ले तपइँको क्यामेरा र माइक्रोफोन प्रयोग गर्दैछ।</translation>
 <translation id="7617377681829253106">Chromium भर्खरै झन राम्रो भयो</translation>
 <translation id="7686590090926151193">Chromium तपाईंको डिफल्ट ब्राउजर होइन</translation>
diff --git a/chrome/app/resources/chromium_strings_pa.xtb b/chrome/app/resources/chromium_strings_pa.xtb
index bdd78d64..5b3b439 100644
--- a/chrome/app/resources/chromium_strings_pa.xtb
+++ b/chrome/app/resources/chromium_strings_pa.xtb
@@ -240,6 +240,7 @@
 <translation id="7561906087460245826">Chromium (<ph name="URL" />) ਤੋਂ ਵੀ ਡਾਟਾ ਕਲੀਅਰ ਕਰੋ</translation>
 <translation id="7585853947355360626">ਜੇ ਇਸ ਪੰਨੇ 'ਤੇ ਸੈਟਿੰਗ ਨਹੀਂ ਦਿਸਦੀ, ਤਾਂ ਆਪਣੀਆਂ <ph name="LINK_BEGIN" />
       Chromium OS ਸੈਟਿੰਗਾਂ<ph name="LINK_END" /> ਵਿੱਚ ਦੇਖੋ</translation>
+<translation id="7597596667193879455">ਤੁਸੀਂ ਆਪਣੇ ਖਾਤੇ ਵਿੱਚ ਵਿਸਤ੍ਰਿਤ ਸੁਰੱਖਿਅਤ ਬ੍ਰਾਊਜ਼ਿੰਗ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਹੈ। ਇਸਨੂੰ ਹੁਣ Chromium ਲਈ ਵੀ ਪ੍ਰਾਪਤ ਕਰੋ।</translation>
 <translation id="761356813943268536">Chromium ਤੁਹਾਡਾ ਕੈਮਰਾ ਅਤੇ ਮਾਈਕ੍ਰੋਫੋਨ ਵਰਤ ਰਿਹਾ ਹੈ।</translation>
 <translation id="7617377681829253106">Chromium ਬਿਹਤਰ ਬਣ ਗਿਆ ਹੈ</translation>
 <translation id="7686590090926151193">Chromium ਤੁਹਾਡਾ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਬ੍ਰਾਊਜ਼ਰ ਨਹੀਂ ਹੈ</translation>
diff --git a/chrome/app/resources/chromium_strings_sk.xtb b/chrome/app/resources/chromium_strings_sk.xtb
index 8a2470bc..8e187f7 100644
--- a/chrome/app/resources/chromium_strings_sk.xtb
+++ b/chrome/app/resources/chromium_strings_sk.xtb
@@ -239,6 +239,7 @@
 <translation id="7549178288319965365">Informácie o systéme Chromium OS</translation>
 <translation id="7561906087460245826">Tiež vymazať údaje z prehliadača Chromium (<ph name="URL" />)</translation>
 <translation id="7585853947355360626">Ak určité nastavenie nevidíte na tejto stránke, skontrolujte <ph name="LINK_BEGIN" />nastavenia operačného systému Chromium<ph name="LINK_END" /></translation>
+<translation id="7597596667193879455">V účte ste zapli Zlepšené bezpečné prehliadanie. Povoľte ho aj pre Chromium.</translation>
 <translation id="761356813943268536">Chromium používa vašu kameru a mikrofón.</translation>
 <translation id="7617377681829253106">Prehliadač Chromium bol práve vylepšený</translation>
 <translation id="7686590090926151193">Chromium nie je váš predvolený prehliadač</translation>
diff --git a/chrome/app/resources/chromium_strings_sw.xtb b/chrome/app/resources/chromium_strings_sw.xtb
index 9f8f285..518664a 100644
--- a/chrome/app/resources/chromium_strings_sw.xtb
+++ b/chrome/app/resources/chromium_strings_sw.xtb
@@ -240,6 +240,7 @@
 <translation id="7561906087460245826">Futa pia data kwenye Chromium (<ph name="URL" />)</translation>
 <translation id="7585853947355360626">Ikiwa mipangilio haionekani kwenye ukurasa huu, angalia katika.<ph name="LINK_BEGIN" />
       mipangilio ya mfumo wa uendeshaji wa Chromium<ph name="LINK_END" /></translation>
+<translation id="7597596667193879455">Umewasha Kipengele cha Kuvinjari Salama Kilichoboreshwa katika akaunti yako. Sasa kiwashe kwenye Chromium.</translation>
 <translation id="761356813943268536">Chromium inatumia kamera na maikrofoni yako.</translation>
 <translation id="7617377681829253106">Chromium imeboreshwa</translation>
 <translation id="7686590090926151193">Chromium si kivinjari chako chaguomsingi</translation>
diff --git a/chrome/app/resources/chromium_strings_vi.xtb b/chrome/app/resources/chromium_strings_vi.xtb
index b0b2ec48..6edbcb6 100644
--- a/chrome/app/resources/chromium_strings_vi.xtb
+++ b/chrome/app/resources/chromium_strings_vi.xtb
@@ -238,6 +238,7 @@
 <translation id="7561906087460245826">Đồng thời xóa dữ liệu khỏi Chromium (<ph name="URL" />)</translation>
 <translation id="7585853947355360626">Nếu một tùy chọn cài đặt không hiển thị trên trang này, hãy tìm trong mục <ph name="LINK_BEGIN" />
       cài đặt của Chromium OS<ph name="LINK_END" /></translation>
+<translation id="7597596667193879455">Bạn đã bật tính năng Duyệt web an toàn có tăng cường bảo vệ trong tài khoản. Giờ hãy sử dụng tính năng này trên Chromium.</translation>
 <translation id="761356813943268536">Chromium đang sử dụng máy ảnh và micrô của bạn.</translation>
 <translation id="7617377681829253106">Chromium được cải tiến hơn</translation>
 <translation id="7686590090926151193">Chromium không phải là trình duyệt mặc định của bạn</translation>
diff --git a/chrome/app/resources/chromium_strings_zu.xtb b/chrome/app/resources/chromium_strings_zu.xtb
index b439bb51..ce1e7c9 100644
--- a/chrome/app/resources/chromium_strings_zu.xtb
+++ b/chrome/app/resources/chromium_strings_zu.xtb
@@ -240,6 +240,7 @@
 <translation id="7561906087460245826">Futhi sula idatha kusuka ku-Chromium (<ph name="URL" />)</translation>
 <translation id="7585853947355360626">Uma isilungiselelo singaveli kuleli khasi, bheka<ph name="LINK_BEGIN" />
       izilungiselelo ze-Chromium OS<ph name="LINK_END" /></translation>
+<translation id="7597596667193879455">Uvule Ukuphequlula Ngokuphepha Okugqamile ku-akhawunti yakho. Manje yitholele i-Chromium.</translation>
 <translation id="761356813943268536">I-Chromium isebenzisa ikhamela yakho nemakrofoni.</translation>
 <translation id="7617377681829253106">I-Chromium ibe ngcono nakakhulu</translation>
 <translation id="7686590090926151193">I-Chromium akusona isiphequluli sakho esizenzakalelayo</translation>
diff --git a/chrome/app/resources/generated_resources_ar.xtb b/chrome/app/resources/generated_resources_ar.xtb
index 0c13c4e..2c40f4d 100644
--- a/chrome/app/resources/generated_resources_ar.xtb
+++ b/chrome/app/resources/generated_resources_ar.xtb
@@ -417,6 +417,7 @@
            • حذف ملفات تعريف الارتباط والبيانات المؤقَّتة الأخرى للمواقع الإلكترونية
             <ph name="LINE_BREAKS" />
                   لن تتأثر الإشارات المرجعية وكلمات المرور المحفوظة وسجلّ التصفُّح بهذا الإجراء.</translation>
+<translation id="1425233109861512854">لم يتم التعرّف على رمز الدخول.</translation>
 <translation id="1426410128494586442">نعم</translation>
 <translation id="142655739075382478"><ph name="APP_NAME" /> محظور</translation>
 <translation id="1426870617281699524">النقر على "المحاولة مرة أخرى"، وقبول المطالبة على جهاز الكمبيوتر</translation>
@@ -583,6 +584,7 @@
 <translation id="1593926297800505364">حفظ طريقة الدفع</translation>
 <translation id="1595492813686795610">‏جارٍ ترقية Linux</translation>
 <translation id="1596286373007273895">متوفِّر</translation>
+<translation id="1596709061955594992">تم إيقاف اتصال البلوتوث. يُرجى تفعيل اتصال البلوتوث لرؤية الأجهزة المتاحة.</translation>
 <translation id="1598233202702788831">أوقف المشرف التحديثات.</translation>
 <translation id="1600857548979126453">الدخول إلى الواجهة الخلفية لبرنامج تصحيح أخطاء الصفحة</translation>
 <translation id="1601560923496285236">تطبيق</translation>
@@ -1135,6 +1137,7 @@
 <translation id="2148892889047469596">علامة التبويب "إرسال"</translation>
 <translation id="2149973817440762519">تعديل الإشارة</translation>
 <translation id="2150139952286079145">بحث في الوجهات</translation>
+<translation id="2150491840563706000">ليس لديك إذن للبث على هذه الشاشة.</translation>
 <translation id="2150661552845026580">هل تريد إضافة "<ph name="EXTENSION_NAME" />"؟</translation>
 <translation id="2151576029659734873">تم إدخال فهرس علامات تبويب غير صحيح.</translation>
 <translation id="2152281589789213846">إضافة الطابعات إلى ملفك الشخصي</translation>
@@ -2602,6 +2605,7 @@
 <translation id="3654045516529121250">الاطّلاع على إعدادات إمكانية الوصول</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{لديه حق الوصول الدائم إلى ملف واحد.}zero{لديه حق الوصول الدائم إلى # من الملفات.}two{لديه حق الوصول الدائم إلى ملفين (#).}few{ لديه حق الوصول الدائم إلى # ملفات.}many{لديه حق الوصول الدائم إلى # ملفًا.}other{لديه حق الوصول الدائم إلى # من الملفات.}}</translation>
 <translation id="3658871634334445293">‏تسارع TrackPoint</translation>
+<translation id="3659929705630080526">لقد أدخلت رمز دخول غير صحيح عدّة مرات. يُرجى إعادة المحاولة لاحقًا.</translation>
 <translation id="3660234220361471169">غير موثوق بها</translation>
 <translation id="3664511988987167893">رمز الإضافة</translation>
 <translation id="3665589677786828986">‏اكتشف Chrome أن بعض إعداداتك تم إتلافها من قبل برنامج آخر وإعادة تعيينها للحالة التلقائية الأصلية.</translation>
@@ -2685,6 +2689,7 @@
 <translation id="3743842571276656710">أدخِل رقم التعريف الشخصي للإقران مع الجهاز <ph name="DEVICE_NAME" /></translation>
 <translation id="3747077776423672805">‏لإزالة التطبيقات، انتقِل إلى "الإعدادات" &gt; "متجر Google Play" &gt; إدارة إعدادات Android المفضّلة &gt; "التطبيقات" أو "مدير التطبيقات". ثم انقر على التطبيق الذي تريد إلغاء تثبيته (عليك التمرير السريع لليمين أو اليسار للعثور على التطبيق)، ثم انقر على "إلغاء التثبيت" أو "إيقاف".</translation>
 <translation id="3747220812138541072">عرض اقتراحات نصية مضمّنة تظهر أثناء الكتابة</translation>
+<translation id="3747603683749989726">هل تريد تفعيل ميزة الأمان المُحسّن؟</translation>
 <translation id="3748706263662799310">الإبلاغ عن خطأ</translation>
 <translation id="3750562496035670393">‏حفظ متصفّح Chrome كلمة مرورك على هذا الجهاز، ويمكنك حفظها في حسابك على Google بدلاً من ذلك. وبعدها، ستكون جميع كلمات المرور في حسابك على Google متوفّرة أيضًا طالما أنّك مسجّلٌ الدخول إلى الحساب.</translation>
 <translation id="3752253558646317685">اطلب من طفلك مواصلة رفع إصبعه لحفظ بصمة الإصبع.</translation>
@@ -3197,6 +3202,7 @@
 <translation id="4279129444466079448">‏يمكنك تثبيت <ph name="PROFILE_LIMIT" /> ملف شخصي لبطاقة eSIM كحد أقصى على هذا الجهاز. لإضافة ملف شخصي آخر، عليك أولاً إزالة أحد الملفات الشخصية الحالية.</translation>
 <translation id="4281844954008187215">بنود الخدمة</translation>
 <translation id="4282196459431406533">‏Smart Lock قيد التفعيل</translation>
+<translation id="4284755288573763878">هل تريد إيقاف ميزة الأمان المُحسّن؟</translation>
 <translation id="4285418559658561636">تحديث كلمة المرور</translation>
 <translation id="4285498937028063278">إزالة تثبيت</translation>
 <translation id="428565720843367874">تعذّر برنامج مكافحة الفيروسات بشكل غير متوقع أثناء فحص هذا الملف.</translation>
@@ -4669,6 +4675,7 @@
 <translation id="5865508026715185451">سيتم إيقاف تطبيق <ph name="APP_NAME" /> مؤقتًا بعد قليل.</translation>
 <translation id="586567932979200359">أنت تشغّل <ph name="PRODUCT_NAME" /> من صورته على القرص. ويسمح لك تثبيته على الكمبيوتر بتشغيله بدون الحاجة إلى صورة القرص، كما يضمن ذلك الحفاظ على تحديثه بشكل مستمر.</translation>
 <translation id="5865733239029070421">‏يُرسِل إحصاءات الاستخدام وتقارير الأعطال إلى Google تلقائيًا.</translation>
+<translation id="5865848066829891215">مشاكل في اتصال الشبكة</translation>
 <translation id="5867841422488265304">البحث عن عنوان الويب أو إدخاله</translation>
 <translation id="5869029295770560994">حسنًا</translation>
 <translation id="5869522115854928033">كلمات المرور المحفوظة</translation>
@@ -5963,6 +5970,7 @@
 <translation id="7243784282103630670">‏حدث خطأ أثناء ترقية نظام التشغيل Linux. ستتم استعادة الحاوية باستخدام النسخة الاحتياطية.</translation>
 <translation id="7245628041916450754">‏<ph name="WIDTH" /> x <ph name="HEIGHT" /> (الأفضل)</translation>
 <translation id="7246230585855757313">أعِد إدخال مفتاح الأمان وحاول مجددًا</translation>
+<translation id="7249197363678284330">يمكنك تغيير هذا الإعداد في شريط العناوين.</translation>
 <translation id="7250616558727237648">لم يستجِب الجهاز الذي تحاول المشاركة معه لطلبك. يُرجى إعادة المحاولة.</translation>
 <translation id="725109152065019550">عذرًا، أوقف المشرف التخزين الخارجي بحسابك.</translation>
 <translation id="7251346854160851420">خلفية تلقائية</translation>
@@ -7722,6 +7730,7 @@
 <translation id="9026731007018893674">تنزيل</translation>
 <translation id="9026852570893462412">قد تستغرق هذه العملية بضع دقائق. جارٍ تنزيل الآلة الافتراضية.</translation>
 <translation id="9027459031423301635">فتح الرابط في &amp;علامة تبويب جديدة</translation>
+<translation id="9029667986262585240">تم إيقاف ميزة "الحماية المُحسّنة للتصفّح الآمن" في حسابك.</translation>
 <translation id="9030515284705930323">‏لم تمكّن مؤسستك متجر Google Play لحسابك. اتصل بالمشرف للحصول على مزيد من المعلومات.</translation>
 <translation id="9030754204056345429">أسرع</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_bn.xtb b/chrome/app/resources/generated_resources_bn.xtb
index 60ae3aa3..056b2313 100644
--- a/chrome/app/resources/generated_resources_bn.xtb
+++ b/chrome/app/resources/generated_resources_bn.xtb
@@ -416,6 +416,7 @@
            • কুকি ও সাইটের অন্যান্য স্বল্পস্থায়ী ডেটা মুছে দেবে
             <ph name="LINE_BREAKS" />
            বুকমার্ক, ইতিহাস ও সেভ করে রাখা পাসওয়ার্ডে প্রভাব পড়বে না।</translation>
+<translation id="1425233109861512854">অ্যাক্সেস কোড শনাক্ত করা যায়নি</translation>
 <translation id="1426410128494586442">হ্যাঁ</translation>
 <translation id="142655739075382478"><ph name="APP_NAME" /> ব্লক করা আছে</translation>
 <translation id="1426870617281699524">'আবার চেষ্টা করুন' এ ক্লিক করে কম্পিউটার স্ক্রিনে দেখানো প্রম্পটে যা করতে বলা হবে তাই করুন</translation>
@@ -583,6 +584,7 @@
 <translation id="1593926297800505364">পেমেন্ট পদ্ধতি সেভ করুন</translation>
 <translation id="1595492813686795610">Linux আপগ্রেড হচ্ছে</translation>
 <translation id="1596286373007273895">উপলভ্য</translation>
+<translation id="1596709061955594992">ব্লুটুথ বন্ধ আছে। উপলভ্য ডিভাইস দেখতে, ব্লুটুথ চালু করুন।</translation>
 <translation id="1598233202702788831">আপনার প্রশাসক দ্বারা আপডেট অক্ষম করা হয়েছে।</translation>
 <translation id="1600857548979126453">পৃষ্ঠা ডিবাগার ব্যাকএন্ড অ্যাক্সেস করুন</translation>
 <translation id="1601560923496285236">প্রয়োগ করুন</translation>
@@ -1144,6 +1146,7 @@
 <translation id="2148892889047469596">ট্যাব কাস্ট করুন</translation>
 <translation id="2149973817440762519">বুকমার্ক সম্পাদনা করুন</translation>
 <translation id="2150139952286079145">গন্তব্যস্থল খুঁজুন</translation>
+<translation id="2150491840563706000">আপনার, এই ডিসপ্লেতে কাস্ট করার অনুমতি নেই</translation>
 <translation id="2150661552845026580">"<ph name="EXTENSION_NAME" />" জুড়বেন?</translation>
 <translation id="2151576029659734873">ভুল ট্যাব সূচি দেওয়া হয়েছে৷</translation>
 <translation id="2152281589789213846">আপনার প্রোফাইলে প্রিন্টার যোগ করুন</translation>
@@ -2611,6 +2614,7 @@
 <translation id="3654045516529121250">আপনার ব্যবহারযোগ্যতার সেটিংস পড়ুন</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{একটি ফাইলে এটার স্থায়ী অ্যাক্সেস আছে৷}one{#টি ফাইলে এটার স্থায়ী অ্যাক্সেস আছে৷}other{#টি ফাইলে এটার স্থায়ী অ্যাক্সেস আছে৷}}</translation>
 <translation id="3658871634334445293">TrackPoint অ্যাক্সিলারেশন</translation>
+<translation id="3659929705630080526">আপনি অনেক বার ভুল অ্যাক্সেস কোড লিখেছেন। পরে আবার চেষ্টা করুন</translation>
 <translation id="3660234220361471169">অবিশ্বস্থ</translation>
 <translation id="3664511988987167893">এক্সটেনশনের আইকন</translation>
 <translation id="3665589677786828986">Chrome শনাক্ত করেছে যে অন্য কোনো প্রোগ্রাম আপনার কিছু সেটিংয়ের ক্ষতি করেছে এবং সেগুলিকে তাদের মূল ডিফল্টে রিসেট করেছে।</translation>
@@ -2694,6 +2698,7 @@
 <translation id="3743842571276656710"><ph name="DEVICE_NAME" />-এর সঙ্গে পেয়ার করতে পিন লিখুন</translation>
 <translation id="3747077776423672805">অ্যাপগুলি সরাতে, সেটিংস &gt; Google Play Store &gt; Android অভিরুচি ম্যানেজ করুন &gt; অ্যাপ বা অ্যাপ্লিকেশন ম্যানেজার বিকল্পে যান। এরপর যে অ্যাপটিকে আনইনস্টল করতে চান তাতে ট্যাপ করুন (অ্যাপটি খোঁজার জন্য আপনাকে ডানদিক বা বাঁদিকে সোয়াইপ করতে হতে পারে)। এরপর আনইনস্টল করুন বা বন্ধ করুন বিকল্পে ট্যাপ করুন।</translation>
 <translation id="3747220812138541072">টাইপ করার সাথে সাথে প্রদর্শিত হবে এমন ইনলাইন লেখার সাজেশন দেখানো হবে</translation>
+<translation id="3747603683749989726">উন্নত নিরাপত্তা চালু করবেন?</translation>
 <translation id="3748706263662799310">একটি বাগ রিপোর্ট করুন</translation>
 <translation id="3750562496035670393">Chrome আপনার পাসওয়ার্ড এই ডিভাইসে সেভ করেছে, তবে এর পরিবর্তে আপনি এটি Google অ্যাকাউন্টেও সেভ করতে পারবেন। এরপর সাইন-ইন করলে আপনার Google অ্যাকাউন্টে সেভ করা সব পাসওয়ার্ড উপলভ্য হবে।</translation>
 <translation id="3752253558646317685">আঙ্গুলের ছাপ সেভ করতে আপনার সন্তানকে তার আঙ্গুল তুলতে বলুন</translation>
@@ -3208,6 +3213,7 @@
 <translation id="4279129444466079448">আপনি এই ডিভাইসে সর্বাধিক <ph name="PROFILE_LIMIT" />টি eSIM প্রোফাইল ইনস্টল করতে পারবেন। আরেকটি প্রোফাইল যোগ করতে, আগে থেকে আছে এমন প্রোফাইল সরান।</translation>
 <translation id="4281844954008187215">পরিষেবার শর্তাদি</translation>
 <translation id="4282196459431406533">Smart Lock চালু আছে</translation>
+<translation id="4284755288573763878">উন্নত নিরাপত্তা বন্ধ করবেন?</translation>
 <translation id="4285418559658561636">পাসওয়ার্ড আপডেট করুন</translation>
 <translation id="4285498937028063278">আনপিন</translation>
 <translation id="428565720843367874">এই ফাইলটি স্ক্যান করার সময় এন্টি ভাইরাস সফ্টওয়্যার অপ্রত্যাশিতভাবে ব্যর্থ হয়েছে৷</translation>
@@ -4682,6 +4688,7 @@
 <translation id="5865508026715185451"><ph name="APP_NAME" /> শীঘ্রই পজ করা হবে</translation>
 <translation id="586567932979200359">আপনি <ph name="PRODUCT_NAME" /> থেকে এটির ডিস্ক ছবি চালাচ্ছেন৷ এটিকে আপনার কম্পিউটারে ইনস্টল করা হলে তা ডিস্ক ছবি ছাড়াই এটিকে চালাতে দেয় এবং এটিকে আপ টু ডেট আছে কিনা তা নিশ্চিত করে৷</translation>
 <translation id="5865733239029070421">ব্যবহারের পরিসংখ্যান এবং ক্র্যাশ রিপোর্ট নিজে থেকেই Google-কে পাঠায়</translation>
+<translation id="5865848066829891215">নেটওয়ার্ক কমিউনিকেশন সংক্রান্ত সমস্যা</translation>
 <translation id="5867841422488265304">ওয়েব ঠিকানা খুঁজুন বা লিখুন</translation>
 <translation id="5869029295770560994">ঠিক আছে, বুঝেছি</translation>
 <translation id="5869522115854928033">সংরক্ষিত পাসওয়ার্ড</translation>
@@ -5975,6 +5982,7 @@
 <translation id="7243784282103630670">Linux আপগ্রেড করার সময় একটি সমস্যা হয়েছে। আপনার ব্যাক-আপ ব্যবহার করে আমরা কন্টেনারটিকে আগের অবস্থায় ফিরিয়ে আনব।</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> x <ph name="HEIGHT" /> (সর্বোত্তম)</translation>
 <translation id="7246230585855757313">আপনার নিরাপত্তা কী আবার লিখুন এবং আবার চেষ্টা করুন</translation>
+<translation id="7249197363678284330">অ্যাড্রেস বার থেকে এই সেটিং পরিবর্তন করুন।</translation>
 <translation id="7250616558727237648">আপনি যে ডিভাইস শেয়ার করছেন সেটি থেকে কোনও প্রতিক্রিয়া পাওয়া যায়নি। আবার চেষ্টা করুন।</translation>
 <translation id="725109152065019550">দুঃখিত, আপনার প্রশাসক আপনার অ্যাকাউন্টে এক্সটার্নাল স্টোরেজ বন্ধ করেছে৷</translation>
 <translation id="7251346854160851420">ডিফল্ট ওয়ালপেপার</translation>
@@ -7729,6 +7737,7 @@
 <translation id="9026731007018893674">ডাউনলোড</translation>
 <translation id="9026852570893462412">এতে কয়েক মিনিট সময় লাগতে পারে। ভার্চুয়াল মেশিন ডাউনলোড হচ্ছে।</translation>
 <translation id="9027459031423301635">নতুন ট্যাবে লিঙ্ক খুলুন</translation>
+<translation id="9029667986262585240">আপনার অ্যাকাউন্টে উন্নত নিরাপদ ব্রাউজিং আপনি বন্ধ করে রেখেছেন।</translation>
 <translation id="9030515284705930323">আপনার প্রতিষ্ঠান আপনার অ্যাকাউন্টের জন্য Google Play Store চালু করেনি। আরও তথ্যের জন্য আপনার অ্যাডমিনিস্ট্রেটরের সাথে যোগাযোগ করুন।</translation>
 <translation id="9030754204056345429">আরও দ্রুত</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_ca.xtb b/chrome/app/resources/generated_resources_ca.xtb
index f7e0a89..244ca2ad 100644
--- a/chrome/app/resources/generated_resources_ca.xtb
+++ b/chrome/app/resources/generated_resources_ca.xtb
@@ -268,7 +268,7 @@
 <translation id="126710816202626562">Idioma de traducció:</translation>
 <translation id="126768002343224824">16x</translation>
 <translation id="1272079795634619415">Atura</translation>
-<translation id="1272508081857842302">S'estan obrint els <ph name="BEGIN_LINK" />enllaços admesos<ph name="END_LINK" /></translation>
+<translation id="1272508081857842302">Obertura d'<ph name="BEGIN_LINK" />enllaços compatibles<ph name="END_LINK" /></translation>
 <translation id="1272978324304772054">Aquest compte d'usuari no pertany al domini on s'ha registrat el dispositiu. Si el voleu registrar en un domini diferent, primer cal que apliqueu el procés de recuperació del dispositiu.</translation>
 <translation id="1274997165432133392">Galetes i altres dades dels llocs web</translation>
 <translation id="1275718070701477396">Seleccionat</translation>
diff --git a/chrome/app/resources/generated_resources_cs.xtb b/chrome/app/resources/generated_resources_cs.xtb
index 5be49f81e..fb1a254 100644
--- a/chrome/app/resources/generated_resources_cs.xtb
+++ b/chrome/app/resources/generated_resources_cs.xtb
@@ -415,6 +415,7 @@
            • Smažou se soubory cookie a jiná dočasná data webů.
             <ph name="LINE_BREAKS" />
            Záložky, historie ani uložená hesla nebudou dotčena.</translation>
+<translation id="1425233109861512854">Přístupový kód nebyl rozpoznán</translation>
 <translation id="1426410128494586442">Ano</translation>
 <translation id="142655739075382478">Aplikace <ph name="APP_NAME" /> je zablokována</translation>
 <translation id="1426870617281699524">Klikněte na Zkusit znovu a potvrďte výzvu na počítači</translation>
@@ -581,6 +582,7 @@
 <translation id="1593926297800505364">Uložit platební metodu</translation>
 <translation id="1595492813686795610">Linux se upgraduje</translation>
 <translation id="1596286373007273895">Dostupné</translation>
+<translation id="1596709061955594992">Bluetooth je vypnutý. Pokud chcete zobrazit dostupná zařízení, zapněte Bluetooth.</translation>
 <translation id="1598233202702788831">Administrátor aktualizace zakázal.</translation>
 <translation id="1600857548979126453">Získat přístup k ladicímu programu stránky na serveru</translation>
 <translation id="1601560923496285236">Použít</translation>
@@ -1133,6 +1135,7 @@
 <translation id="2148892889047469596">Odeslat kartu</translation>
 <translation id="2149973817440762519">Upravit záložku</translation>
 <translation id="2150139952286079145">Vyhledat cíle</translation>
+<translation id="2150491840563706000">K odeslání na tento displej nemáte oprávnění</translation>
 <translation id="2150661552845026580">Přidat rozšíření <ph name="EXTENSION_NAME" />?</translation>
 <translation id="2151576029659734873">Zadaný index karty je neplatný.</translation>
 <translation id="2152281589789213846">Přidat tiskárny do profilu</translation>
@@ -2597,6 +2600,7 @@
 <translation id="3654045516529121250">Čtení nastavení usnadnění přístupu</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{Má trvalý přístup k jednomu souboru.}few{Má trvalý přístup ke # souborům.}many{Má trvalý přístup k # souboru.}other{Má trvalý přístup k # souborům.}}</translation>
 <translation id="3658871634334445293">Akcelerace TrackPointu</translation>
+<translation id="3659929705630080526">Zadali jste příliš mnohokrát nesprávný přístupový kód. Zkuste to znovu později</translation>
 <translation id="3660234220361471169">Nedůvěryhodný</translation>
 <translation id="3664511988987167893">Ikona rozšíření</translation>
 <translation id="3665589677786828986">Prohlížeč Chrome zjistil, že některá z vašich nastavení byla upravena jiným programem, a obnovil je na původní výchozí hodnoty.</translation>
@@ -2680,6 +2684,7 @@
 <translation id="3743842571276656710">Zadejte PIN pro spárování se zařízením <ph name="DEVICE_NAME" /></translation>
 <translation id="3747077776423672805">Chcete-li odstranit aplikace, přejděte do Nastavení &gt; Obchod Google Play &gt; Spravovat nastavení aplikací Android &gt; Aplikace nebo Správce aplikací. Poté klepněte na aplikaci, kterou chcete odinstalovat (možná bude třeba aplikacemi listovat doprava či doleva). Následně klepněte na Odinstalovat nebo Deaktivovat.</translation>
 <translation id="3747220812138541072">Zobrazovat na řádku návrhy psaní, zatímco budete psát</translation>
+<translation id="3747603683749989726">Zapnout vylepšené zabezpečení?</translation>
 <translation id="3748706263662799310">Nahlásit chybu</translation>
 <translation id="3750562496035670393">Chrome heslo uložil do tohoto zařízení, ale můžete ho místo toho uložit do svého účtu Google. Když budete přihlášeni, budou pak k dispozici i všechna hesla ve vašem účtu Google.</translation>
 <translation id="3752253558646317685">Nechte dítě opakovaně přikládat a zvedat prst, aby se uložil celý otisk prstu</translation>
@@ -3194,6 +3199,7 @@
 <translation id="4279129444466079448">Do tohoto zařízení můžete nainstalovat nejvýše <ph name="PROFILE_LIMIT" /> profilů eSIM. Pokud chcete přidat další profil, nejdříve některý z existujících odstraňte.</translation>
 <translation id="4281844954008187215">Smluvní podmínky</translation>
 <translation id="4282196459431406533">Funkce Smart Lock je zapnutá</translation>
+<translation id="4284755288573763878">Vypnout vylepšené zabezpečení?</translation>
 <translation id="4285418559658561636">Aktualizovat heslo</translation>
 <translation id="4285498937028063278">Odepnout</translation>
 <translation id="428565720843367874">Při kontrole tohoto souboru došlo k neočekávané chybě antivirového softwaru.</translation>
@@ -3334,7 +3340,7 @@
 <translation id="4430369329743628066">Byla přidána záložka</translation>
 <translation id="4432621511648257259">Nesprávné heslo</translation>
 <translation id="443454694385851356">Starší (není bezpečné)</translation>
-<translation id="443475966875174318">Aktualizujte nebo odstraňte nekompatibilní aplikace</translation>
+<translation id="443475966875174318">Aktualizovat nebo odstranit nekompatibilní aplikace</translation>
 <translation id="4438043733494739848">Průhledné</translation>
 <translation id="4440097423000553826">Web <ph name="WEBSITE" /> vám do telefonu poslal oznámení. Klepněte na oznámení <ph name="NOTIFICATIONTITLE" /> a podle pokynů potvrďte, že jste to skutečně vy.</translation>
 <translation id="4441124369922430666">Chcete tuto aplikaci automaticky spustit, když se zařízení zapne?</translation>
@@ -4666,6 +4672,7 @@
 <translation id="5865508026715185451">Aplikace <ph name="APP_NAME" /> se brzy pozastaví</translation>
 <translation id="586567932979200359">Aplikaci <ph name="PRODUCT_NAME" /> spouštíte z bitové kopie disku. Pokud ji nainstalujte do svého počítače, můžete ji spouštět bez bitové kopie disku a umožníte její aktualizace.</translation>
 <translation id="5865733239029070421">Automaticky odesílá statistiky o využívání a zprávy o selhání do Googlu</translation>
+<translation id="5865848066829891215">Problémy se síťovou komunikací</translation>
 <translation id="5867841422488265304">Zadejte dotaz nebo webovou adresu</translation>
 <translation id="5869029295770560994">Dobře, rozumím</translation>
 <translation id="5869522115854928033">Uložená hesla</translation>
@@ -5959,6 +5966,7 @@
 <translation id="7243784282103630670">Při upgradování Linuxu došlo k chybě. Kontejner obnovíme z vaší zálohy.</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> × <ph name="HEIGHT" /> (nejlepší)</translation>
 <translation id="7246230585855757313">Opět bezpečností klíč vložte a zkuste to znovu</translation>
+<translation id="7249197363678284330">Toto nastavení můžete změnit na adresním řádku.</translation>
 <translation id="7250616558727237648">Cílové zařízení neodpovědělo. Zkuste to znovu.</translation>
 <translation id="725109152065019550">Je nám líto, správce ve vašem účtu zakázal používání externích úložišť.</translation>
 <translation id="7251346854160851420">Výchozí tapeta</translation>
@@ -7714,6 +7722,7 @@
 <translation id="9026731007018893674">stažený soubor</translation>
 <translation id="9026852570893462412">Tento proces může trvat několik minut. Probíhá stahování virtuálního počítače.</translation>
 <translation id="9027459031423301635">Otevří&amp;t odkaz v nové kartě</translation>
+<translation id="9029667986262585240">Ve svém účtu jste vypnuli Vylepšené Bezpečné prohlížení.</translation>
 <translation id="9030515284705930323">Vaše organizace pro váš účet Obchod Google Play nepovolila. Další informace získáte u svého administrátora.</translation>
 <translation id="9030754204056345429">Rychlejší</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_en-GB.xtb b/chrome/app/resources/generated_resources_en-GB.xtb
index 451dfb6..be776e24 100644
--- a/chrome/app/resources/generated_resources_en-GB.xtb
+++ b/chrome/app/resources/generated_resources_en-GB.xtb
@@ -395,6 +395,7 @@
 <translation id="1408980562518920698">Manage personal information</translation>
 <translation id="1410197035576869800">App Icon</translation>
 <translation id="1410616244180625362">Continue allowing <ph name="HOST" /> to access your camera</translation>
+<translation id="1410797069449661718">Scroll towards the first tab</translation>
 <translation id="1410806973194718079">Unable to check policies</translation>
 <translation id="1412681350727866021">Additional extensions</translation>
 <translation id="1414315029670184034">Don't allow sites to use your camera</translation>
@@ -732,6 +733,7 @@
 <translation id="1725562816265788801">Tab scrolling</translation>
 <translation id="1729533290416704613">It also controls what page is shown when you search from the Omnibox.</translation>
 <translation id="1730917990259790240"><ph name="BEGIN_PARAGRAPH1" />To remove apps, go to Settings &gt; Google Play Store &gt; Manage Android preferences &gt; Apps or Application manager. Then tap the app that you want to uninstall (you may need to swipe right or left to find the app). Then tap Uninstall or Disable.<ph name="END_PARAGRAPH1" /></translation>
+<translation id="1730989807608739928">Scroll towards the last tab</translation>
 <translation id="1731911755844941020">Sending request...</translation>
 <translation id="1733383495376208985">Encrypt synced data with your own <ph name="BEGIN_LINK" />sync passphrase<ph name="END_LINK" />. This doesn't include payment methods and addresses from Google Pay.</translation>
 <translation id="1734212868489994726">Light blue</translation>
@@ -3128,6 +3130,7 @@
 <translation id="4165942112764990069"><ph name="USER_EMAIL" /> does not belong to a valid organization. Contact your administrator. If you are an administrator, you can set up your organisation by visiting: g.co/ChromeEnterpriseAccount</translation>
 <translation id="4167686856635546851">Sites usually use Javascript to display interactive features, like video games or web forms</translation>
 <translation id="4168015872538332605">Some settings belonging to <ph name="PRIMARY_EMAIL" /> are being shared with you. These settings only affect your account when using multiple sign-in.</translation>
+<translation id="4168651806173792090"><ph name="NETWORK_NAME" /> ending in <ph name="LAST_FOUR_DIGITS" /></translation>
 <translation id="4169535189173047238">Don't allow</translation>
 <translation id="4170314459383239649">Clear On Exit</translation>
 <translation id="417096670996204801">Choose a profile</translation>
@@ -3358,6 +3361,7 @@
 <translation id="4432621511648257259">Password incorrect</translation>
 <translation id="443454694385851356">Legacy (insecure)</translation>
 <translation id="443475966875174318">Update or remove incompatible applications</translation>
+<translation id="4435634292635092844">View photos from your phone's camera roll on your <ph name="DEVICE_TYPE" />. <ph name="LINK_BEGIN" />Learn more<ph name="LINK_END" /></translation>
 <translation id="4438043733494739848">Transparent</translation>
 <translation id="4440097423000553826"><ph name="WEBSITE" /> sent a notification to your phone. To confirm that it's you, tap the '<ph name="NOTIFICATIONTITLE" />' notification and follow the steps.</translation>
 <translation id="4441124369922430666">Do you want to automatically start this app when the machine turns on?</translation>
@@ -4616,6 +4620,7 @@
 <translation id="5789643057113097023">.</translation>
 <translation id="5790085346892983794">Success</translation>
 <translation id="5790651917470750848">Port forward already exists</translation>
+<translation id="5792295754950501287">More actions for <ph name="CARD_DESCRIPTION" /></translation>
 <translation id="5792728279623964091">Please tap your power button</translation>
 <translation id="5793339252089865437">If you download the update over your mobile network, it could result in overage charges.</translation>
 <translation id="5794034487966529952">Desk <ph name="DESK_TITLE" /> has <ph name="NUM_BROWSERS" /> browser windows open</translation>
@@ -7027,6 +7032,7 @@
 <translation id="8264024885325823677">This setting is managed by your administrator.</translation>
 <translation id="8264718194193514834">"<ph name="EXTENSION_NAME" />" triggered full screen.</translation>
 <translation id="826511437356419340">Entered window overview mode. Swipe to navigate, or press tab if using a keyboard.</translation>
+<translation id="8265671588726449108">{COUNT,plural, =1{Your incognito window won't open after you relaunch}other{Your {COUNT} incognito windows won't reopen after you relaunch}}</translation>
 <translation id="8266947622852630193">All input methods</translation>
 <translation id="8267539814046467575">Add printer</translation>
 <translation id="8267961145111171918"><ph name="BEGIN_PARAGRAPH1" />This is general information about this device and how it's used (such as battery level, system and app activity and errors). The data will be used to improve Android, and some aggregated information will also help Google apps and partners, such as Android developers, make their apps and products better.<ph name="END_PARAGRAPH1" />
diff --git a/chrome/app/resources/generated_resources_es.xtb b/chrome/app/resources/generated_resources_es.xtb
index 5f540e4..ccd9c2c 100644
--- a/chrome/app/resources/generated_resources_es.xtb
+++ b/chrome/app/resources/generated_resources_es.xtb
@@ -3428,7 +3428,7 @@
 <translation id="4534661889221639075">Inténtalo de nuevo.</translation>
 <translation id="4535127706710932914">Perfil predeterminado</translation>
 <translation id="4535767533210902251">El sensor de huellas digitales está en la tecla de arriba a la derecha de tu teclado. Tócalo ligeramente con un dedo.</translation>
-<translation id="4536140153723794651">Sitios web que pueden usar cookies siempre</translation>
+<translation id="4536140153723794651">Sitios que pueden usar cookies siempre</translation>
 <translation id="4538163005498287211">Según tu fondo de pantalla</translation>
 <translation id="4538417792467843292">Eliminar palabra</translation>
 <translation id="4538792345715658285">Instalada por una política empresarial</translation>
@@ -6070,7 +6070,7 @@
 <translation id="7364796246159120393">Seleccionar archivo</translation>
 <translation id="7365076891350562061">Tamaño del monitor</translation>
 <translation id="7366316827772164604">Buscando dispositivos cercanos...</translation>
-<translation id="7366415735885268578">Añadir un sitio web</translation>
+<translation id="7366415735885268578">Añadir un sitio</translation>
 <translation id="7366909168761621528">Datos de navegación</translation>
 <translation id="7367714965999718019">Generador de códigos QR</translation>
 <translation id="7367758267317684635">Verás tu historial de Chrome en todos tus dispositivos sincronizados</translation>
diff --git a/chrome/app/resources/generated_resources_fa.xtb b/chrome/app/resources/generated_resources_fa.xtb
index 8e6272d..abe64a6 100644
--- a/chrome/app/resources/generated_resources_fa.xtb
+++ b/chrome/app/resources/generated_resources_fa.xtb
@@ -416,6 +416,7 @@
            • کوکی‌ها و دیگر داده‌های موقت سایت حذف می‌شوند
             <ph name="LINE_BREAKS" />
            نشانک‌ها، سابقه، و گذرواژه‌های ذخیره‌شده تحت‌تأثیر قرار نمی‌گیرند.</translation>
+<translation id="1425233109861512854">کد دسترسی شناخته نشد</translation>
 <translation id="1426410128494586442">بله</translation>
 <translation id="142655739075382478"><ph name="APP_NAME" /> مسدود شده است</translation>
 <translation id="1426870617281699524">روی «دوباره امتحان کنید» کلیک کنید و درخواست را در رایانه بپذیرید</translation>
@@ -580,6 +581,7 @@
 <translation id="1593926297800505364">ذخیره روش پرداخت</translation>
 <translation id="1595492813686795610">‏Linux درحال ارتقا است</translation>
 <translation id="1596286373007273895">دردسترس</translation>
+<translation id="1596709061955594992">بلوتوث خاموش است. برای دیدن دستگاه‌های دردسترس، بلوتوث را روشن کنید.</translation>
 <translation id="1598233202702788831">سرپرست سیستم شما به‌روزرسانی‌ها را غیرفعال کرده است.</translation>
 <translation id="1600857548979126453">دسترسی به مرحله پایانی اشکال‌زدای صفحه</translation>
 <translation id="1601560923496285236">اعمال</translation>
@@ -1140,6 +1142,7 @@
 <translation id="2148892889047469596">برگه فرستادن</translation>
 <translation id="2149973817440762519">ویرایش نشانک</translation>
 <translation id="2150139952286079145">جستجوی مقصدها</translation>
+<translation id="2150491840563706000">اجازه ندارید به این نمایشگر محتوا ارسال کنید</translation>
 <translation id="2150661552845026580">"<ph name="EXTENSION_NAME" />" اضافه شود؟</translation>
 <translation id="2151576029659734873">فهرست برگه نامعتبر وارد شده است.</translation>
 <translation id="2152281589789213846">افزودن چاپگرها به نمایه</translation>
@@ -2608,6 +2611,7 @@
 <translation id="3654045516529121250">خواندن تنظیمات دسترس‌پذیری خودتان</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{به یک فایل دسترسی دائم دارد.}one{به # فایل دسترسی دائم دارد.}other{به # فایل دسترسی دائم دارد.}}</translation>
 <translation id="3658871634334445293">‏شتاب TrackPoint</translation>
+<translation id="3659929705630080526">کد دسترسی نادرستی را چند بار وارد کرده‌اید. بعداً دوباره امتحان کنید</translation>
 <translation id="3660234220361471169">غیر قابل اطمینان</translation>
 <translation id="3664511988987167893">نماد افزونه</translation>
 <translation id="3665589677786828986">‏Chrome تشخیص داد که برنامه دیگری برخی از تنظیمات شما را خراب کرده است و این تنظیمات را به پیش‌فرض‌های اولیه آن‌ها بازنشانی کرد.</translation>
@@ -2691,6 +2695,7 @@
 <translation id="3743842571276656710">برای مرتبط شدن با <ph name="DEVICE_NAME" />، پین را وارد کنید</translation>
 <translation id="3747077776423672805">‏برای حذف برنامه‌ها به «تنظیمات &gt; فروشگاه Google Play &gt; مدیریت اولویت‌های Android &gt; برنامه‌ها یا مدیر برنامه» بروید. سپس روی برنامه‌ای که می‌خواهید حذف نصب شود ضربه بزنید (ممکن است برای پیدا کردن برنامه نیاز باشد صفحه را تند به‌راست یا چپ بکشید). سپس روی «حذف نصب» یا «غیرفعال کردن» ضربه بزنید.</translation>
 <translation id="3747220812138541072">پیشنهادهای نوشتار به‌خط درحین تایپ کردن شما نشان داده می‌شود</translation>
+<translation id="3747603683749989726">امنیت پیشرفته روشن شود؟</translation>
 <translation id="3748706263662799310">گزارش یک اشکال</translation>
 <translation id="3750562496035670393">‏Chrome گذرواژه‌تان را در این دستگاه ذخیره کرده است، اما به‌جای دستگاه می‌توانید آن را در «حساب Google» خودتان ذخیره کنید. بدین‌ترتیب، هرگاه به سیستم وارد شده باشید، همه گذرواژه‌های ذخیره‌شده در «حساب Google» شما نیز دردسترس خواهد بود.</translation>
 <translation id="3752253558646317685">از فرزندتان بخواهید مدام انگشتش را بردارد تا اثر انگشت ذخیره شود</translation>
@@ -3206,6 +3211,7 @@
 <translation id="4279129444466079448">می‌توانید تا <ph name="PROFILE_LIMIT" /> نمایه سیم‌کارت داخلی را در این دستگاه نصب کنید. برای افزودن نمایه‌ای دیگر، ابتدا یکی از نمایه‌ها موجود را بردارید.</translation>
 <translation id="4281844954008187215">شرایط استفاده از سرویس</translation>
 <translation id="4282196459431406533">‏Smart Lock روشن شده است</translation>
+<translation id="4284755288573763878">امنیت پیشرفته خاموش شود؟</translation>
 <translation id="4285418559658561636">به‌روزرسانی گذرواژه</translation>
 <translation id="4285498937028063278">برداشتن سنجاق</translation>
 <translation id="428565720843367874">نرم‌افزار آنتی‌ویروس به‌طور غیرمنتظره‌ای در هنگام اسکن کردن این فایل متوقف شد.</translation>
@@ -4679,6 +4685,7 @@
 <translation id="5865508026715185451"><ph name="APP_NAME" /> به‌زودی متوقف می‌شود</translation>
 <translation id="586567932979200359">شما <ph name="PRODUCT_NAME" /> را از تصویر دیسک آن اجرا می‌کنید. با نصب آن روی رایانه می‌توانید آن را بدون تصویر دیسک اجرا کنید، و مطمئن شوید که به روز نگه داشته خواهد شد.</translation>
 <translation id="5865733239029070421">‏به‌طور خودکار آمار کاربرد و گزارش‌های خرابی را به Google ارسال می‌کند</translation>
+<translation id="5865848066829891215">مشکلاتی در ارتباطات شبکه وجود دارد</translation>
 <translation id="5867841422488265304">نشانی وب را جستجو یا تایپ کنید</translation>
 <translation id="5869029295770560994">بله متوجه شدم</translation>
 <translation id="5869522115854928033">گذرواژه‌های ذخیره‌شده</translation>
@@ -5972,6 +5979,7 @@
 <translation id="7243784282103630670">‏هنگام ارتقای Linux، خطایی روی داد. محتوی را با‌استفاده از نسخه پشتیبان شما بازیابی می‌کنیم.</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> × <ph name="HEIGHT" /> (بهترین)</translation>
 <translation id="7246230585855757313">کلید امنیتی را مجدداً وارد کنید و دوباره امتحان کنید</translation>
+<translation id="7249197363678284330">این تنظیم را در نوار نشانی تغییر دهید.</translation>
 <translation id="7250616558727237648">دستگاهی که درحال هم‌رسانی با آن هستید پاسخی نداد. لطفاً دوباره امتحان کنید.</translation>
 <translation id="725109152065019550">متأسفیم، سرپرست سیستم، حافظه خارجی را برای حساب شما غیرفعال کرده است.</translation>
 <translation id="7251346854160851420">کاغذدیواری پیش‌فرض</translation>
@@ -7727,6 +7735,7 @@
 <translation id="9026731007018893674">بارگیری</translation>
 <translation id="9026852570893462412">این فرایند ممکن است چند دقیقه طول بکشد. درحال بارگیری دستگاه مجازی.</translation>
 <translation id="9027459031423301635">باز کردن پیوند در &amp;برگه جدید</translation>
+<translation id="9029667986262585240">«مرور ایمن پیشرفته» را در حسابتان خاموش کرده‌اید.</translation>
 <translation id="9030515284705930323">‏سازمان شما فروشگاه Google Play را برای حسابتان فعال نکرده است. برای اطلاعات بیشتر با سرپرستتان تماس بگیرید.</translation>
 <translation id="9030754204056345429">سریع‌تر</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_gu.xtb b/chrome/app/resources/generated_resources_gu.xtb
index 7a7ddeab..c9b3ecb 100644
--- a/chrome/app/resources/generated_resources_gu.xtb
+++ b/chrome/app/resources/generated_resources_gu.xtb
@@ -414,6 +414,7 @@
            • કુકી અને સાઇટનો અન્ય હંગામી ડેટા ડિલીટ થઈ જશે
             <ph name="LINE_BREAKS" />
            બુકમાર્ક, ઇતિહાસ અને સાચવેલા પાસવર્ડને કોઈ અસર થશે નહીં.</translation>
+<translation id="1425233109861512854">ઍક્સેસ કોડ ઓળખી શકાયો નથી</translation>
 <translation id="1426410128494586442">હા</translation>
 <translation id="142655739075382478"><ph name="APP_NAME" /> બ્લૉક કરેલી છે</translation>
 <translation id="1426870617281699524">ફરીથી પ્રયાસ કરો પર ક્લિક કરો અને તમારા કમ્પ્યુટર પર સંકેતને સ્વીકારો</translation>
@@ -576,6 +577,7 @@
 <translation id="1593926297800505364">ચુકવણી પદ્ધતિ સાચવો</translation>
 <translation id="1595492813686795610">Linux અપગ્રેડ થાય છે</translation>
 <translation id="1596286373007273895">ઉપલબ્ધ</translation>
+<translation id="1596709061955594992">બ્લૂટૂથ બંધ છે. ઉપલબ્ધ ડિવાઇસ જોવા માટે, બ્લૂટૂથ ચાલુ કરો.</translation>
 <translation id="1598233202702788831">તમારા વ્યવસ્થાપક દ્વારા અપડેટ્સ અક્ષમ કરાયા છે.</translation>
 <translation id="1600857548979126453">પેજ ડિબગર બૅકએન્ડ ઍક્સેસ કરો</translation>
 <translation id="1601560923496285236">લાગુ કરો</translation>
@@ -1128,6 +1130,7 @@
 <translation id="2148892889047469596">ટેબ કાસ્ટ કરો</translation>
 <translation id="2149973817440762519">બુકમાર્કમાં ફેરફાર કરો</translation>
 <translation id="2150139952286079145">નિર્ધારિત સ્થાન શોધો</translation>
+<translation id="2150491840563706000">તમને આ ડિસ્પ્લેમાં કાસ્ટ કરવાની પરવાનગી નથી</translation>
 <translation id="2150661552845026580">"<ph name="EXTENSION_NAME" />" ઉમેરીએ?</translation>
 <translation id="2151576029659734873">અમાન્ય ટૅબ અનુક્રમણિકા દાખલ કરી.</translation>
 <translation id="2152281589789213846">તમારી પ્રોફાઇલમાં પ્રિન્ટર ઉમેરો</translation>
@@ -2594,6 +2597,7 @@
 <translation id="3654045516529121250">તમારા ઍક્સેસિબિલિટી સેટિંગ વાંચો</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{તેને એક ફાઇલ માટે કાયમી ઍક્સેસ છે.}one{તેને # ફાઇલ માટે કાયમી ઍક્સેસ છે.}other{તેને # ફાઇલ માટે કાયમી ઍક્સેસ છે.}}</translation>
 <translation id="3658871634334445293">TrackPoint ઍક્સલરેશન</translation>
+<translation id="3659929705630080526">તમે ઘણી બધી વખત ખોટો ઍક્સેસ કોડ દાખલ કર્યો છે. થોડા સમય પછી ફરી પ્રયાસ કરો</translation>
 <translation id="3660234220361471169">અવિશ્વસનીય</translation>
 <translation id="3664511988987167893">એક્સ્ટેંશન આઇકન</translation>
 <translation id="3665589677786828986">Chromeને જાણ થઈ છે કે તમારા કેટલાક સેટિંગ બીજા પ્રોગ્રામ દ્વારા દૂષિત કરવામાં આવેલા અને તેમને તેમના મૂળ ડિફૉલ્ટ પર રીસેટ કર્યા છે.</translation>
@@ -2677,6 +2681,7 @@
 <translation id="3743842571276656710"><ph name="DEVICE_NAME" />ની સાથે જોડાણ કરવા માટે, પિન દાખલ કરો</translation>
 <translation id="3747077776423672805">ઍપને કાઢી નાખવા માટે, સેટિંગ &gt; Google Play Store &gt; Android પસંદગીઓ મેનેજ કરો &gt; ઍપ અથવા ઍપ્લિકેશન મેનેજર પર જાઓ. પછી તમે અનઇન્સ્ટૉલ કરવા માગો છો તે ઍપ પર ટૅપ કરો (ઍપ શોધવા માટે તમારે જમણે અથવા ડાબે સ્વાઇપ કરવું જરૂરી હોય શકે છે). પછી અનઇન્સ્ટૉલ કરો અથવા બંધ કરો પર ટૅપ કરો.</translation>
 <translation id="3747220812138541072">તમે ટાઇપ કરી રહ્યાં હો ત્યારે દેખાતા ઇનલાઇન લખવાના સૂચનો બતાવો</translation>
+<translation id="3747603683749989726">શું વધારેલી સુરક્ષાની સુવિધા ચાલુ કરીએ?</translation>
 <translation id="3748706263662799310">બગની જાણ કરો</translation>
 <translation id="3750562496035670393">Chrome દ્વારા તમારો પાસવર્ડ આ ડિવાઇસમાં સાચવવામાં આવ્યો, પરંતુ તેને બદલે તમે તેને તમારા Google એકાઉન્ટમાં સાચવી શકો છો. પછી, જ્યારે તમે સાઇન ઇન થયા હો, ત્યારે તમારા Google એકાઉન્ટના બધા પાસવર્ડ પણ ઉપલબ્ધ રહેશે.</translation>
 <translation id="3752253558646317685">ફિંગરપ્રિન્ટ સાચવવા માટે તમારા બાળકને તેમની આંગળી ઊંચકતા રહેવાનું જણાવો</translation>
@@ -3192,6 +3197,7 @@
 <translation id="4279129444466079448">તમે આ ડિવાઇસ પર વધુમાં વધુ <ph name="PROFILE_LIMIT" /> ઇ-સિમ પ્રોફાઇલ ઇન્સ્ટૉલ કરી શકો છો. અન્ય કોઈ પ્રોફાઇલ ઉમેરવા માટે, પહેલાં હાલની પ્રોફાઇલ કાઢી નાખો.</translation>
 <translation id="4281844954008187215">સેવાની શરતો</translation>
 <translation id="4282196459431406533">Smart Lock ચાલુ કરેલું છે</translation>
+<translation id="4284755288573763878">શું વધારેલી સુરક્ષાની સુવિધા બંધ કરીએ?</translation>
 <translation id="4285418559658561636">પાસવર્ડ અપડેટ કરો</translation>
 <translation id="4285498937028063278">અનપિન કરો</translation>
 <translation id="428565720843367874">એન્ટી-વાયરસ સૉફ્ટવેર આ ફાઇલ સ્કેન કરતી વખતે અનપેક્ષિત રીતે નિષ્ફળ થયું.</translation>
@@ -4665,6 +4671,7 @@
 <translation id="5865508026715185451"><ph name="APP_NAME" />ને ટૂંક સમયમાં થોભાવવામાં આવશે</translation>
 <translation id="586567932979200359">તમે <ph name="PRODUCT_NAME" /> ને તેની ડિસ્ક છબીથી ચલાવી રહ્યા છો. તેને તમારા કમ્પ્યુટર પર ઇન્સ્ટોલ કરવાથી તમે તેને ડિસ્ક છબી વગર ચલાવી શકો છો, અને તે અપ-ટુ-ડેટ રાખવામાં આવશે તેની ખાતરી થાય છે.</translation>
 <translation id="5865733239029070421">Googleને વપરાશના આંકડા અને ક્રૅશ રિપોર્ટ ઑટોમૅટિક રીતે મોકલે છે</translation>
+<translation id="5865848066829891215">નેટવર્ક કમ્યુનિકેશન સંબંધિત સમસ્યાઓ</translation>
 <translation id="5867841422488265304">વેબ ઍડ્રેસ શોધો અથવા લખો</translation>
 <translation id="5869029295770560994">બરાબર, સમજાઈ ગયું</translation>
 <translation id="5869522115854928033">સાચવેલા પાસવર્ડ્સ</translation>
@@ -5959,6 +5966,7 @@
 <translation id="7243784282103630670">Linux અપગ્રેડ કરતી વખતે ભૂલ આવી હતી. તમારા બૅકઅપનો ઉપયોગ કરીને અમે કન્ટેનરને રિસ્ટોર કરીશું.</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> x <ph name="HEIGHT" /> (શ્રેષ્ઠ)</translation>
 <translation id="7246230585855757313">તમારો સુરક્ષા કોડ ફરી શામેલ કરીને ફરી પ્રયાસ કરો</translation>
+<translation id="7249197363678284330">ઍડ્રેસ બારમાં જઈને આ સેટિંગમાં ફેરફાર કરો.</translation>
 <translation id="7250616558727237648">તમે જે ડિવાઇસ સાથે શેર કરી રહ્યાં છો, તેણે પ્રતિસાદ આપ્યો નથી. કૃપા કરીને ફરી પ્રયાસ કરો.</translation>
 <translation id="725109152065019550">માફ કરશો, તમારા એડમિને તમારા એકાઉન્ટ પર બહારના સ્ટોરેજને બંધ કર્યું છે.</translation>
 <translation id="7251346854160851420">ડિફૉલ્ટ વૉલપેપર</translation>
@@ -7708,6 +7716,7 @@
 <translation id="9026731007018893674">ડાઉનલોડ કરો</translation>
 <translation id="9026852570893462412">આ પ્રક્રિયામાં થોડો સમય લાગી શકે છે. વર્ચ્યુઅલ મશીન ડાઉનલોડ કરી રહ્યાં છીએ.</translation>
 <translation id="9027459031423301635">લિંક નવા &amp;ટૅબમાં ખોલો</translation>
+<translation id="9029667986262585240">તમે તમારા એકાઉન્ટમાં Safe Browsingમાં વધારેલી સુરક્ષાની સુવિધા બંધ કરી છે.</translation>
 <translation id="9030515284705930323">તમારા એકાઉન્ટ માટે તમારી સંસ્થાએ Google Play Store ચાલુ કરેલ નથી. વધુ માહિતી માટે તમારા એડમિનનો સંપર્ક કરો.</translation>
 <translation id="9030754204056345429">વધુ ઝડપી</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_hy.xtb b/chrome/app/resources/generated_resources_hy.xtb
index bc627e5..e1c7dd75 100644
--- a/chrome/app/resources/generated_resources_hy.xtb
+++ b/chrome/app/resources/generated_resources_hy.xtb
@@ -415,6 +415,7 @@
            • Քուքիները և կայքերի մյուս ժամանակավոր տվյալները կջնջվեն։
             <ph name="LINE_BREAKS" />
            Էջանիշները, պատմությունը և պահված գաղտնաբառերը կպահպանվեն։</translation>
+<translation id="1425233109861512854">Չհաջողվեց ճանաչել մուտքի կոդը</translation>
 <translation id="1426410128494586442">Այո</translation>
 <translation id="142655739075382478"><ph name="APP_NAME" /> հավելվածն արգելափակված է</translation>
 <translation id="1426870617281699524">Սեղմեք «Փորձել կրկին» կոճակը և ընդունեք համակարգչի հուշումը</translation>
@@ -581,6 +582,7 @@
 <translation id="1593926297800505364">Պահել վճարման եղանակը</translation>
 <translation id="1595492813686795610">Լինուքսը նորացվում է</translation>
 <translation id="1596286373007273895">Հասանելի է</translation>
+<translation id="1596709061955594992">Bluetooth-ն անջատված է։ Միացրեք այն՝ հասանելի սարքերի ցանկը տեսնելու համար։</translation>
 <translation id="1598233202702788831">Թարմացումներն անջատված են ձեր ադմինիստրատորի կողմից:</translation>
 <translation id="1600857548979126453">Մտնել էջերի վրիպազերծման ծառայություն</translation>
 <translation id="1601560923496285236">Կիրառել</translation>
@@ -1133,6 +1135,7 @@
 <translation id="2148892889047469596">Ներդիրի հեռարձակում</translation>
 <translation id="2149973817440762519">Փոփոխել էջանիշը</translation>
 <translation id="2150139952286079145">Նպատակակետերի որոնում</translation>
+<translation id="2150491840563706000">Դուք այս էկրանին հեռարձակելու թույլտվություն չունեք</translation>
 <translation id="2150661552845026580">Ավելացնե՞լ «<ph name="EXTENSION_NAME" />»-ը:</translation>
 <translation id="2151576029659734873">Ներդիրի սխալ ցուցիչ եք մուտքագրել:</translation>
 <translation id="2152281589789213846">Ավելացնել տպիչներ պրոֆիլում</translation>
@@ -2597,6 +2600,7 @@
 <translation id="3654045516529121250">Կարդալ մատչելիության կարգավորումները</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{Այն մեկ ֆայլի մշտական ​​հասանելիություն ունի:}one{Այն # ֆայլի մշտական ​​հասանելիություն ունի:}other{Այն # ֆայլի մշտական ​​հասանելիություն ունի:}}</translation>
 <translation id="3658871634334445293">TrackPoint-ի արագացում</translation>
+<translation id="3659929705630080526">Դուք չափից շատ անգամ մուտքի սխալ կոդ եք մուտքագրել։ Փորձեք ավելի ուշ։</translation>
 <translation id="3660234220361471169">Անվստահելի</translation>
 <translation id="3664511988987167893">Ընդլայնման պատկերակ</translation>
 <translation id="3665589677786828986">Ձեր կարգավորումներից մի քանիսը փոփոխվել են կողմնակի ծրագրով: Chrome-ը վերականգնել է կանխադրված արժեքները:</translation>
@@ -2680,6 +2684,7 @@
 <translation id="3743842571276656710">Մուտքագրեք PIN կոդը՝ <ph name="DEVICE_NAME" /> սարքը զուգակցելու համար</translation>
 <translation id="3747077776423672805">Հավելվածներ հեռացնելու համար անցեք Կարգավորումներ &gt; Google Play Խանութ &gt; Կառավարել Android-ի կարգավորումները &gt; Հավելվածներ կամ Հավելվածների կառավարիչ: Այնուհետև հպեք այն հավելվածին, որը ցանկանում եք հեռացնել (անհրաժեշտ հավելվածը գտնելու համար, հնարավոր է, պահանջվի թերթել աջ կամ ձախ): Ապա սեղմեք Հեռացնել կամ Անջատել կոճակը:</translation>
 <translation id="3747220812138541072">Ցուցադրել ներտող հուշումներ տեքստ մուտքագրելիս</translation>
+<translation id="3747603683749989726">Միացնե՞լ բարելավված անվտանգությունը</translation>
 <translation id="3748706263662799310">Հաղորդել վրիպակի մասին</translation>
 <translation id="3750562496035670393">Chrome-ը պահել է ձեր գաղտնաբառն այս սարքում, սակայն դուք կարող եք պահել այն ձեր Google հաշվում։ Google հաշվում պահվող բոլոր գաղտնաբառերը նույնպես հասանելի կլինեն, եթե մտնեք հաշիվ։</translation>
 <translation id="3752253558646317685">Երեխան պետք է փոքր-ինչ տեղաշարժի իր մատը, որպեսզի մատնահետքը պահվի։</translation>
@@ -3195,6 +3200,7 @@
 <translation id="4279129444466079448">Դուք կարող եք մինչև <ph name="PROFILE_LIMIT" /> eSIM պրոֆիլ տեղադրել այս սարքում։ Եվս մեկն ավելացնելու համար նախ հեռացրեք առկա պրոֆիլը։</translation>
 <translation id="4281844954008187215">Օգտագործման պայմաններ</translation>
 <translation id="4282196459431406533">Smart Lock-ը միացված է</translation>
+<translation id="4284755288573763878">Անջատե՞լ բարելավված անվտանգությունը</translation>
 <translation id="4285418559658561636">Թարմացնել գաղտնաբառը</translation>
 <translation id="4285498937028063278">Ապամրացնել</translation>
 <translation id="428565720843367874">Այս ֆայլը ստուգելիս՝ հակավիրուսի ծրագիրն անսպասելիորեն խափանվեց:</translation>
@@ -4670,6 +4676,7 @@
 <translation id="5865508026715185451"><ph name="APP_NAME" /> հավելվածի աշխատանքը շուտով կդադարեցվի</translation>
 <translation id="586567932979200359"><ph name="PRODUCT_NAME" />-ը գործարկվում է իր սկավառակի պատկերից: Եթե տեղադրեք այն համակարգչում, կարող եք գործարկել առանց սկավառակի պատկերի` միևնույն ժամանակ այն արդի պահելով:</translation>
 <translation id="5865733239029070421">Ավտոմատ Google-ին է ուղարկում օգտագործման վիճակագրությունը և խափանումների մասին հաշվետվությունները</translation>
+<translation id="5865848066829891215">Ցանցային կապի խնդիրներ</translation>
 <translation id="5867841422488265304">Որոնեք կամ մուտքագրեք վեբ հասցեն</translation>
 <translation id="5869029295770560994">Լավ, հասկացա</translation>
 <translation id="5869522115854928033">Պահված գաղտնաբառեր</translation>
@@ -5963,6 +5970,7 @@
 <translation id="7243784282103630670">Չհաջողվեց նորացնել Լինուքսը։ Մենք կվերականգնենք զետեղարանը՝ օգտագործելով ձեր պահուստավորված տվյալները։</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> x <ph name="HEIGHT" /> (լավագույն)</translation>
 <translation id="7246230585855757313">Նորից տեղադրեք անվտանգության բանալին ու կրկնեք փորձը։</translation>
+<translation id="7249197363678284330">Փոխեք այս կարգավորումը հասցեագոտում։</translation>
 <translation id="7250616558727237648">Սարքը չի արձագանքում։ Նորից փորձեք։</translation>
 <translation id="725109152065019550">Ադմինիստրատորն արգելել է արտաքին պահեստի օգտագործումը ձեր հաշվի համար:</translation>
 <translation id="7251346854160851420">Կանխադրված պաստառ</translation>
@@ -7715,6 +7723,7 @@
 <translation id="9026731007018893674">ներբեռնել</translation>
 <translation id="9026852570893462412">Այս գործընթացը կարող է տևել մի քանի րոպե: Վիրտուալ մեքենան ներբեռնվում է:</translation>
 <translation id="9027459031423301635">Բացել հղումը նոր &amp;ներդիրով</translation>
+<translation id="9029667986262585240">Դուք ձեր հաշվում անջատել եք բարելավված Ապահով դիտարկումը։</translation>
 <translation id="9030515284705930323">Ձեր կազմակերպությունը Google Play Խանութը ձեր հաշվի համար չի միացրել: Լրացուցիչ տեղեկությունների համար կապվեք ձեր ադմինիստրատորի հետ:</translation>
 <translation id="9030754204056345429">Շատ արագ</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_kk.xtb b/chrome/app/resources/generated_resources_kk.xtb
index 532e4ef7..93b68c9 100644
--- a/chrome/app/resources/generated_resources_kk.xtb
+++ b/chrome/app/resources/generated_resources_kk.xtb
@@ -2066,7 +2066,7 @@
 <translation id="3088128611727407543">Қолданба профилі дайындалуда...</translation>
 <translation id="3088325635286126843">&amp;Атын өзгерту…</translation>
 <translation id="3089064280130434511">Сайттардың терезелерді ашып, экрандарға орналастыруына тыйым салу</translation>
-<translation id="3089137131053189723">Іздеу тарихы тазартылды</translation>
+<translation id="3089137131053189723">Іздеу сұрауы өшірілді.</translation>
 <translation id="3090589793601454425">Көшірмеу</translation>
 <translation id="3090819949319990166">Сыртқы CRX файлын <ph name="TEMP_CRX_FILE" /> файлына көшіру мүмкін емес.</translation>
 <translation id="3090871774332213558">"<ph name="DEVICE_NAME" />" жұптастырылған</translation>
diff --git a/chrome/app/resources/generated_resources_lo.xtb b/chrome/app/resources/generated_resources_lo.xtb
index f6d5f2a..ef3cd230 100644
--- a/chrome/app/resources/generated_resources_lo.xtb
+++ b/chrome/app/resources/generated_resources_lo.xtb
@@ -395,6 +395,7 @@
 <translation id="1408980562518920698">ຈັດການຂໍ້ມູນສ່ວນຕົວ</translation>
 <translation id="1410197035576869800">ໄອຄອນແອັບ</translation>
 <translation id="1410616244180625362">ສືບ​ຕໍ່​ໃຫ້ <ph name="HOST" /> ​ເຂົ້າຫາ​ກ້ອງ​ຖ່າຍ​ຮູບ​ຂອງ​ທ່ານ</translation>
+<translation id="1410797069449661718">ເລື່ອນໄປຫາແຖບທຳອິດ</translation>
 <translation id="1410806973194718079">ບໍ່ສາມາດກວດສອບນະໂຍບາຍໄດ້</translation>
 <translation id="1412681350727866021">ສ່ວນຂະຫຍາຍເພີ່ມເຕີມ</translation>
 <translation id="1414315029670184034">ບໍ່ອະນຸຍາດໃຫ້ເວັບໄຊໃຊ້ກ້ອງຖ່າຍຮູບຂອງທ່ານ</translation>
@@ -415,6 +416,7 @@
            • ລຶບຄຸກກີ້ ແລະ ຂໍ້ມູນເວັບໄຊຊົ່ວຄາວອື່ນ
             <ph name="LINE_BREAKS" />
            ບຸກມາກ, ປະຫວັດ ແລະ ລະຫັດຜ່ານທີ່ບັນທຶກໄວ້ຈະບໍ່ໄດ້ຮັບຜົນກະທົບ.</translation>
+<translation id="1425233109861512854">ບໍ່ຮູ້ຈັກລະ​ຫັດ​ການ​ເຂົ້າ​ຫາ</translation>
 <translation id="1426410128494586442">ແມ່ນແລ້ວ</translation>
 <translation id="142655739075382478"><ph name="APP_NAME" /> ຖືກບລັອກໄວ້ຢູ່</translation>
 <translation id="1426870617281699524">ຄລິກລອງອີກຄັ້ງ ແລະ ຮັບເອົາການເຕືອນໃນຄອມພິວເຕີຂອງທ່ານ</translation>
@@ -580,6 +582,7 @@
 <translation id="1593926297800505364">ບັນທຶກວິທີການຈ່າຍເງິນ</translation>
 <translation id="1595492813686795610">Linux ກຳລັງອັບເກຣດ</translation>
 <translation id="1596286373007273895">ສາມາດໃຊ້ໄດ້</translation>
+<translation id="1596709061955594992">Bluetooth ຖືກປິດໄວ້ຢູ່. ເພື່ອເບິ່ງອຸປະກອນທີ່ສາມາດໃຊ້ໄດ້, ກະລຸນາເປີດ Bluetooth ກ່ອນ.</translation>
 <translation id="1598233202702788831">ການ​ອັບ​ເດດ​ຖືກ​ປິດ​ໃຊ້​ງານ​ໂດຍ​ຜູ້​ດູແລລະ​ບົບ​ຂອງ​ທ່ານ.</translation>
 <translation id="1600857548979126453">ເຂົ້າຫາດ້ານຫຼັງຂອງຕົວແກ້ໄຂບັນຫາໜ້າ</translation>
 <translation id="1601560923496285236">ນໍາໃຊ້</translation>
@@ -728,6 +731,7 @@
 <translation id="1725562816265788801">ການເລື່ອນແຖບ</translation>
 <translation id="1729533290416704613">ມັນຍັງຄວບຄຸມວ່າຈະໃຫ້ສະແດງໜ້າໃດຂຶ້ນ ເມື່ອທ່ານຄົ້ນຫາຈາກ Omnibox.</translation>
 <translation id="1730917990259790240"><ph name="BEGIN_PARAGRAPH1" />ເພື່ອລຶບແອັບອອກ, ກະລຸນາໄປທີ່ ການຕັ້ງຄ່າ &gt; Google Play Store &gt; ຈັດການການຕັ້ງຄ່າ Android &gt; ແອັບ ຫຼື ຕົວຈັດການແອັບພລິເຄຊັນ. ແລ້ວແຕະໃສ່ແອັບທີ່ທ່ານຕ້ອງການຖອນການຕິດຕັ້ງ (ທ່ານອາດຈະຈຳເປັນຕ້ອງປັດຂວາ ຫຼື ຊ້າຍເພື່ອຊອກຫາແອັບ). ຈາກນັ້ນແຕະຖອນການຕິດຕັ້ງ ຫຼື ປິດນຳໃຊ້.<ph name="END_PARAGRAPH1" /></translation>
+<translation id="1730989807608739928">ເລື່ອນໄປຫາແຖບສຸດທ້າຍ</translation>
 <translation id="1731911755844941020">ກໍາລັງສົ່ງຄໍາຂໍ...</translation>
 <translation id="1733383495376208985">ເຂົ້າລະຫັດຂໍ້ມູນທີ່ຊິ້ງດ້ວຍ <ph name="BEGIN_LINK" />ລະຫັດຜ່ານການຊິ້ິງຂໍ້ມູນ<ph name="END_LINK" /> ຂອງທ່ານເອງ. ນີ້ບໍ່ຮວມເອົາວິທີການຈ່າຍເງິນ ແລະ ທີ່ຢູ່ຈາກ Google Pay.</translation>
 <translation id="1734212868489994726">ສີຟ້າອ່ອນ</translation>
@@ -1140,6 +1144,7 @@
 <translation id="2148892889047469596">​ແຖບຄາສ໌ທ</translation>
 <translation id="2149973817440762519">ແກ້​ໄຂບຸກມາກສ໌</translation>
 <translation id="2150139952286079145">ຄົ້ນ​ຫາປາຍທາງ</translation>
+<translation id="2150491840563706000">ທ່ານບໍ່ມີການອະນຸຍາດເພື່ອສົ່ງສັນຍານໄປຫາການສະແດງຜົນນີ້ໄດ້</translation>
 <translation id="2150661552845026580">ເພີ່ມ "<ph name="EXTENSION_NAME" />" ບໍ?</translation>
 <translation id="2151576029659734873">ດັດຊະນີແຖບປ້ອນເຂົ້າໃຊ້ບໍ່ໄດ້.</translation>
 <translation id="2152281589789213846">ເພີ່ມເຄື່ອງພິມໃສ່ໂປຣໄຟລ໌ຂອງທ່ານ</translation>
@@ -1450,6 +1455,7 @@
 <translation id="245322989586167203">ໂດຍປົກກະຕິເວັບໄຊຕ່າງໆຈະເຊື່ອມຕໍ່ຫາຜອດຊີຣຽວສຳລັບຄຸນສົມບັດການໂອນຂໍ້ມູນ ເຊັ່ນ: ການຕັ້ງຄ່າເຄືອຂ່າຍຂອງທ່ານ</translation>
 <translation id="2453706416476934374">ເພື່ອຮັບຄຳຕອບທີ່ປັບແຕ່ງມາເມື່ອ <ph name="SUPERVISED_USER_NAME" /> ຖາມຄຳຖາມ, ກະລຸນາເປີດໃຫ້ຜູ້ຊ່ວຍຂອງທ່ານເຂົ້າເຖິງຮູບໜ້າຈໍຂອງສິ່ງທີ່ຢູ່ເທິງໜ້າຈໍຂອງ <ph name="SUPERVISED_USER_NAME" /> ໄດ້. ນີ້ອາດຮວມຂໍ້ມູນກ່ຽວກັບເພງ ຫຼື ວິດີໂອທີ່ກຳລັງເປີດຢູ່ນຳ.</translation>
 <translation id="2453860139492968684">ສໍາ​ເລັດ​</translation>
+<translation id="2454206500483040640">ແບ່ງແລ້ວ</translation>
 <translation id="2454247629720664989">ຄໍາ​ສໍາຄັນ</translation>
 <translation id="2454264884354864965">ກ້ອງປິດຢູ່</translation>
 <translation id="2454524890947537054">ອະນຸມັດຄຳຮ້ອງຂໍເວັບໄຊບໍ?</translation>
@@ -1812,6 +1818,7 @@
 <translation id="2806891468525657116">ມີທາງລັດນັ້ນຢູ່ກ່ອນແລ້ວ</translation>
 <translation id="2807517655263062534">ໄຟລ໌ທີ່ທ່ານດາວໂຫຼດປາກົດຢູ່ບ່ອນນີ້</translation>
 <translation id="2809586584051668049">ແລະ <ph name="NUMBER_ADDITIONAL_DISABLED" /> ເພີ່ມອີກ</translation>
+<translation id="2810235462964014915">ຮ້ອງຂໍເພື່ອອ່ານ ແລະ ປ່ຽນແປງ <ph name="SITE_NAME" /> ແລ້ວ</translation>
 <translation id="2811205483104563968">ບັນຊີ</translation>
 <translation id="2812049959647166806">ບໍ່ຮອງຮັບ Thunderbolt</translation>
 <translation id="2812989263793994277">ຢ່າສະແດງຮູບໃດອີກ</translation>
@@ -2608,6 +2615,7 @@
 <translation id="3654045516529121250">ອ່ານການຕັ້ງຄ່າຄວາມສາມາດເຂົ້າຫາ</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{ມັນມີການເຂົ້າຫາໜຶ່ງ​ໄຟ​ລ໌ຖາວອນ.}other{ມັນມີການເຂົ້າຫາ # ໄຟ​ລ໌ຖາວອນ.}}</translation>
 <translation id="3658871634334445293">ການເລັ່ງຄວາມໄວ TrackPoint</translation>
+<translation id="3659929705630080526">ທ່ານໃສ່ລະ​ຫັດ​ການ​ເຂົ້າ​ຫາບໍ່ຖືກຕ້ອງຫຼາຍເທື່ອເກີນໄປ. ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ</translation>
 <translation id="3660234220361471169">ບໍ່ເຊື່ອຖືໄດ້</translation>
 <translation id="3664511988987167893">ໄອຄອນສ່ວນຂະຫຍາຍ</translation>
 <translation id="3665589677786828986">Chrome ກວດພົບວ່າ ການຕັ້ງຄ່າຂອງທ່ານບາງຕົວໄດ້ຮັບຄວາມເສຍຫາຍໂດຍໂປຣແກຼມອື່ນ ແລະໄດ້ຕັ້ງຄ່າພວກມັນໄປເປັນຄ່າມາດຕະຖານເດີມຂອງພວກມັນແລ້ວ.</translation>
@@ -2691,6 +2699,7 @@
 <translation id="3743842571276656710">ໃສ່ PIN ເພື່ອຈັບຄູ່ກັບ <ph name="DEVICE_NAME" /></translation>
 <translation id="3747077776423672805">ເພື່ອລຶບແອັບອອກ, ກະລຸນາໄປທີ່ ການຕັ້ງຄ່າ &gt; Google Play Store &gt; ຈັດການການຕັ້ງຄ່າ Android &gt; ແອັບ ຫຼື ຕົວຈັດການແອັບພລິເຄຊັນ. ແລ້ວແຕະໃສ່ທີ່ແອັບທີ່ທ່ານຕ້ອງການຖອນການຕິດຕັ້ງ (ທ່ານອາດຈະຈຳເປັນຕ້ອງປັດຂວາ ຫຼື ຊ້າຍເພື່ອຊອກຫາແອັບ). ຈາກນັ້ນແຕະຖອນການຕິດຕັ້ງ ຫຼື ປິດນຳໃຊ້.</translation>
 <translation id="3747220812138541072">ສະແດງການແນະນຳການຂຽນແບບໃນແຖວທີ່ປາກົດເມື່ອທ່ານພິມ</translation>
+<translation id="3747603683749989726">ເປີດໃຊ້ຄວາມປອດໄພທີ່ປັບປຸງດີຂຶ້ນບໍ?</translation>
 <translation id="3748706263662799310">ລາຍງານຂໍ້ຜິດພາດ</translation>
 <translation id="3750562496035670393">Chrome ບັນທຶກລະຫັດຜ່ານຂອງທ່ານໃສ່ອຸປະກອນນີ້ແລ້ວ, ແຕ່ທ່ານສາມາດບັນທຶກມັນໄປໃສ່ບັນຊີ Google ຂອງທ່ານແທນໄດ້. ຈາກນັ້ນ, ລະຫັດຜ່ານທັງໝົດໃນບັນຊີ Google ຂອງທ່ານຈະສາມາດໃຊ້ໄດ້ໃນເວລາທີ່ທ່ານເຂົ້າສູ່ລະບົບນຳ.</translation>
 <translation id="3752253558646317685">ໃຫ້ລູກຂອງທ່ານຍົກນິ້ວມືອອກເພື່ອບັນທຶກລາຍນິ້ວມື</translation>
@@ -3118,6 +3127,7 @@
 <translation id="4165942112764990069"><ph name="USER_EMAIL" /> ບໍ່ໄດ້ເປັນຂອງອົງການທີ່ຖືກຕ້ອງ. ກະລຸນາຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ. ຫາກທ່ານເປັນຜູ້ເບິ່ງແຍງລະບົບ, ທ່ານສາມາດຕັ້ງຄ່າອົງການຂອງທ່ານໄດ້ໂດຍການເຂົ້າໄປ: g.co/ChromeEnterpriseAccount</translation>
 <translation id="4167686856635546851">ໂດຍປົກກະຕິແລ້ວ ເວັບໄຊຈະໃຊ້ JavaScript ເພື່ອສະແດງຄຸນສົມບັດແບບໂຕ້ຕອບ ເຊັ່ນ: ວິດີໂອເກມ ຫຼື ແບບຟອມເວັບ</translation>
 <translation id="4168015872538332605">ການຕັ້ງຄ່າບາງອັນທີ່ເປັນຂອງ <ph name="PRIMARY_EMAIL" /> ກໍາລັງຖືກແຊຣ໌ກັບທ່ານ. ການຕັ້ງຄ່າເຫຼົ່ານີ້ມີຜົນກະທົບກັບບັນຊີຂອງທ່ານພຽງແຕ່ເມື່ອໃຊ້ການລົງຊື່ເຂົ້າ​ໃຊ້ຫຼາຍອັນເທົ່ານັ້ນ.</translation>
+<translation id="4168651806173792090"><ph name="NETWORK_NAME" /> ລົງທ້າຍດ້ວຍ <ph name="LAST_FOUR_DIGITS" /></translation>
 <translation id="4169535189173047238">ບໍ່ອະນຸຍາດ</translation>
 <translation id="4170314459383239649">ລຶບລ້າງເມື່ອອອກ</translation>
 <translation id="417096670996204801">ເລືອກໂປຣໄຟລ໌</translation>
@@ -3206,6 +3216,7 @@
 <translation id="4279129444466079448">ທ່ານສາມາດຕິດຕັ້ງໂປຣໄຟລ໌ eSIM ໄດ້ສູງສຸດ <ph name="PROFILE_LIMIT" /> ໂປຣໄຟລ໌ຢູ່ອຸປະກອນນີ້. ເພື່ອເພີ່ມໂປຣໄຟລ໌ອື່ນ, ໃຫ້ລຶບໂປຣໄຟລ໌ທີ່ມີຢູ່ກ່ອນແລ້ວອອກກ່ອນ.</translation>
 <translation id="4281844954008187215">ຂໍ້ກໍານົດການບໍລິການ</translation>
 <translation id="4282196459431406533">Smart Lock ເປີດຢູ່</translation>
+<translation id="4284755288573763878">ປິດຄວາມປອດໄພທີ່ປັບປຸງດີຂຶ້ນໄວ້ບໍ?</translation>
 <translation id="4285418559658561636">ອັບເດດລະຫັດຜ່ານ</translation>
 <translation id="4285498937028063278">ຖອນໝຸດ</translation>
 <translation id="428565720843367874">ຊອບແວແອນຕິໄວຣັສລົ້ມເຫຼວໂດຍບໍ່ຄາດຄິດ ໃນຂະນະທີ່ກໍາລັງສະແກນໄຟລ໌ນີ້.</translation>
@@ -3347,6 +3358,7 @@
 <translation id="4432621511648257259">ລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ</translation>
 <translation id="443454694385851356">ແບບເດີມ (ບໍ່ປອດໄພ)</translation>
 <translation id="443475966875174318">ອັບເດດ ຫຼື ລຶບແອັບພລິເຄຊັນທີ່ບໍ່ເຂົ້າກັນອອກ</translation>
+<translation id="4435634292635092844">ເບິ່ງຮູບພາບຈາກຮູບຈາກອະລະບໍ້າກ້ອງຂອງໂທລະສັບທ່ານຢູ່ <ph name="DEVICE_TYPE" /> ທ່ານ. <ph name="LINK_BEGIN" />ສຶກສາເພີ່ມເຕີມ<ph name="LINK_END" /></translation>
 <translation id="4438043733494739848">ໂປ່ງແສງ</translation>
 <translation id="4440097423000553826"><ph name="WEBSITE" /> ສົ່ງການແຈ້ງເຕືອນໄປຫາໂທລະສັບຂອງທ່ານແລ້ວ. ເພື່ອຢືນຢັນວ່າແມ່ນທ່ານ, ໃຫ້ແຕະໃສ່ການແຈ້ງເຕືອນ “<ph name="NOTIFICATIONTITLE" />” ແລ້ວເຮັດຕາມຂັ້ນຕອນ.</translation>
 <translation id="4441124369922430666">ທ່ານ​ຕ້ອງ​ການເລີ່ມແອັບນີ້ອັດ​ຕະ​ໂນ​ມັດບໍ ເມື່ອເຄື່ອງຈັກເປີດ​?</translation>
@@ -4604,6 +4616,7 @@
 <translation id="5789643057113097023">.</translation>
 <translation id="5790085346892983794">ສໍາ​ເລັດ</translation>
 <translation id="5790651917470750848">ມີການສົ່ງຕໍ່ຜອດຢູ່ແລ້ວ</translation>
+<translation id="5792295754950501287">ຄຳສັ່ງເພີ່ມເຕີມສຳລັບ <ph name="CARD_DESCRIPTION" /></translation>
 <translation id="5792728279623964091">ກະລຸນາແຕະປຸ່ມປິດເປີດເຄື່ອງຂອງທ່ານ</translation>
 <translation id="5793339252089865437">ຖ້າທ່ານດາວໂຫຼດການອັບເດດຜ່ານເຄືອຂ່າຍມືຖືຂອງທ່ານ, ມັນອາດຈະເຮັດໃຫ້ເກີດການຮຽກເກັບເງິນເພີ່ມເຕີມ.</translation>
 <translation id="5794034487966529952">ໂຕະ <ph name="DESK_TITLE" /> ມີ <ph name="NUM_BROWSERS" /> ໜ້າຈໍໂປຣແກຣມທ່ອງເວັບເປີດຢູ່</translation>
@@ -4678,6 +4691,7 @@
 <translation id="5865508026715185451"><ph name="APP_NAME" /> ຈະຢຸດຊົ່ວຄາວໃນໄວໆນີ້</translation>
 <translation id="586567932979200359">ທ່ານ​ກໍາ​ລັງ​ແລ່ນ <ph name="PRODUCT_NAME" /> ຈາກ​ຮູບ​ແຜ່ນ​ດິສກ໌ຂອງມັນ. ການ​ຕິດ​ຕັ້ງ​ມັນ​ຢູ່ໃນ​ຄອມ​ພິວ​ເຕີ​ຂອງ​ທ່ານ​​ເຮັດ​ໃຫ້​ແລ່ນມັນໄດ້​ໂດຍ​ບໍ່ຕ້ອງ​ມີ​ຮູບ​​ແຜ່ນດິສກ໌ໄດ້​, ແລະ​ຮັບ​ປະ​ກັນ​ໃຫ້ມັນອັບເດດຢູ່.</translation>
 <translation id="5865733239029070421">ສົ່ງສະຖິຕິການໃຊ້ ແລະ ລາຍງານການຂັດຂ້ອງໃຫ້ Google ໂດຍອັດຕະໂນມັດ</translation>
+<translation id="5865848066829891215">ບັນຫາການຕິດຕໍ່ສື່ສານເຄືອຂ່າຍ</translation>
 <translation id="5867841422488265304">ຊອກຫາ ຫຼື ພິມທີ່ຢູ່ເວັບ</translation>
 <translation id="5869029295770560994">ຕົກລົງ, ເຂົ້າໃຈແລ້ວ​</translation>
 <translation id="5869522115854928033">ລະ​ຫັດ​ຜ່ານ​ທີ່ບັນທຶກໄວ້</translation>
@@ -5159,6 +5173,7 @@
 <translation id="6374469231428023295">ລອງໃໝ່ອີກ</translation>
 <translation id="6377268785556383139">1 ຜົນການຊອກຫາສຳລັບ '<ph name="SEARCH_TEXT" />'</translation>
 <translation id="6380143666419481200">ຍອມ​ຮັບ​ ແລະ​ສືບ​ຕໍ່</translation>
+<translation id="6382616130475191723">ອະນຸຍາດໃຫ້ອ່ານ ແລະ ປ່ຽນແປງ <ph name="SITE_NAME" /> ແລ້ວ</translation>
 <translation id="6382958439467370461">ບໍ່ມີທາງລັດທີ່ບໍ່ເຮັດວຽກ</translation>
 <translation id="638418309848716977">ລິ້ງທີ່ຮອງຮັບ</translation>
 <translation id="6384275966486438344">ປ່ຽນການຕັ້ງຄ່າການຄົ້ນຫາຂອງທ່ານເປັນ​: <ph name="SEARCH_HOST" /></translation>
@@ -5971,6 +5986,7 @@
 <translation id="7243784282103630670">ເກີດຄວາມຜິດພາດໃນຂະນະທີ່ອັບເກຣດ Linux. ພວກເຮົາຈະຄືນຄ່າກ່ອງບັນຈຸໂດຍໃຊ້ຂໍ້ມູນສຳຮອງຂອງທ່ານ.</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> x <ph name="HEIGHT" /> (ດີທີ່ສຸດ)</translation>
 <translation id="7246230585855757313">ສຽບກະແຈຄວາມປອດໄພຂອງທ່ານຄືນໃໝ່ ແລ້ວລອງອີກຄັ້ງ</translation>
+<translation id="7249197363678284330">ປ່ຽນການຕັ້ງຄ່ານີ້ໄດ້ໃນ​ແຖບທີ່​ຢູ່.</translation>
 <translation id="7250616558727237648">ອຸປະກອນທີ່ທ່ານກຳລັງແບ່ງປັນບໍ່ຕອບສະໜອງ. ກະລຸນາລອງໃໝ່.</translation>
 <translation id="725109152065019550">ຂໍອະໄພ, ຜູ້ຄວບຄຸມຂອງທ່ານໄດ້ປິດໃຊ້ງານບ່ອນເກັບຂໍ້ມູນທາງນອກຢູ່ໃນບັນຊີຂອງທ່ານ.</translation>
 <translation id="7251346854160851420">ພາບພື້ນຫຼັງເລີ່ມຕົ້ນ</translation>
@@ -6989,6 +7005,7 @@
 <translation id="8244514732452879619">ໃກ້ຈະປິດໄຟແລ້ວ</translation>
 <translation id="8246776524656196770">ປົກປ້ອງກະແຈຄວາມປອດໄພຂອງທ່ານດ້ວຍ PIN (ລະຫັດລະບຸສ່ວນຕົວ)</translation>
 <translation id="8248050856337841185">ແປະໃສ່</translation>
+<translation id="8248381369318572865">ເຂົ້າເຖິງໄມໂຄຣໂຟນ ແລະ ວິເຄາະຄຳເວົ້າຂອງທ່ານ</translation>
 <translation id="8248887045858762645">ເຄັດລັບ Chrome</translation>
 <translation id="8249048954461686687">ໂຟລເດີ OEM</translation>
 <translation id="8249615410597138718">ສົ່ງຫາອຸປະກອນຂອງທ່ານ</translation>
@@ -7009,6 +7026,7 @@
 <translation id="8264024885325823677">ການຕັ້ງຄ່ານີ້ຈັດການໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ.</translation>
 <translation id="8264718194193514834">"<ph name="EXTENSION_NAME" />" ໄດ້ກະຕຸ້ນໜ້າຈໍເຕັມ.</translation>
 <translation id="826511437356419340">ເຂົ້າສູ່ໂໝດພາບຮວມໜ້າຈໍແລ້ວ. ປັດເພື່ອໄປຍັງສ່ວນຕ່າງໆ ຫຼື ກົດ Tab ຖ້າກຳລັງໃຊ້ແປ້ນພິມຢູ່.</translation>
+<translation id="8265671588726449108">{COUNT,plural, =1{ລະບົບຈະບໍ່ເປີດໜ້າຈໍທີ່ບໍ່ເປີດເຜີຍຕົວຕົນຂອງທ່ານຫຼັງຈາກທີ່ທ່ານເລີ່ມເປີດໃຊ້ໃໝ່}other{ລະບົບຈະບໍ່ເປີດໜ້າຈໍທີ່ບໍ່ເປີດເຜີຍຕົວຕົນ {COUNT} ລາຍການຂອງທ່ານຫຼັງຈາກທີ່ທ່ານເລີ່ມເປີດໃຊ້ໃໝ່}}</translation>
 <translation id="8266947622852630193">ວິທີການປ້ອນຂໍ້ມູນທັງໝົດ</translation>
 <translation id="8267539814046467575">ເພີ່ມເຄື່ອງພິມ</translation>
 <translation id="8267961145111171918"><ph name="BEGIN_PARAGRAPH1" />ນີ້ແມ່ນຂໍ້ມູນທົ່ວໄປກ່ຽວກັບອຸປະກອນນີ້ ແລະ ມັນຖືກໃຊ້ມັນແນວໃດ (ເຊັ່ນ: ລະດັບແບັດເຕີຣີ, ການເຄື່ອນໄຫວໃນລະບົບ ແລະ ແອັບ ແລະ ຂໍ້ຜິດພາດຕ່າງໆ). ຂໍ້ມູນຈະຖືກໃຊ້ເພື່ອປັບປຸງ Android ແລະ ບາງຂໍ້ມູນແບບຮວມກັນຈະຊ່ວຍແອັບ ແລະ ບັນດາຮຸ້ນສ່ວນຂອງ Google ເຊັ່ນ ຜູ້ພັດທະນາ Android ເຮັດໃຫ້ແອັບ ແລະ ຜະລິດຕະພັນຂອງເຂົາເຈົ້າດີຂຶ້ນ.<ph name="END_PARAGRAPH1" />
@@ -7723,6 +7741,7 @@
 <translation id="9026731007018893674">ດາວ​ໂຫລດ</translation>
 <translation id="9026852570893462412">ຂະບວນການອາດຈະໃຊ້ເວລາສອງສາມນາທີ. ກຳລັງດາວໂຫຼດເຄື່ອງຈຳລອງສະເໝືອນຈິງ.</translation>
 <translation id="9027459031423301635">ເປີດລິ້ງຢູ່ໃນແຖບໃໝ່</translation>
+<translation id="9029667986262585240">ທ່ານປິດ Safe Browsing ທີ່ປັບປຸງດີຂຶ້ນໃນບັນຊີຂອງທ່ານໄວ້ແລ້ວ.</translation>
 <translation id="9030515284705930323">ອົງການຂອງທ່ານບໍ່ໄດ້ເປີດໃຊ້ Google Play Store ສຳລັບບັນຊີຂອງທ່ານ. ໃຫ້ຕິດຕໍ່ຫາຜູ້ເບິ່ງແຍງລະບົບສຳລັບຂໍ້ມູນເພີ່ມເຕີມ.</translation>
 <translation id="9030754204056345429">ໄວຂຶ້ນ</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_mr.xtb b/chrome/app/resources/generated_resources_mr.xtb
index d1938d5b..2a40855 100644
--- a/chrome/app/resources/generated_resources_mr.xtb
+++ b/chrome/app/resources/generated_resources_mr.xtb
@@ -415,6 +415,7 @@
            • कुकी आणि इतर तात्पुरता साइट डेटा हटवला जाईल
             <ph name="LINE_BREAKS" />
            बुकमार्क, इतिहास आणि सेव्ह केलेल्या पासवर्डवर परिणाम होणार नाही.</translation>
+<translation id="1425233109861512854">अ‍ॅक्सेस कोड ओळखता आला नाही</translation>
 <translation id="1426410128494586442">होय</translation>
 <translation id="142655739075382478"><ph name="APP_NAME" /> ब्लॉक केले आहे</translation>
 <translation id="1426870617281699524">पुन्हा प्रयत्न करा वरवर क्लिक करा आणि तुमच्या काँप्युटरवर प्रॉम्प्ट स्वीकारा</translation>
@@ -581,6 +582,7 @@
 <translation id="1593926297800505364">पेमेंट पद्धत सेव्ह करा</translation>
 <translation id="1595492813686795610">Linux अपग्रेड होत आहे</translation>
 <translation id="1596286373007273895">उपलब्ध आहे</translation>
+<translation id="1596709061955594992">ब्लूटूथ बंद आहे. उपलब्ध डिव्हाइस पाहण्यासाठी, ब्लूटूथ सुरू करा.</translation>
 <translation id="1598233202702788831">तुमच्या ॲडमिनिस्ट्रेटरने अपडेट अक्षम केली आहेत.</translation>
 <translation id="1600857548979126453">पेज ‍डीबगर बॅकएंडला ॲक्सेस करा</translation>
 <translation id="1601560923496285236">लागू करा</translation>
@@ -1142,6 +1144,7 @@
 <translation id="2148892889047469596">टॅब कास्ट करा</translation>
 <translation id="2149973817440762519">बुकमार्क संपादित करा</translation>
 <translation id="2150139952286079145">शोध गंतव्ये</translation>
+<translation id="2150491840563706000">तुम्हाला या डिस्प्लेवर कास्ट करण्याची परवानगी नाही</translation>
 <translation id="2150661552845026580">"<ph name="EXTENSION_NAME" />" जोडायचे ?</translation>
 <translation id="2151576029659734873">चुकीची टॅब अनुक्रमणिका एंटर केली.</translation>
 <translation id="2152281589789213846">तुमच्या प्रोफाइलमध्ये प्रिंटर जोडा</translation>
@@ -2610,6 +2613,7 @@
 <translation id="3654045516529121250">तुमच्या ॲक्सेस योग्यता सेटिंग्ज वाचा</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{यास एका फाईलवर कायमचा प्रवेश आहे.}other{यास # फाइलवर कायमचा प्रवेश आहे.}}</translation>
 <translation id="3658871634334445293">TrackPoint अ‍ॅक्सिलरेशन</translation>
+<translation id="3659929705630080526">तुम्ही खूप वेळा चुकीचा अ‍ॅक्सेस कोड एंटर केला आहे. नंतर पुन्हा प्रयत्न करा</translation>
 <translation id="3660234220361471169">अविश्‍वासू</translation>
 <translation id="3664511988987167893">एक्स्टेंशन आयकन</translation>
 <translation id="3665589677786828986">Chrome ला आढळले आहे की आपल्या काही सेटिंग्ज दुसर्‍या प्रोग्रामद्वारे दूषित झाल्या होत्या आणि त्यांच्या मूळ डीफॉल्टवर त्या रीसेट केल्या.</translation>
@@ -2693,6 +2697,7 @@
 <translation id="3743842571276656710"><ph name="DEVICE_NAME" /> सोबत पेअर करण्यासाठी पिन एंटर करा</translation>
 <translation id="3747077776423672805">ॲप्स काढून टाकण्यासाठी, सेटिंग्ज &gt; Google Play स्टोअर &gt; Android प्राधान्ये व्यवस्थापित करा &gt; ॲप्स किंवा अ‍ॅप्लिकेशन व्यवस्थापक वर जा. नंतर तुम्हाला अनइंस्टॉल करायच्या असलेल्या ॲपवर टॅप करा (ॲप शोधण्‍यासाठी तुम्हाला कदाचित उजवीकडे किंवा डावीकडे स्वाइप करावे लागेल). यानंतर अनइंस्टॉल करा किंंवा बंद करा वर टॅप करा.</translation>
 <translation id="3747220812138541072">तुम्ही टाइप करत असताना दिसणाऱ्या लिखाणाच्या इनलाइन सूचना दाखवा</translation>
+<translation id="3747603683749989726">वर्धित सुरक्षा सुरू करायची का?</translation>
 <translation id="3748706263662799310">बग नोंदवा</translation>
 <translation id="3750562496035670393">Chrome ने तुमचा पासवर्ड या डिव्‍हाइसवर सेव्‍ह केला आहे, पण तुम्‍ही तो त्‍याऐवजी तुमच्‍या Google खाते मध्ये सेव्‍ह करू शकता. त्यानंतर, तुम्ही साइन इन केल्यावर तुमच्या Google खाते मधीलदेखील सर्व पासवर्ड उपलब्ध होतील.</translation>
 <translation id="3752253558646317685">फिंगरप्रिंट सेव्ह करण्यासाठी तुमच्या लहान मुलाला त्यांचे बोट उचलण्यास सांगा</translation>
@@ -3206,6 +3211,7 @@
 <translation id="4279129444466079448">तुम्ही या डिव्हाइसवर कमाल <ph name="PROFILE_LIMIT" /> eSIM प्रोफाइल इंस्टॉल करू शकता. दुसरी प्रोफाइल जोडण्यासाठी, सद्य प्रोफाइल काढून टाका.</translation>
 <translation id="4281844954008187215">सेवा अटी</translation>
 <translation id="4282196459431406533">Smart Lock सुरू आहे</translation>
+<translation id="4284755288573763878">वर्धित सुरक्षा बंद करायची का?</translation>
 <translation id="4285418559658561636">पासवर्ड अपडेट करा</translation>
 <translation id="4285498937028063278">अनपिन</translation>
 <translation id="428565720843367874">या फाईलचे स्कॅनिंग करताना अँटी-व्हायरस सॉफ्टवेअर अनपेक्षितपणे अयशस्वी झाले.</translation>
@@ -4679,6 +4685,7 @@
 <translation id="5865508026715185451"><ph name="APP_NAME" /> लवकरच थांबेल</translation>
 <translation id="586567932979200359">तुम्ही <ph name="PRODUCT_NAME" /> त्याच्या डिस्क प्रतिमेवरून चालवत आहात. तुमच्या कॉंप्युटरवर ते इंस्टॉल केल्यामुळे तुम्हाला ते डिस्क प्रतिमेशिवाय चालवू देते आणि ते अपडेट ठेवले जाईल हे निश्चित करते.</translation>
 <translation id="5865733239029070421">Google ला वापरविषयक आकडेवारी आणि क्रॅश अहवाल आपोआप पाठवते</translation>
+<translation id="5865848066829891215">नेटवर्क कम्युनिकेशनसंबंधित समस्या</translation>
 <translation id="5867841422488265304">वेब पत्ता शोधा किंवा टाइप करा</translation>
 <translation id="5869029295770560994">ठीक आहे, समजले</translation>
 <translation id="5869522115854928033">सेव्ह केलेले पासवर्ड</translation>
@@ -5973,6 +5980,7 @@
 <translation id="7243784282103630670">Linux अपग्रेड करताना एरर आली. आम्ही तुमचा बॅकअप वापरून कंटनेर रिस्टोअर करू.</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> x <ph name="HEIGHT" /> (उत्कृष्‍ट)</translation>
 <translation id="7246230585855757313">तुमची सिक्युरिटी की पुन्हा घाला आणि पुन्हा प्रयत्न करा</translation>
+<translation id="7249197363678284330">अ‍ॅड्रेस बारमध्ये हे सेटिंग बदला.</translation>
 <translation id="7250616558727237648">तुम्ही ज्या डिव्हाइससह शेअर करत आहात त्याने प्रतिसाद दिला नाही. कृपया पुन्हा प्रयत्न करा.</translation>
 <translation id="725109152065019550">क्षमस्व, तुमच्या ॲडमिनिस्ट्रेटरने तुमच्या खात्यावरील बाह्य स्टोरेज अक्षम केले आहे.</translation>
 <translation id="7251346854160851420">डीफॉल्ट वॉलपेपर</translation>
@@ -7726,6 +7734,7 @@
 <translation id="9026731007018893674">डाउनलोड करा</translation>
 <translation id="9026852570893462412">या प्रक्रियेला काही मिनिटे लागू शकतात. व्हर्च्युअल मशीन डाउनलोड करत आहे.</translation>
 <translation id="9027459031423301635">नवीन &amp;टॅब मध्ये लिंक उघडा</translation>
+<translation id="9029667986262585240">तुम्ही तुमच्या खात्यामध्ये वर्धित सुरक्षित ब्राउझिंग बंद केले.</translation>
 <translation id="9030515284705930323">तुमच्या संस्थेने तुमच्या खात्यासाठी Google Play स्टोअर सक्षम केले नाही. अधिक माहितीसाठी तुमच्या ॲडमिनिस्ट्रेटरशी संपर्क साधा.</translation>
 <translation id="9030754204056345429">अधिक जलद</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_ms.xtb b/chrome/app/resources/generated_resources_ms.xtb
index 5b301ac5..9b08cc5 100644
--- a/chrome/app/resources/generated_resources_ms.xtb
+++ b/chrome/app/resources/generated_resources_ms.xtb
@@ -415,6 +415,7 @@
            • Memadamkan kuki dan data laman lain yang bersifat sementara
             <ph name="LINE_BREAKS" />
            Penanda halaman, sejarah dan kata laluan yang disimpan tidak akan terjejas.</translation>
+<translation id="1425233109861512854">Kod akses tidak dicam</translation>
 <translation id="1426410128494586442">Ya</translation>
 <translation id="142655739075382478"><ph name="APP_NAME" /> disekat</translation>
 <translation id="1426870617281699524">Klik Cuba Lagi dan terima gesaan pada komputer anda</translation>
@@ -582,6 +583,7 @@
 <translation id="1593926297800505364">Simpan kaedah pembayaran</translation>
 <translation id="1595492813686795610">Linux sedang ditingkatkan</translation>
 <translation id="1596286373007273895">Tersedia</translation>
+<translation id="1596709061955594992">Bluetooth dimatikan. Untuk melihat peranti yang tersedia, hidupkan Bluetooth.</translation>
 <translation id="1598233202702788831">Kemas kini dilumpuhkan oleh pentadbir anda</translation>
 <translation id="1600857548979126453">Akses bahagian belakang penyahpepijat halaman</translation>
 <translation id="1601560923496285236">Gunakan</translation>
@@ -1143,6 +1145,7 @@
 <translation id="2148892889047469596">Hantar tab</translation>
 <translation id="2149973817440762519">Edit Penanda Halaman</translation>
 <translation id="2150139952286079145">Cari destinasi</translation>
+<translation id="2150491840563706000">Anda tidak mempunyai kebenaran untuk menghantar kepada paparan ini</translation>
 <translation id="2150661552845026580">Tambah "<ph name="EXTENSION_NAME" />"?</translation>
 <translation id="2151576029659734873">Indeks tab tidak sah dimasukkan.</translation>
 <translation id="2152281589789213846">Tambahkan pencetak pada profil anda</translation>
@@ -2611,6 +2614,7 @@
 <translation id="3654045516529121250">Baca tetapan kebolehaksesan anda</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{Apl mempunyai akses tetap kepada satu fail.}other{Apl mempunyai akses tetap kepada # fail.}}</translation>
 <translation id="3658871634334445293">Pemecutan TrackPoint</translation>
+<translation id="3659929705630080526">Anda telah memasukkan kod akses yang salah terlalu banyak kali. Cuba sebentar lagi</translation>
 <translation id="3660234220361471169">Tidak Dipercayai</translation>
 <translation id="3664511988987167893">Ikon Sambungan</translation>
 <translation id="3665589677786828986">Chrome mengesan bahawa sesetengah tetapan anda telah terganggu oleh program lain dan menetapkannya semula kepada tetapan lalainya yang asal.</translation>
@@ -2694,6 +2698,7 @@
 <translation id="3743842571276656710">Masukkan PIN untuk digandingkan dengan <ph name="DEVICE_NAME" /></translation>
 <translation id="3747077776423672805">Untuk mengalih keluar apl, pergi ke Tetapan &gt; Gedung Google Play &gt; Urus pilihan Android &gt; Apl atau Pengurus aplikasi. Kemudian, ketik apl yang ingin dinyahpasang (anda mungkin perlu meleret ke kanan atau ke kiri untuk mencari apl). Kemudian, ketik Nyahpasang atau Lumpuhkan.</translation>
 <translation id="3747220812138541072">Tunjukkan cadangan penulisan sebaris yang dipaparkan semasa anda menaip</translation>
+<translation id="3747603683749989726">Hidupkan keselamatan dipertingkat?</translation>
 <translation id="3748706263662799310">Laporkan pepijat</translation>
 <translation id="3750562496035670393">Chrome menyimpan kata laluan anda pada peranti ini, tetapi anda juga boleh menyimpan kata laluan pada Google Account anda. Kemudian, semua kata laluan dalam Google Account anda juga akan tersedia semasa anda log masuk.</translation>
 <translation id="3752253558646317685">Minta anak anda mengangkat jarinya beberapa kali untuk menyimpan cap jari</translation>
@@ -3209,6 +3214,7 @@
 <translation id="4279129444466079448">Anda boleh memasang sehingga <ph name="PROFILE_LIMIT" /> profil eSIM pada peranti ini. Untuk menambah profil lain, alih keluar profil yang sedia ada dahulu.</translation>
 <translation id="4281844954008187215">Syarat Perkhidmatan</translation>
 <translation id="4282196459431406533">Smart Lock dihidupkan</translation>
+<translation id="4284755288573763878">Matikan keselamatan dipertingkat?</translation>
 <translation id="4285418559658561636">Kemas kini Kata laluan</translation>
 <translation id="4285498937028063278">Menyahpin</translation>
 <translation id="428565720843367874">Perisian anti-virus gagal tanpa dijangka semasa mengimbas fail ini.</translation>
@@ -4682,6 +4688,7 @@
 <translation id="5865508026715185451"><ph name="APP_NAME" /> akan dijeda tidak lama lagi</translation>
 <translation id="586567932979200359">Anda menjalankan <ph name="PRODUCT_NAME" /> daripada imej cakeranya. Pemasangannya pada komputer anda membolehkan anda menjalankannya tanpa imej cakera, dan memastikannya adalah yang terkini.</translation>
 <translation id="5865733239029070421">Menghantar perangkaan penggunaan dan laporan ranap sistem secara automatik kepada Google</translation>
+<translation id="5865848066829891215">Isu komunikasi rangkaian</translation>
 <translation id="5867841422488265304">Cari atau taip alamat web</translation>
 <translation id="5869029295770560994">OK, Faham</translation>
 <translation id="5869522115854928033">Kata laluan disimpan</translation>
@@ -5975,6 +5982,7 @@
 <translation id="7243784282103630670">Terdapat ralat semasa meningkatkan Linux. Kami akan memulihkan bekas menggunakan sandaran anda.</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> x <ph name="HEIGHT" /> (Terbaik)</translation>
 <translation id="7246230585855757313">Masukkan semula kunci keselamatan anda dan cuba lagi</translation>
+<translation id="7249197363678284330">Tukar tetapan ini dalam bar alamat.</translation>
 <translation id="7250616558727237648">Peranti yang anda pilih untuk berkongsi fail tidak memberikan respons. Sila cuba lagi.</translation>
 <translation id="725109152065019550">Maaf, pentadbir anda telah melumpuhkan storan luar pada akaun anda.</translation>
 <translation id="7251346854160851420">Kertas dinding lalai</translation>
@@ -7730,6 +7738,7 @@
 <translation id="9026731007018893674">muat turun</translation>
 <translation id="9026852570893462412">Proses ini mungkin mengambil masa beberapa minit. Memuat turun mesin maya.</translation>
 <translation id="9027459031423301635">Buka Pautan dalam Tab &amp;Baharu</translation>
+<translation id="9029667986262585240">Anda mematikan Penyemakan Imbas Selamat Dipertingkat dalam akaun anda.</translation>
 <translation id="9030515284705930323">Organisasi anda belum mendayakan Gedung Google Play untuk akaun anda. Hubungi pentadbir anda untuk mendapatkan maklumat lanjut.</translation>
 <translation id="9030754204056345429">Lebih laju</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_ne.xtb b/chrome/app/resources/generated_resources_ne.xtb
index 1f2e584..9075254 100644
--- a/chrome/app/resources/generated_resources_ne.xtb
+++ b/chrome/app/resources/generated_resources_ne.xtb
@@ -411,6 +411,7 @@
            • कुकी तथा साइटका अन्य अस्थायी डेटा मेटिने छन्
             <ph name="LINE_BREAKS" />
            बुकमार्क, इतिहास तथा सुरक्षित गरिएका पासवर्डहरूमा केही पनि असर पर्ने छैन।</translation>
+<translation id="1425233109861512854">एक्सेस कोड पहिचान गर्न सकिएन</translation>
 <translation id="1426410128494586442">हो</translation>
 <translation id="142655739075382478"><ph name="APP_NAME" /> माथि रोक लगाइएको छ</translation>
 <translation id="1426870617281699524">फेरि प्रयास गर्नुहोस् नामक बटनमा क्लिक गर्नुहोस् र आफ्नो कम्प्युटरमा प्रम्प्टलाई स्वीकार गर्नुहोस्</translation>
@@ -576,6 +577,7 @@
 <translation id="1593926297800505364">भुक्तानी विधि सेभ गर्नुहोस्</translation>
 <translation id="1595492813686795610">Linux को स्तरवृद्धि हुँदै छ</translation>
 <translation id="1596286373007273895">उपलब्ध छ</translation>
+<translation id="1596709061955594992">ब्लुटुथ अफ गरिएको छ। तपाईं उपलब्ध डिभाइसहरू हेर्न चाहनुहुन्छ भने ब्लुटुथ अन गर्नुहोस्।</translation>
 <translation id="1598233202702788831">अद्यावधिकहरू तपाईँको प्रशासकद्वारा असक्षम गरिएका छन्।</translation>
 <translation id="1600857548979126453">पृष्ठ डिबजर ब्याकएन्ड पहुँच गर्नुहोस्</translation>
 <translation id="1601560923496285236">लागु गर्नुहोस्</translation>
@@ -1128,6 +1130,7 @@
 <translation id="2148892889047469596">Cast ट्याब</translation>
 <translation id="2149973817440762519">बुकमार्क सम्पादन गर्नुहोस्</translation>
 <translation id="2150139952286079145">खोज गन्तव्यहरू</translation>
+<translation id="2150491840563706000">तपाईंलाई यो डिस्प्लेमा कास्ट गर्ने अनुमति दिइएको छैन</translation>
 <translation id="2150661552845026580">"<ph name="EXTENSION_NAME" />" थप्ने?</translation>
 <translation id="2151576029659734873">अवैध ट्याब सूचकांक प्रवेश गरिएको छ।</translation>
 <translation id="2152281589789213846">आफ्नो प्रोफाइलमा प्रिन्टरहरू थप्नुहोस्</translation>
@@ -2594,6 +2597,7 @@
 <translation id="3654045516529121250">तपाइँका पहुँच सेटिङहरू पढ्नुहोस्</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{ यसको एउटा फाइलमा स्थायी पहुँच छ।}other{ यसको # फाइलहरूमा स्थायी पहुँच छ।}}</translation>
 <translation id="3658871634334445293">TrackPoint एक्सिलेरेसन</translation>
+<translation id="3659929705630080526">तपाईंले अत्यन्तै धेरै पटक गलत एक्सेस कोड हाल्नुभएको छ। पछि फेरि प्रयास गर्नुहोस्</translation>
 <translation id="3660234220361471169">अविश्वासनीय</translation>
 <translation id="3664511988987167893">विस्तारसम्बन्धी आइकन</translation>
 <translation id="3665589677786828986">Chrome ले तपाईंका केही सेटिङहरू अर्को कार्यक्रमले विकृत भएका थिए भनि पत्ता लगाएर तिनीहरूलाई तिनीहरूको मूल पूर्वनिर्धारितहरूमा रिसेट गर्‍यो।</translation>
@@ -2677,6 +2681,7 @@
 <translation id="3743842571276656710"><ph name="DEVICE_NAME" /> सँग कनेक्ट गर्न PIN हाल्नुहोस्</translation>
 <translation id="3747077776423672805">एप हटाउन, सेटिङहरू &gt; Google Play स्टोर &gt; Android का प्राथमिकताहरू व्यवस्थापन गर्नुहोस् &gt; एपहरू वा एप प्रबन्धक नामक विकल्पमा जानुहोस्। त्यसपछि आफूले स्थापना रद्द गर्न चाहेको एपमा ट्याप गर्नुहोस् (उक्त एप फेला पार्न तपाईंले दायाँ वा बायाँतिर स्वाइप गर्नु पर्ने हुन सक्छ)। त्यसपछि स्थापना रद्द गर्नुहोस् वा असक्षम पार्नुहोस् नामक बटनमा ट्याप गर्नुहोस्।</translation>
 <translation id="3747220812138541072">मैले टाइप गर्दै जाँदा लेखनसम्बन्धी इनलाइन सुझावहरू देखाइयोस्</translation>
+<translation id="3747603683749989726">परिष्कृत सुरक्षा अन गर्ने हो?</translation>
 <translation id="3748706263662799310">बगका बारेमा रिपोर्ट गर्नुहोस्</translation>
 <translation id="3750562496035670393">Chrome ले तपाईंको पासवर्ड यो डिभाइसमा सेभ गरेको छ तर तपाईं उक्त पासवर्ड यो डिभाइसको साटो आफ्नो Google खातामा सेभ गर्न सक्नुहुन्छ। तपाईंले त्यसो गर्नुभयो भने तपाईं साइन इन भएका बेला पनि तपाईंको Google खातामा भएका सबै पासवर्डहरू उपलब्ध हुने छन्।</translation>
 <translation id="3752253558646317685">फिंगरप्रिन्ट सेभ गर्न आफ्ना बच्चालाई उनको औँला उठाउँदै राख्दै गर्न लगाउनुहोस्</translation>
@@ -3191,6 +3196,7 @@
 <translation id="4279129444466079448">तपाईं यो डिभाइसमा बढीमा <ph name="PROFILE_LIMIT" /> वटा eSIM प्रोफाइल इन्स्टल गर्न सक्नुहुन्छ। अर्को प्रोफाइल हाल्न सर्वप्रथम अहिलेको प्रोफाइल हटाउनुहोस्।</translation>
 <translation id="4281844954008187215">सेवाका सर्तहरू</translation>
 <translation id="4282196459431406533">Smart Lock सक्रिय छ</translation>
+<translation id="4284755288573763878">परिष्कृत सुरक्षा अफ गर्ने हो?</translation>
 <translation id="4285418559658561636">पासवर्ड अपडेट गर्नुहोस्</translation>
 <translation id="4285498937028063278">अनपिन गर्नुहोस्</translation>
 <translation id="428565720843367874">यो फाइल स्क्यान गर्दा एन्टि-भाइरस सफ्टवेयर अनपेक्षित रूपमा असफल भयो।</translation>
@@ -4663,6 +4669,7 @@
 <translation id="5865508026715185451"><ph name="APP_NAME" /> चाँडै पज हुने छ</translation>
 <translation id="586567932979200359">तपाईंले <ph name="PRODUCT_NAME" /> लाई यसको डिस्क छविबाट चलाइरहनु भएको छ। यसलाई तपाईंको कम्प्युटरमा स्थापना गर्नाले यसले तपाईंलाई डिस्क छवि बिना चालू गर्न अनुमति दिन्छ, र यो नवीनतम रहने सुनिश्चित गर्छ।</translation>
 <translation id="5865733239029070421">प्रयोगसम्बन्धी तथ्याङ्क र क्र्यास रिपोर्टहरू स्वतः Google मा पठाउँछ</translation>
+<translation id="5865848066829891215">नेटवर्क सञ्चारसम्बन्धी समस्या देखिए</translation>
 <translation id="5867841422488265304">वेब ठेगाना खोज्नुहोस् वा टाइप गर्नुहोस्</translation>
 <translation id="5869029295770560994">ठिक छ, बुझेँ</translation>
 <translation id="5869522115854928033">बचत गरिएका पासवर्डहरू</translation>
@@ -5955,6 +5962,7 @@
 <translation id="7243784282103630670">Linux को स्तरवृद्धि गर्ने क्रममा कुनै त्रुटि भयो। हामी तपाईंको ब्याकअप प्रयोग गरी उक्त कन्टेनर पुनर्स्थापना गर्ने छौँ।</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> गुणा <ph name="HEIGHT" /> (उत्कृष्ट)</translation>
 <translation id="7246230585855757313">आफ्नो सुरक्षा साँचो पुनः प्रविष्टि गरी फेरि प्रयास गर्नुहोस्</translation>
+<translation id="7249197363678284330">एड्रेस बारमा गई यो सेटिङ बदल्नुहोस्।</translation>
 <translation id="7250616558727237648">तपाईंले जुन यन्त्रसँग यो फाइल सेयर गर्न चाहनुहुन्छ उक्त यन्त्रबाट कुनै प्रतिक्रिया प्राप्त भएन। कृपया फेरि प्रयास गर्नुहोस्।</translation>
 <translation id="725109152065019550">माफ गर्नुहोस्, तपाईंको व्यवस्थापकले तपाईंखो यन्त्रमा बाह्य भन्डारणलाई अक्षम बनाएको छ।</translation>
 <translation id="7251346854160851420">डिफल्ट वालपेपर</translation>
@@ -7708,6 +7716,7 @@
 <translation id="9026731007018893674">डाउनलोड गर्नुहोस्</translation>
 <translation id="9026852570893462412">यो प्रक्रियामा केही मिनेट लाग्न सक्छ। भर्चुअल मेसिन डाउनलोड गर्दै।</translation>
 <translation id="9027459031423301635">लिङ्कलाई नयाँ &amp;ट्याबमा खोल्नुहोस्</translation>
+<translation id="9029667986262585240">तपाईंले आफ्नो खातामा परिष्कृत Safe Browsing अफ गर्नुभयो।</translation>
 <translation id="9030515284705930323">तपाईँको संगठनले तपाईँको खाताको लागि Google Play स्टोरलाई सक्रिय गरेको छैन। थप जानकारीका लागि आफ्नो प्रशासकलाई सम्पर्क गर्नुहोस्।</translation>
 <translation id="9030754204056345429">अझ छिटो</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_nl.xtb b/chrome/app/resources/generated_resources_nl.xtb
index 626b2a6..6c20e6a 100644
--- a/chrome/app/resources/generated_resources_nl.xtb
+++ b/chrome/app/resources/generated_resources_nl.xtb
@@ -4026,7 +4026,7 @@
 <translation id="5184209580557088469">Er bestaat al een ticket met deze gebruikersnaam</translation>
 <translation id="5184662919967270437">Je apparaat updaten</translation>
 <translation id="5185359571430619712">Extensies controleren</translation>
-<translation id="5185386675596372454">De nieuwste versie van <ph name="EXTENSION_NAME" /> staat uit omdat er meer rechten voor nodig zijn.</translation>
+<translation id="5185386675596372454">De nieuwste versie van <ph name="EXTENSION_NAME" /> is uitgezet omdat er meer rechten voor nodig zijn.</translation>
 <translation id="5185500136143151980">Geen internet</translation>
 <translation id="5187826826541650604"><ph name="KEY_NAME" /> (<ph name="DEVICE" />)</translation>
 <translation id="5190187232518914472">Beleef je favoriete herinneringen opnieuw. Ga naar <ph name="LINK_BEGIN" />Google Foto's<ph name="LINK_END" /> om albums toe te voegen of te bewerken.</translation>
@@ -7476,7 +7476,7 @@
 <translation id="8777628254805677039">root-wachtwoord</translation>
 <translation id="8778328560035799409">Je huidige toegewezen schakelaars worden gewist</translation>
 <translation id="8780123805589053431">Afbeeldingsbeschrijvingen ophalen van Google</translation>
-<translation id="8780443667474968681">Gesproken zoekopdracht staat uit.</translation>
+<translation id="8780443667474968681">Gesproken zoekopdracht is uitgezet.</translation>
 <translation id="8781834595282316166">Nieuw tabblad in groep</translation>
 <translation id="8782565991310229362">Starten van kiosk-app geannuleerd.</translation>
 <translation id="8783834180813871000">Typ de bluetooth-koppelingscode en druk op Return of Enter.</translation>
diff --git a/chrome/app/resources/generated_resources_pa.xtb b/chrome/app/resources/generated_resources_pa.xtb
index 966c929..6f939f50 100644
--- a/chrome/app/resources/generated_resources_pa.xtb
+++ b/chrome/app/resources/generated_resources_pa.xtb
@@ -416,6 +416,7 @@
            • ਕੁਕੀਜ਼ ਅਤੇ ਹੋਰ ਕੁਝ ਸਮੇਂ ਲਈ ਰੱਖੇ ਸਾਈਟ ਡਾਟੇ ਨੂੰ ਮਿਟਾਵੇਗੀ
             <ph name="LINE_BREAKS" />
            ਬੁੱਕਮਾਰਕ, ਇਤਿਹਾਸ ਅਤੇ ਰੱਖਿਅਤ ਕੀਤੇ ਪਾਸਵਰਡ ਪ੍ਰਭਾਵਿਤ ਨਹੀਂ ਹੋਣਗੇ।</translation>
+<translation id="1425233109861512854">ਪਹੁੰਚ ਕੋਡ ਦੀ ਪਛਾਣ ਨਹੀਂ ਹੋਈ</translation>
 <translation id="1426410128494586442">ਹਾਂ</translation>
 <translation id="142655739075382478"><ph name="APP_NAME" /> ਨੂੰ ਬਲਾਕ ਕੀਤਾ ਗਿਆ ਹੈ</translation>
 <translation id="1426870617281699524">ਮੁੜ-ਕੋਸ਼ਿਸ਼ ਕਰੋ 'ਤੇ ਕਲਿੱਕ ਕਰੋ ਅਤੇ ਆਪਣੇ ਕੰਪਿਊਟਰ 'ਤੇ ਉਤਪ੍ਰੇਰਕ ਸਵੀਕਾਰ ਕਰੋ</translation>
@@ -583,6 +584,7 @@
 <translation id="1593926297800505364">ਭੁਗਤਾਨ ਵਿਧੀ ਰੱਖਿਅਤ ਕਰੋ</translation>
 <translation id="1595492813686795610">Linux ਅੱਪਗ੍ਰੇਡ ਹੋ ਰਿਹਾ ਹੈ</translation>
 <translation id="1596286373007273895">ਉਪਲਬਧ</translation>
+<translation id="1596709061955594992">ਬਲੂਟੁੱਥ ਬੰਦ ਹੈ। ਉਪਲਬਧ ਡੀਵਾਈਸਾਂ ਨੂੰ ਦੇਖਣ ਲਈ, ਬਲੂਟੁੱਥ ਚਾਲੂ ਕਰੋ।</translation>
 <translation id="1598233202702788831">ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਅੱਪਡੇਟਾਂ ਅਯੋਗ ਬਣਾਈਆਂ ਗਈਆਂ ਹਨ।</translation>
 <translation id="1600857548979126453">ਪੰਨਾ ਡੀਬੱਗਰ ਤੱਕ ਪਹੁੰਚ ਬੈਕਨਡ</translation>
 <translation id="1601560923496285236">ਲਾਗੂ ਕਰੋ</translation>
@@ -1144,6 +1146,7 @@
 <translation id="2148892889047469596">ਟੈਬ ਨੂੰ ਕਾਸਟ ਕਰੋ</translation>
 <translation id="2149973817440762519">ਬੁੱਕਮਾਰਕ ਸੰਪਾਦਿਤ ਕਰੋ</translation>
 <translation id="2150139952286079145">ਮੰਜ਼ਿਲਾਂ ਖੋਜੋ</translation>
+<translation id="2150491840563706000">ਤੁਹਾਡੇ ਕੋਲ ਇਸ ਡਿਸਪਲੇ 'ਤੇ ਕਾਸਟ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।</translation>
 <translation id="2150661552845026580">ਕੀ "<ph name="EXTENSION_NAME" />" ਨੂੰ ਜੋੜਨਾ ਹੈ?</translation>
 <translation id="2151576029659734873">ਅਵੈਧ ਟੈਬ ਕ੍ਰਮ-ਸੂਚੀ ਦਰਜ ਕੀਤਾ ਗਿਆ।</translation>
 <translation id="2152281589789213846">ਪ੍ਰਿੰਟਰਾਂ ਨੂੰ ਆਪਣੇ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ</translation>
@@ -2611,6 +2614,7 @@
 <translation id="3654045516529121250">ਆਪਣੀਆਂ ਪਹੁੰਚਯੋਗਤਾ ਸੈਟਿੰਗਾਂ ਪੜ੍ਹੋ</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{ਇਸਦੀ ਇੱਕ ਫਾਈਲ ਤੱਕ ਸਥਾਈ ਪਹੁੰਚ ਹੈ।}one{ਇਸਦੀ # ਫਾਈਲਾਂ ਤੱਕ ਸਥਾਈ ਪਹੁੰਚ ਹੈ।}other{ਇਸਦੀ # ਫਾਈਲਾਂ ਤੱਕ ਸਥਾਈ ਪਹੁੰਚ ਹੈ।}}</translation>
 <translation id="3658871634334445293">TrackPoint ਐਕਸੈੱਲਰੇਸ਼ਨ</translation>
+<translation id="3659929705630080526">ਤੁਸੀਂ ਕਈ ਵਾਰ ਗਲਤ ਪਹੁੰਚ ਕੋਡ ਦਾਖਲ ਕੀਤਾ ਹੈ। ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ</translation>
 <translation id="3660234220361471169">ਭਰੋਸੇਯੋਗ ਨਹੀਂ</translation>
 <translation id="3664511988987167893">ਐਕਟੈਂਸ਼ਨ ਪ੍ਰਤੀਕ</translation>
 <translation id="3665589677786828986">Chrome ਨੇ ਪਤਾ ਲਗਾਇਆ ਕਿ ਕਿਸੇ ਹੋਰ ਪ੍ਰੋਗਰਾਮ ਨਾ ਤੁਹਾਡੀਆਂ ਕੁਝ ਸੈਟਿੰਗਾਂ ਨੂੰ ਖਰਾਬ ਕਰ ਦਿੱਤਾ ਹੈ ਅਤੇ ਉਹਨਾਂ ਨੂੰ ਮੂਲ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੈਟਿੰਗਾਂ 'ਤੇ ਰੀਸੈੱਟ ਕੀਤਾ ਗਿਆ ਸੀ।</translation>
@@ -2694,6 +2698,7 @@
 <translation id="3743842571276656710"><ph name="DEVICE_NAME" /> ਨਾਲ ਜੋੜਾਬੱਧ ਕਰਨ ਲਈ ਪਿੰਨ ਦਾਖਲ ਕਰੋ</translation>
 <translation id="3747077776423672805">ਐਪਾਂ ਨੂੰ ਹਟਾਉਣ ਲਈ, ਸੈਟਿੰਗਾਂ &gt; Google Play Store &gt; Android ਤਰਜੀਹਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ &gt; ਐਪਾਂ ਜਾਂ ਐਪਲੀਕੇਸ਼ਨ ਪ੍ਰਬੰਧਕ 'ਤੇ ਜਾਓ। ਫਿਰ ਉਸ ਐਪ 'ਤੇ ਟੈਪ ਕਰੋ ਜਿਸਨੂੰ ਤੁਸੀਂ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ (ਐਪ ਨੂੰ ਲੱਭਣ ਲਈ ਤੁਹਾਨੂੰ ਸੱਜੇ ਜਾਂ ਖੱਬੇ ਸਵਾਈਪ ਕਰਨਾ ਪੈ ਸਕਦਾ ਹੈ)। ਫਿਰ 'ਅਣਸਥਾਪਤ ਕਰੋ' ਜਾਂ 'ਬੰਦ ਕਰੋ' 'ਤੇ ਟੈਪ ਕਰੋ।</translation>
 <translation id="3747220812138541072">ਟਾਈਪ ਕਰਨ ਵੇਲੇ ਦਿਸਣ ਵਾਲੇ ਲਿਖਣ ਸੰਬੰਧੀ ਇਨਲਾਈਨ ਸੁਝਾਅ ਦਿਖਾਓ</translation>
+<translation id="3747603683749989726">ਕੀ ਵਿਸਤ੍ਰਿਤ ਸੁਰੱਖਿਆ ਨੂੰ ਚਾਲੂ ਕਰਨਾ ਹੈ?</translation>
 <translation id="3748706263662799310">ਬੱਗ ਦੀ ਰਿਪੋਰਟ ਕਰੋ</translation>
 <translation id="3750562496035670393">Chrome ਨੇ ਇਸ ਡੀਵਾਈਸ 'ਤੇ ਤੁਹਾਡਾ ਪਾਸਵਰਡ ਰੱਖਿਅਤ ਕਰ ਲਿਆ ਹੈ, ਪਰ ਤੁਸੀਂ ਇਸਦੀ ਬਜਾਏ Google ਖਾਤੇ ਵਿੱਚ ਰੱਖਿਅਤ ਕਰ ਸਕਦੇ ਹੋ। ਫਿਰ, ਤੁਹਾਡੇ ਸਾਈਨ-ਇਨ ਹੋਣ ਵੇਲੇ ਸਾਰੇ ਪਾਸਵਰਡ ਤੁਹਾਡੇ Google ਖਾਤੇ ਵਿੱਚ ਉਪਲਬਧ ਹੋਣਗੇ।</translation>
 <translation id="3752253558646317685">ਫਿੰਗਰਪ੍ਰਿੰਟ ਨੂੰ ਰੱਖਿਅਤ ਕਰਨ ਲਈ ਆਪਣੇ ਬੱਚੇ ਤੋਂ ਉਸਦੀ ਉਂਗਲ ਨੂੰ ਸਪਰਸ਼ ਕਰਵਾਉਂਦੇ ਰਹੋ</translation>
@@ -3209,6 +3214,7 @@
 <translation id="4279129444466079448">ਤੁਸੀਂ ਇਸ ਡੀਵਾਈਸ 'ਤੇ <ph name="PROFILE_LIMIT" /> ਤੱਕ ਈ-ਸਿਮ ਪ੍ਰੋਫਾਈਲ ਸਥਾਪਤ ਕਰ ਸਕਦੇ ਹੋ। ਇੱਕ ਹੋਰ ਪ੍ਰੋਫਾਈਲ ਸ਼ਾਮਲ ਕਰਨ ਲਈ, ਪਹਿਲਾਂ ਮੋਜੂਦਾ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਹਟਾਓ।</translation>
 <translation id="4281844954008187215">ਸੇਵਾ ਦੀਆਂ ਮਦਾਂ</translation>
 <translation id="4282196459431406533">ਸਮਾਰਟ ਲੌਕ ਚਾਲੂ ਹੈ</translation>
+<translation id="4284755288573763878">ਕੀ ਵਿਸਤ੍ਰਿਤ ਸੁਰੱਖਿਆ ਨੂੰ ਬੰਦ ਕਰਨਾ ਹੈ?</translation>
 <translation id="4285418559658561636">ਪਾਸਵਰਡ ਅੱਪਡੇਟ ਕਰੋ</translation>
 <translation id="4285498937028063278">ਅਨਪਿਨ ਕਰੋ</translation>
 <translation id="428565720843367874">ਇਹ ਫਾਈਲ ਸਕੈਨ ਕਰਦੇ ਸਮੇਂ ਐਂਟੀ-ਵਾਇਰਸ ਸਾਫਟਵੇਅਰ ਅਚਾਨਕ ਅਸਫਲ ਹੋਇਆ।</translation>
@@ -4682,6 +4688,7 @@
 <translation id="5865508026715185451"><ph name="APP_NAME" /> ਨੂੰ ਜਲਦ ਰੋਕਿਆ ਜਾਵੇਗਾ</translation>
 <translation id="586567932979200359">ਤੁਸੀਂ ਇਸਦੇ ਡਿਸਕ ਚਿੱਤਰ ਤੋਂ <ph name="PRODUCT_NAME" /> ਚਲਾ ਰਹੇ ਹੋ। ਇਸਨੂੰ ਆਪਣੇ ਕੰਪਿਊਟਰ 'ਤੇ ਸਥਾਪਤ ਕਰਨ ਨਾਲ ਇਹ ਤੁਹਾਨੂੰ ਬਿਨਾਂ ਡਿਸਕ ਇਮੇਜ ਦੇ ਚਲਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦਾ ਹੈ ਅਤੇ ਇਹ ਪੱਕਾ ਕਰਦਾ ਹੈ ਕਿ ਇਸਨੂੰ ਅੱਪ ਟੂ ਡੇਟ ਰੱਖਿਆ ਜਾਵੇਗਾ।</translation>
 <translation id="5865733239029070421">Google ਨੂੰ ਵਰਤੋਂ ਅੰਕੜੇ ਅਤੇ ਕ੍ਰੈਸ਼ ਰਿਪੋਰਟਾਂ ਸਵੈਚਲਿਤ ਤੌਰ 'ਤੇ ਭੇਜਦੀ ਹੈ</translation>
+<translation id="5865848066829891215">ਨੈੱਟਵਰਕ ਸੰਚਾਰ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ</translation>
 <translation id="5867841422488265304">ਖੋਜੋ ਜਾਂ ਵੈੱਬਸਾਈਟ ਪਤਾ ਟਾਈਪ ਕਰੋ</translation>
 <translation id="5869029295770560994">ਠੀਕ, ਸਮਝ ਲਿਆ</translation>
 <translation id="5869522115854928033">ਸੁਰੱਖਿਅਤ ਕੀਤੇ ਪਾਸਵਰਡ</translation>
@@ -5976,6 +5983,7 @@
 <translation id="7243784282103630670">Linux ਨੂੰ ਅੱਪਗ੍ਰੇਡ ਕਰਨ ਦੌਰਾਨ ਗੜਬੜ ਹੋਈ। ਅਸੀਂ ਤੁਹਾਡਾ ਬੈਕਅੱਪ ਵਰਤ ਕੇ ਕੰਟੇਨਰ ਨੂੰ ਮੁੜ-ਬਹਾਲ ਕਰਾਂਗੇ।</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> x <ph name="HEIGHT" /> (ਸਭ ਤੋਂ ਵਧੀਆ)</translation>
 <translation id="7246230585855757313">ਆਪਣੀ ਸੁਰੱਖਿਆ ਕੁੰਜੀ ਨੂੰ ਮੁੜ-ਪਾਓ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ</translation>
+<translation id="7249197363678284330">ਇਸ ਸੈਟਿੰਗ ਨੂੰ ਪਤਾ ਬਾਰ ਵਿੱਚ ਬਦਲੋ।</translation>
 <translation id="7250616558727237648">ਜਿਸ ਡੀਵਾਈਸ ਨਾਲ ਤੁਸੀਂ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ, ਉਸ ਨੇ ਜਵਾਬ ਨਹੀਂ ਦਿੱਤਾ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।</translation>
 <translation id="725109152065019550">ਮਾਫ਼ ਕਰਨਾ, ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਨੇ ਤੁਹਾਡੇ ਖਾਤੇ 'ਤੇ ਬਾਹਰੀ ਸਟੋਰੇਜ ਨੂੰ ਬੰਦ ਕਰ ਦਿੱਤਾ ਹੈ।</translation>
 <translation id="7251346854160851420">ਪੂਰਵ-ਨਿਰਧਾਰਤ ਵਾਲਪੇਪਰ</translation>
@@ -7729,6 +7737,7 @@
 <translation id="9026731007018893674">ਡਾਊਨਲੋਡ</translation>
 <translation id="9026852570893462412">ਇਸ ਪ੍ਰਕਿਰਿਆ ਵਿੱਚ ਕੁਝ ਮਿੰਟ ਲੱਗ ਸਕਦੇ ਹਨ। ਆਭਾਸੀ ਮਸ਼ੀਨ ਡਾਊਨਲੋਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ।</translation>
 <translation id="9027459031423301635">ਨਵੀਂ &amp;ਟੈਬ ਵਿੱਚ ਲਿੰਕ ਖੋਲ੍ਹੋ</translation>
+<translation id="9029667986262585240">ਤੁਸੀਂ ਆਪਣੇ ਖਾਤੇ ਵਿੱਚ ਵਿਸਤ੍ਰਿਤ ਸੁਰੱਖਿਅਤ ਬ੍ਰਾਊਜ਼ਿੰਗ ਨੂੰ ਬੰਦ ਕਰ ਦਿੱਤਾ ਹੈ।</translation>
 <translation id="9030515284705930323">ਤੁਹਾਡੇ ਸੰਗਠਨ ਨੇ ਤੁਹਾਡੇ ਖਾਤੇ ਲਈ Google Play Store ਨੂੰ ਚਾਲੂ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਵਧੇਰੇ ਜਾਣਕਾਰੀ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।</translation>
 <translation id="9030754204056345429">ਵਧੇਰੇ ਤੇਜ਼</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_sk.xtb b/chrome/app/resources/generated_resources_sk.xtb
index 8144972..fd2a02dc 100644
--- a/chrome/app/resources/generated_resources_sk.xtb
+++ b/chrome/app/resources/generated_resources_sk.xtb
@@ -415,6 +415,7 @@
            • odstráni súbory cookie a ostatné dočasné údaje webov.
             <ph name="LINE_BREAKS" />
            Záložky, história a uložené heslá nebudú ovplyvnené.</translation>
+<translation id="1425233109861512854">Prístupový kód nebol rozpoznaný</translation>
 <translation id="1426410128494586442">Áno</translation>
 <translation id="142655739075382478">Aplikácia <ph name="APP_NAME" /> je blokovaná</translation>
 <translation id="1426870617281699524">Kliknite na možnosť Skúsiť znova a potvrďte výzvu v počítači</translation>
@@ -581,6 +582,7 @@
 <translation id="1593926297800505364">Uložiť spôsob platby</translation>
 <translation id="1595492813686795610">Linux sa inovuje</translation>
 <translation id="1596286373007273895">K dispozícii</translation>
+<translation id="1596709061955594992">Rozhranie Bluetooth je vypnuté. Ak chcete zobraziť dostupné zariadenia, zapnite ho.</translation>
 <translation id="1598233202702788831">Aktualizácie zakázal váš správca.</translation>
 <translation id="1600857548979126453">Pristupovať k ladiacemu nástroju stránok na strane servera</translation>
 <translation id="1601560923496285236">Použiť</translation>
@@ -1133,6 +1135,7 @@
 <translation id="2148892889047469596">Prenášanie karty</translation>
 <translation id="2149973817440762519">Upraviť záložku</translation>
 <translation id="2150139952286079145">Hľadať ciele</translation>
+<translation id="2150491840563706000">Nemáte povolenie spustiť prenos na tejto obrazovke</translation>
 <translation id="2150661552845026580">Pridať rozšírenie <ph name="EXTENSION_NAME" />?</translation>
 <translation id="2151576029659734873">Zadali ste neplatný index kariet.</translation>
 <translation id="2152281589789213846">Pridanie tlačiarní do vášho profilu</translation>
@@ -2598,6 +2601,7 @@
 <translation id="3654045516529121250">Čítať nastavenia dostupnosti</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{Aplikácia má trvalý prístup k jednému súboru.}few{Aplikácia má trvalý prístup k # súborom.}many{Aplikácia má trvalý prístup k # súboru.}other{Aplikácia má trvalý prístup k # súborom.}}</translation>
 <translation id="3658871634334445293">Akcelerácia zariadenia TrackPoint</translation>
+<translation id="3659929705630080526">Príliš veľakrát ste zadali nesprávny prístupový kód. Skúste to neskôr</translation>
 <translation id="3660234220361471169">Nedôveryhodné</translation>
 <translation id="3664511988987167893">Ikona rozšírenia</translation>
 <translation id="3665589677786828986">Chrome zistil, že niektoré nastavenia boli poškodené iným programom, a preto obnovil pôvodné predvolené nastavenia.</translation>
@@ -2681,6 +2685,7 @@
 <translation id="3743842571276656710">Zadajte PIN na spárovanie so zariadením <ph name="DEVICE_NAME" /></translation>
 <translation id="3747077776423672805">Ak chcete odstrániť aplikácie, prejdite do časti Nastavenia &gt; Obchod Google Play &gt; Spravovať predvoľby Androidu &gt; Aplikácie alebo Správca aplikácií. Potom klepnite na aplikáciu, ktorú chcete odinštalovať (možno budete musieť aplikáciu nájsť potiahnutím prstom doľava alebo doprava). Potom klepnite na možnosť Odinštalovať alebo Deaktivovať.</translation>
 <translation id="3747220812138541072">Zobrazovať v texte návrhy písania, kým píšete</translation>
+<translation id="3747603683749989726">Chcete zapnúť zlepšené zabezpečenie?</translation>
 <translation id="3748706263662799310">Nahlásiť chybu</translation>
 <translation id="3750562496035670393">Chrome uložil vaše heslo do tohto zariadenia, ale môžete si ho namiesto toho uložiť do účtu Google. Potom budete mať tiež k dispozícii všetky heslá zo svojho účtu Google, kým sa neodhlásite.</translation>
 <translation id="3752253558646317685">Vaše dieťa musí zdvíhať prst, aby sa odtlačok uložil</translation>
@@ -3196,6 +3201,7 @@
 <translation id="4279129444466079448">V tomto zariadení môžete nainštalovať maximálne tento počet profilov eSIM: <ph name="PROFILE_LIMIT" />. Ak chcete pridať ďalší profil, najprv odstráňte existujúci.</translation>
 <translation id="4281844954008187215">Zmluvné podmienky</translation>
 <translation id="4282196459431406533">Smart Lock je zapnutý</translation>
+<translation id="4284755288573763878">Chcete vypnúť zlepšené zabezpečenie?</translation>
 <translation id="4285418559658561636">Aktualizovať heslo</translation>
 <translation id="4285498937028063278">Odopnúť</translation>
 <translation id="428565720843367874">Antivírusový softvér pri skenovaní tohto súboru neočakávane zlyhal.</translation>
@@ -4668,6 +4674,7 @@
 <translation id="5865508026715185451">Aplikácia <ph name="APP_NAME" /> bude čoskoro pozastavená</translation>
 <translation id="586567932979200359">Prehliadač <ph name="PRODUCT_NAME" /> je spustený z jeho obrazu disku. Inštalácia do počítača vám umožní spustiť ho bez obrazu disku a zabezpečí jeho aktualizáciu.</translation>
 <translation id="5865733239029070421">Automaticky odosiela štatistiky o používaní a správy o zlyhaní Googlu</translation>
+<translation id="5865848066829891215">Problémy s komunikáciou v sieti</translation>
 <translation id="5867841422488265304">Zadajte dopyt alebo webovú adresu</translation>
 <translation id="5869029295770560994">Dobre</translation>
 <translation id="5869522115854928033">Uložené heslá</translation>
@@ -5961,6 +5968,7 @@
 <translation id="7243784282103630670">Počas inovácie systému Linux sa vyskytla chyba. Kontajner obnovíme pomocou vašej zálohy.</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> x <ph name="HEIGHT" /></translation>
 <translation id="7246230585855757313">Bezpečnostný kľúč opäť vložte a skúste to znova</translation>
+<translation id="7249197363678284330">Toto nastavenie môžete zmeniť na paneli s adresou.</translation>
 <translation id="7250616558727237648">Zariadenie, s ktorým položku zdieľate, neodpovedalo. Skúste to znova.</translation>
 <translation id="725109152065019550">Je nám ľúto, správca vo vašom účte zakázal používanie externých ukladacích priestorov.</translation>
 <translation id="7251346854160851420">Predvolená tapeta</translation>
@@ -7716,6 +7724,7 @@
 <translation id="9026731007018893674">stiahnuť</translation>
 <translation id="9026852570893462412">Tento proces môže trvať niekoľko minút. Sťahuje sa virtuálny počítač.</translation>
 <translation id="9027459031423301635">Otvoriť odkaz na novej &amp;karte</translation>
+<translation id="9029667986262585240">V účte ste vypli Zlepšené bezpečné prehliadanie.</translation>
 <translation id="9030515284705930323">Vaša organizácia nepovoľuje vo vašom účte Obchod Google Play. Ak chcete získať ďalšie informácie, kontaktujte svojho správcu.</translation>
 <translation id="9030754204056345429">Rýchlejšia</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_sw.xtb b/chrome/app/resources/generated_resources_sw.xtb
index 072d88e3..c55c065 100644
--- a/chrome/app/resources/generated_resources_sw.xtb
+++ b/chrome/app/resources/generated_resources_sw.xtb
@@ -415,6 +415,7 @@
            • Kitafuta vidakuzi na data nyingine ya tovuti ya muda
             <ph name="LINE_BREAKS" />
            Alamisho, historia na manenosiri uliyoyahifadhi hayataathiriwa.</translation>
+<translation id="1425233109861512854">Msimbo wa kufikia hautambuliki</translation>
 <translation id="1426410128494586442">Ndiyo</translation>
 <translation id="142655739075382478"><ph name="APP_NAME" /> imezuiwa</translation>
 <translation id="1426870617281699524">Bofya Jaribu Tena na ukubali kidokezo kwenye kompyuta yako</translation>
@@ -579,6 +580,7 @@
 <translation id="1593926297800505364">Hifadhi njia ya kulipa</translation>
 <translation id="1595492813686795610">Inaweka toleo jipya la Linux</translation>
 <translation id="1596286373007273895">Inapatikana</translation>
+<translation id="1596709061955594992">Bluetooth imezimwa. Ili uone vifaa vinavyopatikana, washa Bluetooth.</translation>
 <translation id="1598233202702788831">Sasisho zimezimwa na msimamizi wako.</translation>
 <translation id="1600857548979126453">Fikia sehemu ya nyuma ya kitatuzi ukurasa</translation>
 <translation id="1601560923496285236">Tekeleza</translation>
@@ -1139,6 +1141,7 @@
 <translation id="2148892889047469596">Tuma kichupo</translation>
 <translation id="2149973817440762519">Badilisha Alamisho</translation>
 <translation id="2150139952286079145">Mahali pa kutafuta</translation>
+<translation id="2150491840563706000">Huna ruhusa ya kutuma kwenye skrini hii</translation>
 <translation id="2150661552845026580">Ungependa kuongeza "<ph name="EXTENSION_NAME" />"?</translation>
 <translation id="2151576029659734873">Uorodheshaji batili wa kichupo umeingizwa.</translation>
 <translation id="2152281589789213846">Weka printa kwenye wasifu wako</translation>
@@ -1449,6 +1452,7 @@
 <translation id="245322989586167203">Tovuti huunganisha kwenye milango ya kutuma biti za data kwa mfululizo kwa ajili ya vipengele vya uhamishaji wa data, kama vile kuweka mipangilio ya mtandao wako</translation>
 <translation id="2453706416476934374">Ili <ph name="SUPERVISED_USER_NAME" /> aweze kupata majibu yanayomfaa zaidi anapouliza maswali, ruhusu programu ya Mratibu ifikie picha ya yaliyo kwenye skrini ya <ph name="SUPERVISED_USER_NAME" />. Huenda maelezo haya pia yakajumuisha nyimbo au video zinazocheza.</translation>
 <translation id="2453860139492968684">Maliza</translation>
+<translation id="2454206500483040640">Vilivyotenganishwa</translation>
 <translation id="2454247629720664989">Neno muhimu</translation>
 <translation id="2454264884354864965">Kamera imezimwa</translation>
 <translation id="2454524890947537054">Ungependa kuidhinisha ombi la tovuti?</translation>
@@ -1811,6 +1815,7 @@
 <translation id="2806891468525657116">Tayari umeweka njia hii ya mkato</translation>
 <translation id="2807517655263062534">Faili unazopakua zitaonekana hapa</translation>
 <translation id="2809586584051668049">na <ph name="NUMBER_ADDITIONAL_DISABLED" /> zaidi</translation>
+<translation id="2810235462964014915">Vilivyoomba ruhusa ya kusoma na kubadilisha <ph name="SITE_NAME" /></translation>
 <translation id="2811205483104563968">Akaunti</translation>
 <translation id="2812049959647166806">Thunderbolt haiwezi kutumika</translation>
 <translation id="2812989263793994277">Usionyeshe picha zozote</translation>
@@ -2607,6 +2612,7 @@
 <translation id="3654045516529121250">Soma mipangilio yako ya ufikiaji</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{Ina idhini ya ufikiaji wa kudumu wa faili moja.}other{Ina idhini ya ufikiaji wa kudumu wa faili #.}}</translation>
 <translation id="3658871634334445293">Kuongeza kasi ya TrackPoint</translation>
+<translation id="3659929705630080526">Umeweka msimbo wa kufikia usio sahihi mara nyingi mno. Jaribu tena baadaye</translation>
 <translation id="3660234220361471169">Haviaminiki</translation>
 <translation id="3664511988987167893">Aikoni ya Kiendelezi</translation>
 <translation id="3665589677786828986">Chrome imegundua kuwa baadhi ya mipangilio yako ilivurugwa na programu nyingine na ikairejesha kwenye mipangilio yake iliyotoka nayo kiwandani.</translation>
@@ -2690,6 +2696,7 @@
 <translation id="3743842571276656710">Weka PIN ili uoanishe na <ph name="DEVICE_NAME" /></translation>
 <translation id="3747077776423672805">Ili kuondoa programu, nenda kwenye Mipangilio &gt; Duka la Google Play &gt;Mapendeleo ya Kudhibiti Android &gt; Kidhibiti cha programu. Kisha uguse programu unayotaka kuondoa (huenda utahitaji kutelezesha kidole kulia au kushoto ili kupata programu). Kisha uguse 'Ondoa' au 'Zima'.</translation>
 <translation id="3747220812138541072">Onyesha mapendekezo ya maandishi yanayoonekana unapoandika</translation>
+<translation id="3747603683749989726">Ungependa kuwasha kipengele cha usalama kilichoboreshwa?</translation>
 <translation id="3748706263662799310">Ripoti hitilafu</translation>
 <translation id="3750562496035670393">Chrome imehifadhi nenosiri lako kwenye kifaa hiki, lakini unaweza kulihifadhi kwenye Akaunti yako ya Google badala yake. Ukishafanya hivyo, manenosiri yote yaliyo kwenye Akaunti yako ya Google pia yatapatikana ukiwa umeingia katika akaunti.</translation>
 <translation id="3752253558646317685">Mwambie mtoto wako aendelee kuinua kidole chake ili uhifadhi alama yake ya kidole</translation>
@@ -3203,6 +3210,7 @@
 <translation id="4279129444466079448">Unaweza kuweka hadi wasifu <ph name="PROFILE_LIMIT" /> wa eSIM kwenye kifaa hiki. Ili uweze kuweka wasifu mwingine, ondoa wasifu uliopo kwanza.</translation>
 <translation id="4281844954008187215">Sheria na Masharti</translation>
 <translation id="4282196459431406533">Smart Lock imewashwa</translation>
+<translation id="4284755288573763878">Ungependa kuzima kipengele cha usalama kilichoboreshwa?</translation>
 <translation id="4285418559658561636">Sasisha Nenosiri</translation>
 <translation id="4285498937028063278">Banua</translation>
 <translation id="428565720843367874">Programu ya kingavirusi imeacha kufanya kazi ghafla wakati ilipokuwa inakagua faili hii.</translation>
@@ -4676,6 +4684,7 @@
 <translation id="5865508026715185451"><ph name="APP_NAME" /> itasimamishwa hivi karibuni</translation>
 <translation id="586567932979200359">Unaendesha <ph name="PRODUCT_NAME" /> kutoka kwenye picha yake ya diski. Kuisakinisha kwenye kompyuta yako kunakuruhusu kuendesha bila picha ya diski, na kunahakikisha itasasishwa.</translation>
 <translation id="5865733239029070421">Hutuma kiotomatiki takwimu za matumizi na ripoti za programu kuacha kufanya kazi kwa Google</translation>
+<translation id="5865848066829891215">Matatizo ya mawasiliano ya mtandao</translation>
 <translation id="5867841422488265304">Tafuta au andika anwani ya wavuti</translation>
 <translation id="5869029295770560994">Sawa, Nimeelewa</translation>
 <translation id="5869522115854928033">Manenosiri yaliyohifadhiwa</translation>
@@ -5157,6 +5166,7 @@
 <translation id="6374469231428023295">Jaribu Tena</translation>
 <translation id="6377268785556383139">Imepata tokeo 1 la '<ph name="SEARCH_TEXT" />'</translation>
 <translation id="6380143666419481200">Kubali na uendelee</translation>
+<translation id="6382616130475191723">Vilivyoruhusiwa kusoma na kubadilisha <ph name="SITE_NAME" /></translation>
 <translation id="6382958439467370461">Hakuna njia za mkato ambazo hazitumiki</translation>
 <translation id="638418309848716977">Viungo vinavyoweza kutumika</translation>
 <translation id="6384275966486438344">Badilisha mipangilio yako ya kutafuta iwe: <ph name="SEARCH_HOST" /></translation>
@@ -5969,6 +5979,7 @@
 <translation id="7243784282103630670">Hitilafu imetokea wakati wa kupata toleo jipya la Linux. Tutarejesha metadata tukitumia nakala uliyohifadhi.</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> x <ph name="HEIGHT" /> (Bora)</translation>
 <translation id="7246230585855757313">Weka tena ufunguo wako wa usalama kisha ujaribu tena</translation>
+<translation id="7249197363678284330">Unaweza kubadilisha mipangilio hii katika sehemu ya anwani.</translation>
 <translation id="7250616558727237648">Kifaa unachoshiriki nacho hakijakubali. Tafadhali jaribu tena.</translation>
 <translation id="725109152065019550">Samahani, msimamizi wako amelemaza hifadhi ya nje kwenye akaunti yako.</translation>
 <translation id="7251346854160851420">Mandhari chaguomsingi</translation>
@@ -6991,6 +7002,7 @@
 <translation id="8244514732452879619">Tutafunga kifaa hivi punde</translation>
 <translation id="8246776524656196770">Linda ufunguo wako wa usalama ukitumia PIN (Nambari ya Siri ya Utambulisho)</translation>
 <translation id="8248050856337841185">&amp;Bandika</translation>
+<translation id="8248381369318572865">Kufikia maikrofoni yako na kuchambua matamshi yako</translation>
 <translation id="8248887045858762645">Kidokezo cha Chrome</translation>
 <translation id="8249048954461686687">Folda ya OEM</translation>
 <translation id="8249615410597138718">Tuma kwenye Vifaa Vyako</translation>
@@ -7725,6 +7737,7 @@
 <translation id="9026731007018893674">pakua</translation>
 <translation id="9026852570893462412">Huenda mchakato huu ukachukua dakika kadhaa. Inapakua kompyuta iliyo mbali.</translation>
 <translation id="9027459031423301635">Fungua Kiungo katika Kichupo &amp;Kipya</translation>
+<translation id="9029667986262585240">Umezima Kipengele cha Kuvinjari Salama Kilichoboreshwa katika akaunti yako.</translation>
 <translation id="9030515284705930323">Shirika lako halijaruhusu Duka la Google Play kwa akaunti yako. Wasiliana na msimamizi wako kwa maelezo zaidi.</translation>
 <translation id="9030754204056345429">Haraka sana</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_uz.xtb b/chrome/app/resources/generated_resources_uz.xtb
index 2eda7bc..7c0de04 100644
--- a/chrome/app/resources/generated_resources_uz.xtb
+++ b/chrome/app/resources/generated_resources_uz.xtb
@@ -393,6 +393,7 @@
 <translation id="1408980562518920698">Shaxsiy axborotni boshqarish</translation>
 <translation id="1410197035576869800">Ilova ikonkasi</translation>
 <translation id="1410616244180625362"><ph name="HOST" /> saytiga kamerangizdan foydalanishga ruxsat berish</translation>
+<translation id="1410797069449661718">Birinchi varaq tomonga varaqlash</translation>
 <translation id="1410806973194718079">Qoidalar tekshirilmadi</translation>
 <translation id="1412681350727866021">Qoʻshimcha kengaytmalar</translation>
 <translation id="1414315029670184034">Saytlarga kameradan foydalanishni taqiqlash</translation>
@@ -719,6 +720,7 @@
 <translation id="1725562816265788801">Varaqni aylantirish</translation>
 <translation id="1729533290416704613">Bundan tashqari kengaytma Omniboksda so‘rovlar kiritishda foydalaniladigan birlamchi qidiruv tizimini o‘zgartirib qo‘ydi.</translation>
 <translation id="1730917990259790240"><ph name="BEGIN_PARAGRAPH1" />Ilovalarni olib tashlash uchun Sozlamalar &gt; Google Play Market &gt; Android sozlamalari boshqaruvi &gt; Ilovalar yoki Ilovalar menejeri menyusiga kiring. Kerakli ilovani tanlab (ilovani topish uchun ekranni oʻng yoki chapga suring), “Olib tashlash” yoki “Faolsizlantirish” parametrini bosing.<ph name="END_PARAGRAPH1" /></translation>
+<translation id="1730989807608739928">Oxirgi varaq tomonga varaqlash</translation>
 <translation id="1731911755844941020">So‘rov yuborilmoqda...</translation>
 <translation id="1733383495376208985">Sinxronlangan maʼlumotlarni <ph name="BEGIN_LINK" />kodli ibora bilan shifrlang<ph name="END_LINK" />. Google Pay manzillari va toʻlov usullari esa shifrlanmaydi.</translation>
 <translation id="1734212868489994726">Havorang</translation>
@@ -3113,6 +3115,7 @@
 <translation id="4165942112764990069"><ph name="USER_EMAIL" /> ishonchli tashkilotga tegishli emas. Administratorga murojaat qiling. Administrator boʻlsangiz, tashkilotingizni quyidagi sahifa orqali sozlang: g.co/ChromeEnterpriseAccount</translation>
 <translation id="4167686856635546851">Saytlar odatda video oʻyinlar yoki veb shakllarni aks ettirish maqsadida JavaScript ishlatadi</translation>
 <translation id="4168015872538332605"><ph name="PRIMARY_EMAIL" /> foydalanuvchisining ba`zi sozlamalari multi-kirish (bir nechta hisobdan) vaqtida hisobingizga ta`sir qiladi.</translation>
+<translation id="4168651806173792090"><ph name="NETWORK_NAME" /> (oxirgi raqamlari <ph name="LAST_FOUR_DIGITS" />)</translation>
 <translation id="4169535189173047238">Rad etish</translation>
 <translation id="4170314459383239649">Chiqish vaqtida tozalansin</translation>
 <translation id="417096670996204801">Profilni tanlang</translation>
@@ -3343,6 +3346,7 @@
 <translation id="4432621511648257259">Parol xato kiritildi</translation>
 <translation id="443454694385851356">Eskirgan (himoyasizroq)</translation>
 <translation id="443475966875174318">Mos kelmaydigan ilovalarni yangilash yoki olib tashlash</translation>
+<translation id="4435634292635092844">Telefon kamerasidagi rasmlarni <ph name="DEVICE_TYPE" /> qurilmasida ochish. <ph name="LINK_BEGIN" />Batafsil<ph name="LINK_END" /></translation>
 <translation id="4438043733494739848">Shaffof</translation>
 <translation id="4440097423000553826"><ph name="WEBSITE" /> telefoningizga bildirishnoma yubordi. Shaxsingizni tasdiqlash uchun “<ph name="NOTIFICATIONTITLE" />” bildirishnomasini bosing va qadamlarni bajaring.</translation>
 <translation id="4441124369922430666">Qurilma yoqilganda bu ilova avtomatik ishga tushirilsinmi?</translation>
@@ -4601,6 +4605,7 @@
 <translation id="5789643057113097023">.</translation>
 <translation id="5790085346892983794">Bajarildi</translation>
 <translation id="5790651917470750848">Port allaqachon uzatilmoqda</translation>
+<translation id="5792295754950501287"><ph name="CARD_DESCRIPTION" /> uchun boshqa amallar</translation>
 <translation id="5792728279623964091">Quvvat tugmasini bosing</translation>
 <translation id="5793339252089865437">Yangilanishlarni mobil tarmoq orqali amalga oshirsangiz, ancha pulingiz ketib qoladi.</translation>
 <translation id="5794034487966529952"><ph name="DESK_TITLE" /> ish stolida <ph name="NUM_BROWSERS" /> ta brauzer oynasi ochilgan</translation>
@@ -7009,6 +7014,7 @@
 <translation id="8264024885325823677">Bu sozlama administrator tomonidan boshqariladi.</translation>
 <translation id="8264718194193514834">“<ph name="EXTENSION_NAME" />” kengaytmasi kiosk rejimida ishga tushdi.</translation>
 <translation id="826511437356419340">Umumiy nazar rejimi yoqildi. Orqaga yoki oldinga oʻtkazish uchun suring yoki klaviatura bor boʻlsa, tab tugmasini bosing</translation>
+<translation id="8265671588726449108">{COUNT,plural, =1{Brauzer qayta ishga tushirilganda Inkognito oynasi qayta ochilmaydi}other{Brauzer qayta ishga tushirilganda {COUNT} ta Inkognito oynasi qayta ochilmaydi}}</translation>
 <translation id="8266947622852630193">Barcha matn kiritish usullari</translation>
 <translation id="8267539814046467575">Printer kiritish</translation>
 <translation id="8267961145111171918"><ph name="BEGIN_PARAGRAPH1" />Bunga qurilma va undan qanday foydalanish haqidagi umumiy axborot, masalan, batareya quvvati darajasi, tizim va ilovalardan qanday foydalanishingiz, ishdan chiqish hisobotlari kiradi. Ba’zi to‘plangan ma’lumotlar Android dasturchilar kabi hamkorlarimizga ham o‘z ilovalari va mahsulotlarini yanada yaxshilashga yordam beradi.<ph name="END_PARAGRAPH1" />
diff --git a/chrome/app/resources/generated_resources_vi.xtb b/chrome/app/resources/generated_resources_vi.xtb
index d2ad722..6945e7d 100644
--- a/chrome/app/resources/generated_resources_vi.xtb
+++ b/chrome/app/resources/generated_resources_vi.xtb
@@ -415,6 +415,7 @@
            • Xóa cookie và dữ liệu trang web tạm thời khác
             <ph name="LINE_BREAKS" />
            Dấu trang, lịch sử duyệt web và mật khẩu đã lưu sẽ không bị ảnh hưởng.</translation>
+<translation id="1425233109861512854">Không nhận dạng được mã truy cập</translation>
 <translation id="1426410128494586442">Có</translation>
 <translation id="142655739075382478">Đã chặn <ph name="APP_NAME" /></translation>
 <translation id="1426870617281699524">Hãy nhấp vào Thử lại rồi chấp nhận lời nhắc trên máy tính của bạn</translation>
@@ -582,6 +583,7 @@
 <translation id="1593926297800505364">Lưu phương thức thanh toán</translation>
 <translation id="1595492813686795610">Linux đang nâng cấp</translation>
 <translation id="1596286373007273895">Có sẵn</translation>
+<translation id="1596709061955594992">Bluetooth đang tắt. Để xem các thiết bị có thể ghép đôi, hãy bật Bluetooth.</translation>
 <translation id="1598233202702788831">Quản trị viên của bạn đã tắt bản cập nhật.</translation>
 <translation id="1600857548979126453">Truy cập chương trình phụ trợ trình gỡ lỗi trang</translation>
 <translation id="1601560923496285236">Áp dụng</translation>
@@ -1143,6 +1145,7 @@
 <translation id="2148892889047469596">Truyền thẻ</translation>
 <translation id="2149973817440762519">Chỉnh sửa dấu trang</translation>
 <translation id="2150139952286079145">Tìm kiếm máy in đích</translation>
+<translation id="2150491840563706000">Bạn không có quyền truyền cho màn hình này</translation>
 <translation id="2150661552845026580">Và "<ph name="EXTENSION_NAME" />"?</translation>
 <translation id="2151576029659734873">Chỉ mục thẻ không hợp lệ được nhập.</translation>
 <translation id="2152281589789213846">Thêm máy in vào hồ sơ của bạn</translation>
@@ -2611,6 +2614,7 @@
 <translation id="3654045516529121250">Đọc cài đặt trợ năng của bạn</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{Ứng dụng có quyền truy cập vĩnh viễn vào một tệp.}other{Ứng dụng có quyền truy cập vĩnh viễn vào # tệp.}}</translation>
 <translation id="3658871634334445293">Tăng tốc TrackPoint</translation>
+<translation id="3659929705630080526">Bạn đã nhập sai mã truy cập quá nhiều lần. Hãy thử lại sau</translation>
 <translation id="3660234220361471169">Không đáng tin cậy</translation>
 <translation id="3664511988987167893">Biểu tượng tiện ích</translation>
 <translation id="3665589677786828986">Chrome phát hiện thấy rằng một số cài đặt của bạn đã bị lỗi do một chương trình khác gây ra và đặt lại các cài đặt đó về giá trị mặc định ban đầu.</translation>
@@ -2694,6 +2698,7 @@
 <translation id="3743842571276656710">Nhập mã PIN để ghép nối thiết bị với <ph name="DEVICE_NAME" /></translation>
 <translation id="3747077776423672805">Để xóa ứng dụng, hãy chuyển đến mục Cài đặt &gt; Cửa hàng Google Play &gt; Quản lý tùy chọn Android &gt; Ứng dụng hoặc Trình quản lý ứng dụng. Tiếp theo, hãy nhấn vào ứng dụng mà bạn muốn gỡ cài đặt (bạn có thể phải vuốt sang phải hoặc trái để tìm ứng dụng đó). Sau đó, hãy nhấn vào Gỡ cài đặt hoặc Tắt.</translation>
 <translation id="3747220812138541072">Khi bạn nhập dữ liệu, nội dung gợi ý khi viết sẽ xuất hiện cùng dòng</translation>
+<translation id="3747603683749989726">Bật tính năng bảo mật nâng cao?</translation>
 <translation id="3748706263662799310">Báo cáo lỗi</translation>
 <translation id="3750562496035670393">Chrome đã lưu mật khẩu của bạn trên thiết bị này, nhưng bạn cũng có thể chuyển sang lưu mật khẩu đó vào Tài khoản Google. Sau đó, tất cả mật khẩu trong Tài khoản Google của bạn đều sẽ có sẵn hiện khi bạn đăng nhập.</translation>
 <translation id="3752253558646317685">Yêu cầu con bạn liên tục nhấc ngón tay lên để lưu dấu vân tay</translation>
@@ -3209,6 +3214,7 @@
 <translation id="4279129444466079448">Bạn có thể cài đặt tối đa <ph name="PROFILE_LIMIT" /> hồ sơ eSIM trên thiết bị này. Để thêm một hồ sơ khác, trước tiên, hãy xóa một hồ sơ hiện có.</translation>
 <translation id="4281844954008187215">Điều khoản dịch vụ</translation>
 <translation id="4282196459431406533">Đã bật Smart Lock</translation>
+<translation id="4284755288573763878">Tắt tính năng bảo mật nâng cao?</translation>
 <translation id="4285418559658561636">Cập nhật mật khẩu</translation>
 <translation id="4285498937028063278">Bỏ ghim</translation>
 <translation id="428565720843367874">Phần mềm chống vi-rút bị lỗi bất ngờ khi đang quét tệp này.</translation>
@@ -4682,6 +4688,7 @@
 <translation id="5865508026715185451"><ph name="APP_NAME" /> sắp tạm dừng</translation>
 <translation id="586567932979200359">Bạn đang chạy <ph name="PRODUCT_NAME" /> từ hình ảnh đĩa của nó. Cài đặt trình duyệt này trên máy tính cho phép bạn chạy trình duyệt mà không cần có hình ảnh đĩa và đảm bảo trình duyệt sẽ được cập nhật.</translation>
 <translation id="5865733239029070421">Tự động gửi số liệu thống kê sử dụng và báo cáo sự cố cho Google</translation>
+<translation id="5865848066829891215">Vấn đề về kết nối mạng</translation>
 <translation id="5867841422488265304">Tìm kiếm hoặc nhập địa chỉ web</translation>
 <translation id="5869029295770560994">OK</translation>
 <translation id="5869522115854928033">Mật khẩu đã lưu</translation>
@@ -5975,6 +5982,7 @@
 <translation id="7243784282103630670">Đã xảy ra lỗi khi nâng cấp Linux. Chúng tôi sẽ khôi phục vùng chứa bằng bản sao lưu của bạn.</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> x <ph name="HEIGHT" /> (Tốt nhất)</translation>
 <translation id="7246230585855757313">Cắm lại khóa bảo mật rồi thử lại lần nữa</translation>
+<translation id="7249197363678284330">Thay đổi chế độ cài đặt này trong thanh địa chỉ.</translation>
 <translation id="7250616558727237648">Thiết bị mà bạn đang chia sẻ tệp không phản hồi. Vui lòng thử lại.</translation>
 <translation id="725109152065019550">Rất tiếc, quản trị viên của bạn đã tắt bộ nhớ ngoài trên tài khoản của bạn.</translation>
 <translation id="7251346854160851420">Hình nền mặc định</translation>
@@ -7729,6 +7737,7 @@
 <translation id="9026731007018893674">tải xuống</translation>
 <translation id="9026852570893462412">Quá trình này có thể mất vài phút. Đang tải xuống máy ảo.</translation>
 <translation id="9027459031423301635">Mở Liên kết trong &amp;Tab Mới</translation>
+<translation id="9029667986262585240">Bạn đã tắt tính năng Duyệt web an toàn có tăng cường bảo vệ trong tài khoản.</translation>
 <translation id="9030515284705930323">Tổ chức chưa bật Cửa hàng Google Play cho tài khoản của bạn. Hãy liên hệ với quản trị viên để biết thêm thông tin.</translation>
 <translation id="9030754204056345429">Nhanh hơn</translation>
 <translation id="9030785788945687215">Gmail</translation>
diff --git a/chrome/app/resources/generated_resources_zu.xtb b/chrome/app/resources/generated_resources_zu.xtb
index a69edcd21..c2c362c 100644
--- a/chrome/app/resources/generated_resources_zu.xtb
+++ b/chrome/app/resources/generated_resources_zu.xtb
@@ -416,6 +416,7 @@
            • Susa amakhukhi nenye idatha yesikhashana yesayithi
             <ph name="LINE_BREAKS" />
            Amabhukhimakhi, umlando, namaphasiwedi alondoloziwe ngeke aze athinteke.</translation>
+<translation id="1425233109861512854">Ikhodi yokufinyelela ayaziwa</translation>
 <translation id="1426410128494586442">Yebo</translation>
 <translation id="142655739075382478"><ph name="APP_NAME" /> ivinjiwe</translation>
 <translation id="1426870617281699524">Chofoza ukuzama futhi, uphinde wamukele ukwaziswa kukhompuyutha yakho</translation>
@@ -581,6 +582,7 @@
 <translation id="1593926297800505364">Londoloza indlela yokukhokha</translation>
 <translation id="1595492813686795610">I-Linux iyathuthukiswa</translation>
 <translation id="1596286373007273895">Iyatholakala</translation>
+<translation id="1596709061955594992">I-Bluetooth ivaliwe. Ukuze ubone amadivayisi atholakalayo, vula i-Bluetooth.</translation>
 <translation id="1598233202702788831">Izibuyekezo zikhutshazwe ngumlawuli wakho.</translation>
 <translation id="1600857548979126453">Finyelela ukulandelelwa kwesilungisi senkinga sekhasi</translation>
 <translation id="1601560923496285236">Faka</translation>
@@ -1142,6 +1144,7 @@
 <translation id="2148892889047469596">Ithebhu yokusakaza</translation>
 <translation id="2149973817440762519">Hlela ibhukhimakhi</translation>
 <translation id="2150139952286079145">Sesha izindawo</translation>
+<translation id="2150491840563706000">Awunayo imvume yokusakaza kulesi sibonisi</translation>
 <translation id="2150661552845026580">Engeza i-"<ph name="EXTENSION_NAME" />"?</translation>
 <translation id="2151576029659734873">Kufakwe inkomba yethebhu engavumelekile.</translation>
 <translation id="2152281589789213846">Engeza amaphrinta kuphrofayela yakho</translation>
@@ -2610,6 +2613,7 @@
 <translation id="3654045516529121250">Funda izilungiselelo zokufinyelela kwakho</translation>
 <translation id="3655712721956801464">{NUM_FILES,plural, =1{Inokufinyelela okungunaphakade kufayela elilodwa.}one{Inokufinyelela okungunaphakade kumafayela angu-#.}other{Inokufinyelela okungunaphakade kumafayela angu-#.}}</translation>
 <translation id="3658871634334445293">Isisheshisi se-TrackPoint</translation>
+<translation id="3659929705630080526">Ufake ikhodi yokufinyelela engalungile izikhathi eziningi kakhulu. Zama futhi ngemuva kwesikhathi</translation>
 <translation id="3660234220361471169">Okungathenjiwe</translation>
 <translation id="3664511988987167893">Isithonjana sesandiso</translation>
 <translation id="3665589677786828986">I-Chrome ithole ukuthi ezinye izilungiselelo zakho zonakaliswe olunye uhlelo bese yazisetha kabusha zaba okumisiwe kwasekuqaleni.</translation>
@@ -2693,6 +2697,7 @@
 <translation id="3743842571276656710">Faka Iphinikhodi ukuze ubhangqe ne-<ph name="DEVICE_NAME" /></translation>
 <translation id="3747077776423672805">Ukuze ususe izinhlelo zokusebenza, iya kuzilungiselelo &gt; isiphathi sezinhlelo zokusebenza noma uhlelo lokusebenza. Bese uthephe uhlelo lokusebenza ofuna ukulikhipha (ungadinga ukuswayiphela ngakwesokudla noma ngakwesokunxele ukuze uthole uhlelo lokusebenza). Bes uthephe okuthi Khipha noma Khubaza.</translation>
 <translation id="3747220812138541072">Bonisa iziphakamiso zokubhala emgqeni ezivela njengoba uthayipha</translation>
+<translation id="3747603683749989726">Vula ukuvikeleka okuthuthukisiwe?</translation>
 <translation id="3748706263662799310">Bika isiphazamiso</translation>
 <translation id="3750562496035670393">I-Chrome ilondoloze iphasiwedi yakho kule divayisi, kodwa kunalokho ungayilondoloza ku-Google Account yakho. Futhi, wonke amaphasiwedi ku-Google Account yakho azotholakala futhi lapho ungene ngemvume.</translation>
 <translation id="3752253558646317685">Tshela ingane yakho ukuthi iqhubeke nokuphakamisa umunwe wayo ukulondoloza isigxivizo somunwe</translation>
@@ -3207,6 +3212,7 @@
 <translation id="4279129444466079448">Ungafaka amaphrofayela we-eSIM afika kwangu-<ph name="PROFILE_LIMIT" /> kule divayisi. Ukwengeza elinye iphrofayela, susa kuqala iphrofayela elikhona kakade.</translation>
 <translation id="4281844954008187215">Imigomo yesevisi</translation>
 <translation id="4282196459431406533">I-Smart Lock ivulekile</translation>
+<translation id="4284755288573763878">Vala ukuvikeleka okuthuthukisiwe?</translation>
 <translation id="4285418559658561636">Buyekeza iphasiwedi</translation>
 <translation id="4285498937028063278">Susa ukuphina</translation>
 <translation id="428565720843367874">Isofthiwe yokulwa namagciwane yehluleke ngokungalindelekile ngenkathi iskena leli fayela.</translation>
@@ -4683,6 +4689,7 @@
 <translation id="5865508026715185451">I-<ph name="APP_NAME" /> izophumula maduze</translation>
 <translation id="586567932979200359">Usebenzisa i-<ph name="PRODUCT_NAME" /> kusukela kusithombe sayo sediski. Ukuyifaka kukhompyutha yakho kukuvumela ukuthi uyisebenzise ngaphandle kwesithombe sediski, kuphinde kuqiniseke ukuthi izogcinwa isesikhathini samanje.</translation>
 <translation id="5865733239029070421">Ithumela ngokuzenzakalela izibalo zokusetshenziswa nemibiko yokuphahlazeka ku-Google</translation>
+<translation id="5865848066829891215">Izinkinga zokuxhumana kwenethiwekhi</translation>
 <translation id="5867841422488265304">Sesha noma thayipha ikheli lewebhu</translation>
 <translation id="5869029295770560994">OK, ngiyezwa</translation>
 <translation id="5869522115854928033">Amaphasiwedi alondoloziwe</translation>
@@ -5976,6 +5983,7 @@
 <translation id="7243784282103630670">Kubenephutha ngenkathi kuthuthukiswa i-Linux. Sizolondoloza isiqukathi sisebenzisa isipele sakho.</translation>
 <translation id="7245628041916450754"><ph name="WIDTH" /> x <ph name="HEIGHT" /> (Okuhamba phambili)</translation>
 <translation id="7246230585855757313">Faka kabusha ukhiye wakho wokuqinisekisa ubunikazi bes uyazama futhi</translation>
+<translation id="7249197363678284330">Shintsha leli sethingi kubha yekheli.</translation>
 <translation id="7250616558727237648">Idivayisi owabelana nayo ayisabelanga. Sicela uzame futhi.</translation>
 <translation id="725109152065019550">Uxolo, umlawuli wakho ukhubaze isitoreji sangaphandle ku-akhawunti yakho.</translation>
 <translation id="7251346854160851420">Isithombe sangemuva esizenzakalelayo</translation>
@@ -7730,6 +7738,7 @@
 <translation id="9026731007018893674">landa</translation>
 <translation id="9026852570893462412">Le nqubo ingathatha amaminithi ambalwa. Ilanda umshini obonakalayo.</translation>
 <translation id="9027459031423301635">Vula isixhumanisi kuthebhu entsha</translation>
+<translation id="9029667986262585240">Uvale Ukuphequlula Ngokuphepha Okugqamile ku-akhawunti yakho.</translation>
 <translation id="9030515284705930323">Inhlangano yakho ayizange inike amandla i-Google Play Isitolo se-akhawunti yakho. Xhumana nomlawuli wakho ukuze uthole olunye ulwazi.</translation>
 <translation id="9030754204056345429">Sheshasheshayo</translation>
 <translation id="9030785788945687215">I-Gmail</translation>
diff --git a/chrome/app/resources/google_chrome_strings_ar.xtb b/chrome/app/resources/google_chrome_strings_ar.xtb
index 49e2cd0..00dbc41 100644
--- a/chrome/app/resources/google_chrome_strings_ar.xtb
+++ b/chrome/app/resources/google_chrome_strings_ar.xtb
@@ -306,6 +306,7 @@
 <translation id="8914504000324227558">‏إعادة تشغيل Chrome</translation>
 <translation id="8922193594870374009">‏لإرسال رقم من <ph name="ORIGIN" /> إلى هاتفك الذي يعمل بنظام التشغيل Android، يُرجى تسجيل الدخول إلى متصفِّح Chrome على الجهازَين.</translation>
 <translation id="8927704157641862988">‏يمكنك تخصيص خيارات الخصوصية التي يوليها متصفِّح Chrome الأهمية الأكبر، علمًا بأنّ هذا الدليل لا يتضمّن كل الإعدادات والخيارات.</translation>
+<translation id="8983720963221508955">‏تم تفعيل ميزة "الحماية المُحسّنة للتصفّح الآمن" في حسابك. ويمكنك استخدامها الآن في Chrome.</translation>
 <translation id="8986207147630327271">أنت على وشك إضافة ملف شخصي للعمل إلى هذا المتصفِّح ومنح المشرف إمكانية التحكُّم في هذا الملف الشخصي للعمل فقط.</translation>
 <translation id="8999208279178790196">{0,plural, =0{‏يتوفر تحديث لمتصفح Chrome}=1{‏يتوفر تحديث لمتصفح Chrome}two{‏يتوفر تحديث لمتصفح Chrome منذ يومين}few{‏يتوفر تحديث لمتصفح Chrome منذ # أيام}many{‏يتوفر تحديث لمتصفح Chrome منذ # يومًا}other{‏يتوفر تحديث لمتصفح Chrome منذ # يوم}}</translation>
 <translation id="9026991721384951619">‏تعذر على نظام التشغيل Chrome مزامنة البيانات نظرًا لأن تفاصيل تسجيل الدخول إلى حسابك قديمة.</translation>
diff --git a/chrome/app/resources/google_chrome_strings_bn.xtb b/chrome/app/resources/google_chrome_strings_bn.xtb
index 8af12e8c..a5363f0d 100644
--- a/chrome/app/resources/google_chrome_strings_bn.xtb
+++ b/chrome/app/resources/google_chrome_strings_bn.xtb
@@ -306,6 +306,7 @@
 <translation id="8914504000324227558">Chrome আবার লঞ্চ করুন</translation>
 <translation id="8922193594870374009"><ph name="ORIGIN" /> থেকে আপনার Android ফোনে একটি নম্বর পাঠাতে, উভয় ডিভাইসের Chrome-এ সাইন-ইন করুন।</translation>
 <translation id="8927704157641862988">গোপনীয়তা পছন্দগুলি কাস্টমাইজ করুন যা Chrome সবচেয়ে গুরুত্বপূর্ণ বলে মনে করে। এই গাইড প্রতিটি সেটিং এবং বিকল্প অন্তর্ভুক্ত করে না।</translation>
+<translation id="8983720963221508955">আপনার অ্যাকাউন্টে উন্নত নিরাপদ ব্রাউজিং চালু করেছেন। এখন Chrome-এ চালু করুন।</translation>
 <translation id="8986207147630327271">আপনি এই ব্রাউজারে অফিস প্রোফাইল যোগ করছেন এবং আপনার অ্যাডমিনিস্ট্রেটরকে শুধু অফিস প্রোফাইলটি নিয়ন্ত্রণ করার অধিকার দিচ্ছেন।</translation>
 <translation id="8999208279178790196">{0,plural, =0{Chrome-এর একটি আপডেট উপলভ্য আছে}=1{Chrome-এর একটি আপডেট উপলভ্য আছে}one{Chrome-এর একটি আপডেট # দিন ধরে উপলভ্য আছে}other{Chrome-এর একটি আপডেট # দিন ধরে উপলভ্য আছে}}</translation>
 <translation id="9026991721384951619">আপনার অ্যাকাউন্টের সাইন-ইনের বিবরণটি পুরনো হওয়ায় Chrome OS আপনার ডেটা সিঙ্ক করতে পারেনি৷</translation>
diff --git a/chrome/app/resources/google_chrome_strings_cs.xtb b/chrome/app/resources/google_chrome_strings_cs.xtb
index 2f8f5b4..bffb8b4 100644
--- a/chrome/app/resources/google_chrome_strings_cs.xtb
+++ b/chrome/app/resources/google_chrome_strings_cs.xtb
@@ -309,6 +309,7 @@
 <translation id="8914504000324227558">Znovu spustit Chrome</translation>
 <translation id="8922193594870374009">Chcete-li poslat číslo z webu <ph name="ORIGIN" /> na svůj telefon Android, v obou zařízeních se přihlaste do Chromu.</translation>
 <translation id="8927704157641862988">Přizpůsobte si nastavení ochrany soukromí, která jsou v prohlížeči Chrome nejdůležitější. Tento průvodce nezahrnuje všechna nastavení a možnosti.</translation>
+<translation id="8983720963221508955">Ve svém účtu jste zapnuli Vylepšené Bezpečné prohlížení. Teď ho můžete zapnout v prohlížeči Chrome.</translation>
 <translation id="8986207147630327271">Přidáváte do tohoto prohlížeče pracovní profil a povolujete administrátorovi ovládat pouze pracovní profil.</translation>
 <translation id="8999208279178790196">{0,plural, =0{Je k dispozici aktualizace Chromu}=1{Je k dispozici aktualizace Chromu}few{Již # dny je k dispozici aktualizace Chromu}many{Již # dne je k dispozici aktualizace Chromu}other{Již # dní je k dispozici aktualizace Chromu}}</translation>
 <translation id="9026991721384951619">Chrome OS vaše data nemohl synchronizovat, protože vaše přihlašovací údaje nejsou aktuální.</translation>
diff --git a/chrome/app/resources/google_chrome_strings_fa.xtb b/chrome/app/resources/google_chrome_strings_fa.xtb
index 4f6006a3..a78a07d 100644
--- a/chrome/app/resources/google_chrome_strings_fa.xtb
+++ b/chrome/app/resources/google_chrome_strings_fa.xtb
@@ -302,6 +302,7 @@
 <translation id="8914504000324227558">‏راه‌اندازی مجدد Chrome</translation>
 <translation id="8922193594870374009">‏برای ارسال شماره از <ph name="ORIGIN" /> به تلفن Android خود، در هر دو دستگاه به سیستم Chrome وارد شوید.</translation>
 <translation id="8927704157641862988">‏انتخاب‌های حریم‌خصوصی را که ازنظر Chrome مهم‌ترین هستند سفارشی کنید. این راهنما حاوی همه تنظیمات و گزینه‌ها نیست.</translation>
+<translation id="8983720963221508955">‏«مرور ایمن پیشرفته» را در حسابتان روشن کرده‌اید. اکنون آن را برای Chrome دریافت کنید.</translation>
 <translation id="8986207147630327271">با این کار، نمایه کاری را به این مرورگر اضافه می‌کنید و فقط کنترل نمایه کاری را دراختیار سرپرست قرار می‌دهید.</translation>
 <translation id="8999208279178790196">{0,plural, =0{‏به‌روزرسانی Chrome دردسترس است}=1{‏به‌روزرسانی Chrome دردسترس است}one{‏به‌روزرسانی Chrome از # روز پیش دردسترس است}other{‏به‌روزرسانی Chrome از # روز پیش دردسترس است}}</translation>
 <translation id="9026991721384951619">‏سیستم‌عامل Chrome قادر به همگام‌سازی داده‌هایتان نبود زیرا جزئیات ورود به سیستم حساب شما به‌روز نیست.</translation>
diff --git a/chrome/app/resources/google_chrome_strings_gu.xtb b/chrome/app/resources/google_chrome_strings_gu.xtb
index 5abddad07..fe500b3 100644
--- a/chrome/app/resources/google_chrome_strings_gu.xtb
+++ b/chrome/app/resources/google_chrome_strings_gu.xtb
@@ -310,6 +310,7 @@
 <translation id="8914504000324227558">Chrome ફરીથી લોંચ કરો</translation>
 <translation id="8922193594870374009"><ph name="ORIGIN" />માંથી તમારા Android ફોન પર નંબર મોકલવા માટે, બન્ને ડિવાઇસ પર Chromeમાં સાઇન ઇન કરો.</translation>
 <translation id="8927704157641862988">પ્રાઇવસીની એ પસંદગીઓને કસ્ટમાઇઝ કરો કે જેને Chrome સૌથી મહત્ત્વપૂર્ણ માને છે. આ માર્ગદર્શિકામાં દરેક સેટિંગ અને વિકલ્પ શામેલ નથી.</translation>
+<translation id="8983720963221508955">તમે તમારા એકાઉન્ટમાં Safe Browsingમાં વધારેલી સુરક્ષાની સુવિધા ચાલુ કરી છે. હવે આ સુવિધા Chrome માટે મેળવો.</translation>
 <translation id="8986207147630327271">તમે આ બ્રાઉઝરમાં ઑફિસની પ્રોફાઇલ ઉમેરી રહ્યાં છો અને તમારા વ્યવસ્થાપકને માત્ર ઑફિસની પ્રોફાઇલનો નિયંત્રણ આપી રહ્યાં છો.</translation>
 <translation id="8999208279178790196">{0,plural, =0{એક Chrome અપડેટ ઉપલબ્ધ છે}=1{એક Chrome અપડેટ ઉપલબ્ધ છે}one{Chrome અપડેટ # દિવસ માટે ઉપલબ્ધ છે}other{Chrome અપડેટ # દિવસ માટે ઉપલબ્ધ છે}}</translation>
 <translation id="9026991721384951619">સાઇન ઇન વિગતો જૂની હોવાને કારણે Chrome OS તમારા ડેટાને સિંક કરી શક્યું નથી.</translation>
diff --git a/chrome/app/resources/google_chrome_strings_hy.xtb b/chrome/app/resources/google_chrome_strings_hy.xtb
index 5c5a5b9..a4c3185e 100644
--- a/chrome/app/resources/google_chrome_strings_hy.xtb
+++ b/chrome/app/resources/google_chrome_strings_hy.xtb
@@ -307,6 +307,7 @@
 <translation id="8914504000324227558">Chrome-ի վերագործարկում</translation>
 <translation id="8922193594870374009">Հեռախոսահամարը <ph name="ORIGIN" /> կայքից ձեր Android հեռախոսին ուղարկելու համար երկու սարքերի Chrome դիտարկիչներում մտեք հաշիվ։</translation>
 <translation id="8927704157641862988">Կարգավորեք գաղտնիության ամենակարևոր պարամետրերը Chrome-ում։ Այս ուղեցույցում նշված են ոչ բոլոր կարգավորումները։</translation>
+<translation id="8983720963221508955">Դուք ձեր հաշվում միացրել եք բարելավված Ապահով դիտարկումը։ Այժմ միացրեք այն Chrome-ի համար։</translation>
 <translation id="8986207147630327271">Դուք ավելացնում եք աշխատանքային պրոֆիլ այս դիտարկիչում և միայն դրա վերահսկողությունը տրամադրում ադմինիստրատորին։</translation>
 <translation id="8999208279178790196">{0,plural, =0{Հասանելի է Chrome-ի նոր տարբերակը}=1{Հասանելի է Chrome-ի նոր տարբերակը}one{Chrome-ի նոր տարբերակը # օր է հասանելի է}other{Chrome-ի նոր տարբերակը # օր է հասանելի է}}</translation>
 <translation id="9026991721384951619">Chrome OS-ը չկարողացավ համաժամացնել տվյալները, քանի որ ձեր հաշիվ մուտք գործելու տվյալները հնացած են:</translation>
diff --git a/chrome/app/resources/google_chrome_strings_lo.xtb b/chrome/app/resources/google_chrome_strings_lo.xtb
index f7d990a..f262c4bc2 100644
--- a/chrome/app/resources/google_chrome_strings_lo.xtb
+++ b/chrome/app/resources/google_chrome_strings_lo.xtb
@@ -310,6 +310,7 @@
 <translation id="8914504000324227558">ເປີດໃຊ້ Chrome ຄືນໃໝ່</translation>
 <translation id="8922193594870374009">ເພື່ອສົ່ງເບີຈາກ <ph name="ORIGIN" /> ໃຫ້ໂທລະສັບ Android ຂອງທ່ານ, ກະລຸນາເຂົ້າສູ່ລະບົບ Chrome ຢູ່ໃນທັງສອງອຸປະກອນ.</translation>
 <translation id="8927704157641862988">ປັບແຕ່ງຕົວເລືອກຄວາມເປັນສ່ວນຕົວທີ່ Chrome ພິຈາລະນາວ່າສຳຄັນທີ່ສຸດ. ຂໍ້ແນະນຳນີ້ບໍ່ຮວມການຕັ້ງຄ່າ ແລະ ຕົວເລືອກທັງໝົດ.</translation>
+<translation id="8983720963221508955">ທ່ານເປີດໃຊ້ Safe Browsing ທີ່ປັບປຸງດີຂຶ້ນໃນບັນຊີຂອງທ່ານແລ້ວ. ຕອນນີ້ສາມາດໃຊ້ໄດ້ກັບ Chrome ແລ້ວ.</translation>
 <translation id="8986207147630327271">ທ່ານກຳລັງເພີ່ມໂປຣໄຟລ໌ບ່ອນເຮັດວຽກໃສ່ໂປຣແກຣມທ່ອງເວັບນີ້ ແລະ ໃຫ້ຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານຄວບຄຸມໂປຣໄຟລ໌ບ່ອນເຮັດວຽກດັ່ງກ່າວເທົ່ານັ້ນ.</translation>
 <translation id="8999208279178790196">{0,plural, =0{ມີການອັບເດດ Chrome}=1{ມີການອັບເດດ Chrome}other{ມີການອັບເດດ Chrome ເປັນເວລາ # ມື້ແລ້ວ}}</translation>
 <translation id="9026991721384951619">Chrome OS ບໍ່​ສາ​ມາດຊິງຄ໌ຂໍ້​ມູນ​ຂອງ​ທ່ານໄດ້ ​ເພາະ​ວ່າ​ລາຍລະອຽດການລົງຊື່ເຂົ້າບັນ​ຊີຂອງທ່ານຫຼ້າສະໄໝແລ້ວ.</translation>
diff --git a/chrome/app/resources/google_chrome_strings_mr.xtb b/chrome/app/resources/google_chrome_strings_mr.xtb
index deac5ad..eeba835 100644
--- a/chrome/app/resources/google_chrome_strings_mr.xtb
+++ b/chrome/app/resources/google_chrome_strings_mr.xtb
@@ -308,6 +308,7 @@
 <translation id="8914504000324227558">Chrome रीलाँच करा</translation>
 <translation id="8922193594870374009"><ph name="ORIGIN" /> वरून तुमच्या Android फोनवर नंबर पाठवण्यासाठी, दोन्ही डिव्हाइसवर Chrome मध्ये साइन इन करा.</translation>
 <translation id="8927704157641862988">Chrome ला गोपनीयतेसंबंधित सर्वात महत्त्वाच्या वाटणाऱ्या निवडी कस्टमाइझ करा. या मार्गदर्शकामध्ये प्रत्येक सेटिंग आणि पर्यायाचा समावेश नाही.</translation>
+<translation id="8983720963221508955">तुम्ही तुमच्या खात्यामध्ये वर्धित सुरक्षित ब्राउझिंग सुरू केले. ते आता Chrome साठी मिळवा.</translation>
 <translation id="8986207147630327271">तुम्ही या ब्राउझरवर कार्य प्रोफाइल जोडत आहात आणि तुमच्या अ‍ॅडमिस्ट्रेटरला फक्त कार्य प्रोफाइलवर नियंत्रण देत आहात.</translation>
 <translation id="8999208279178790196">{0,plural, =0{Chrome अपडेट उपलब्ध आहे}=1{Chrome अपडेट उपलब्ध आहे}other{Chrome अपडेट # दिवसांसाठी उपलब्ध आहे}}</translation>
 <translation id="9026991721384951619">तुमचे खाते साइन इन तपशील कालबाह्य झाल्यामुळे Chrome OS तुमचा डेटा संकालित करू शकले नाही.</translation>
diff --git a/chrome/app/resources/google_chrome_strings_ms.xtb b/chrome/app/resources/google_chrome_strings_ms.xtb
index e58abfd..61acc19 100644
--- a/chrome/app/resources/google_chrome_strings_ms.xtb
+++ b/chrome/app/resources/google_chrome_strings_ms.xtb
@@ -302,6 +302,7 @@
 <translation id="8914504000324227558">Lancarkan semula Chrome</translation>
 <translation id="8922193594870374009">Untuk menghantar nombor daripada <ph name="ORIGIN" /> ke telefon Android anda, log masuk ke Chrome pada kedua-dua peranti.</translation>
 <translation id="8927704157641862988">Sesuaikan pilihan privasi yang dianggap paling penting oleh Chrome. Panduan ini tidak merangkumi setiap tetapan dan pilihan.</translation>
+<translation id="8983720963221508955">Anda menghidupkan Penyemakan Imbas Selamat Dipertingkat dalam akaun anda. Sekarang dapatkan ciri ini untuk Chrome.</translation>
 <translation id="8986207147630327271">Anda menambahkan profil kerja pada penyemak imbas ini dan memberi pentadbir anda kawalan terhadap profil kerja sahaja.</translation>
 <translation id="8999208279178790196">{0,plural, =0{Kemas kini Chrome tersedia}=1{Kemas kini Chrome tersedia}other{Kemas kini Chrome telah tersedia selama # hari}}</translation>
 <translation id="9026991721384951619">OS Chrome tidak dapat menyegerakkan data anda kerana butiran log masuk akaun anda sudah lapuk.</translation>
diff --git a/chrome/app/resources/google_chrome_strings_ne.xtb b/chrome/app/resources/google_chrome_strings_ne.xtb
index cf2fcd69..409c431 100644
--- a/chrome/app/resources/google_chrome_strings_ne.xtb
+++ b/chrome/app/resources/google_chrome_strings_ne.xtb
@@ -306,6 +306,7 @@
 <translation id="8914504000324227558">Chrome पुनः सुरु गर्नुहोस्</translation>
 <translation id="8922193594870374009"><ph name="ORIGIN" /> बाट आफ्नो Android फोनमा कुनै नम्बर पठाउन दुवै यन्त्रमा Chrome मा साइन इन गर्नुहोस्।</translation>
 <translation id="8927704157641862988">Chrome ले अत्यन्तै महत्त्वपूर्ण ठान्ने गोपनीयतासम्बन्धी विकल्पहरू कस्टमाइज गर्नुहोस्। यो निर्देशिकामा सबै सेटिङ र विकल्पहरू समावेश गरिएको छैन।</translation>
+<translation id="8983720963221508955">तपाईंले आफ्नो खातामा परिष्कृत Safe Browsing अन गर्नुभयो। अब Chrome मा पनि यो सुविधा अन गर्नुहोस्।</translation>
 <translation id="8986207147630327271">तपाईं यो ब्राउजरमा एउटा कार्य प्रोफाइल हाल्दै हुनुहुन्छ। तपाईंले यसो गर्नुभयो भने तपाईंका एड्मिन केवल उक्त कार्य प्रोफाइल नियन्त्रण गर्न सक्नुहुन्छ।</translation>
 <translation id="8999208279178790196">{0,plural, =0{Chrome को अद्यावधिक उपलब्ध छ}=1{Chrome को अद्यावधिक उपलब्ध छ}other{Chrome को अद्यावधिक # दिनदेखि उपलब्ध छ}}</translation>
 <translation id="9026991721384951619">Chrome OS ले तपाइँको लगत समक्रमण गर्न सकेन किनभने तपाइँको खाता साइन-इन विवरणहरूको मिति सिधिएको छ।</translation>
diff --git a/chrome/app/resources/google_chrome_strings_pa.xtb b/chrome/app/resources/google_chrome_strings_pa.xtb
index bd560b7..422db2a 100644
--- a/chrome/app/resources/google_chrome_strings_pa.xtb
+++ b/chrome/app/resources/google_chrome_strings_pa.xtb
@@ -310,6 +310,7 @@
 <translation id="8914504000324227558">Chrome ਨੂੰ ਮੁੜ-ਲਾਂਚ ਕਰੋ</translation>
 <translation id="8922193594870374009"><ph name="ORIGIN" /> ਤੋਂ ਆਪਣੇ Android ਫ਼ੋਨ 'ਤੇ ਨੰਬਰ ਭੇਜਣ ਲਈ, ਦੋਵੇਂ ਡੀਵਾਈਸਾਂ 'ਤੇ Chrome ਵਿੱਚ ਸਾਈਨ-ਇਨ ਕਰੋ।</translation>
 <translation id="8927704157641862988">ਉਨ੍ਹਾਂ ਪਰਦੇਦਾਰੀ ਵਿਕਲਪਾਂ ਨੂੰ ਵਿਉਂਤਬੱਧ ਕਰੋ ਜਿਨ੍ਹਾਂ ਨੂੰ Chrome ਸਭ ਤੋਂ ਮਹੱਤਵਪੂਰਨ ਸਮਝਦਾ ਹੈ। ਇਸ ਗਾਈਡ ਵਿੱਚ ਹਰ ਸੈਟਿੰਗ ਅਤੇ ਵਿਕਲਪ ਸ਼ਾਮਲ ਨਹੀਂ ਹੈ।</translation>
+<translation id="8983720963221508955">ਤੁਸੀਂ ਆਪਣੇ ਖਾਤੇ ਵਿੱਚ ਵਿਸਤ੍ਰਿਤ ਸੁਰੱਖਿਅਤ ਬ੍ਰਾਊਜ਼ਿੰਗ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਹੈ। ਇਸਨੂੰ ਹੁਣ Chromium ਲਈ ਵੀ ਪ੍ਰਾਪਤ ਕਰੋ।</translation>
 <translation id="8986207147630327271">ਤੁਸੀਂ ਇਸ ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਕੋਈ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਸ਼ਾਮਲ ਕਰ ਰਹੇ ਹੋ ਅਤੇ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸਿਰਫ਼ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ 'ਤੇ ਕੰਟਰੋਲ ਦੇ ਰਹੇ ਹੋ।</translation>
 <translation id="8999208279178790196">{0,plural, =0{Chrome ਅੱਪਡੇਟ ਉਪਲਬਧ ਹੈ}=1{Chrome ਅੱਪਡੇਟ ਉਪਲਬਧ ਹੈ}other{Chrome ਅੱਪਡੇਟ # ਦਿਨਾਂ ਤੋਂ ਉਪਲਬਧ ਹੈ}}</translation>
 <translation id="9026991721384951619">Chrome OS ਤੁਹਾਡਾ ਡਾਟਾ ਸਿੰਕ ਨਹੀਂ ਕਰ ਸਕਿਆ ਕਿਉਂਕਿ ਤੁਹਾਡੇ ਖਾਤਾ ਸਾਈਨ-ਇਨ ਵੇਰਵੇ ਪੁਰਾਣੇ ਹਨ।</translation>
diff --git a/chrome/app/resources/google_chrome_strings_sk.xtb b/chrome/app/resources/google_chrome_strings_sk.xtb
index 4407bb4..74630f871 100644
--- a/chrome/app/resources/google_chrome_strings_sk.xtb
+++ b/chrome/app/resources/google_chrome_strings_sk.xtb
@@ -307,6 +307,7 @@
 <translation id="8914504000324227558">Znova spustiť prehliadač Chrome</translation>
 <translation id="8922193594870374009">Ak chcete zo zariadenia <ph name="ORIGIN" /> odoslať číslo do svojho telefónu s Androidom, prihláste sa v oboch zariadeniach do Chromu.</translation>
 <translation id="8927704157641862988">Prispôsobte si možnosti ochrany súkromia, ktoré Chrome považuje za najdôležitejšie. Tento sprievodca neobsahuje všetky nastavenia a možnosti.</translation>
+<translation id="8983720963221508955">V účte ste zapli Zlepšené bezpečné prehliadanie. Povoľte ho aj pre Chrome.</translation>
 <translation id="8986207147630327271">Do tohto prehliadača pridávate pracovný profil a svojmu správcovi udeľujete kontrolu iba nad ním.</translation>
 <translation id="8999208279178790196">{0,plural, =0{Je k dispozícii aktualizácia Chromu}=1{Je k dispozícii aktualizácia Chromu}few{Aktualizácia Chromu je k dispozícii už # dni}many{Aktualizácia Chromu je k dispozícii už # dňa}other{Aktualizácia Chromu je k dispozícii už # dní}}</translation>
 <translation id="9026991721384951619">Systému OS Chrome sa nepodarilo synchronizovať vaše údaje, pretože vaše prihlasovacie údaje účtu sú zastarané.</translation>
diff --git a/chrome/app/resources/google_chrome_strings_sw.xtb b/chrome/app/resources/google_chrome_strings_sw.xtb
index f2af4a3a..520f014 100644
--- a/chrome/app/resources/google_chrome_strings_sw.xtb
+++ b/chrome/app/resources/google_chrome_strings_sw.xtb
@@ -310,6 +310,7 @@
 <translation id="8914504000324227558">Zindua upya Chrome</translation>
 <translation id="8922193594870374009">Ili uweze kutuma nambari kwa simu yako ya Android kutoka <ph name="ORIGIN" />, ingia katika akaunti kwenye Chrome ukitumia vifaa vyote viwili.</translation>
 <translation id="8927704157641862988">Weka mapendeleo kwenye chaguo za faragha ambazo Chrome inachukulia kuwa ni muhimu zaidi. Mwongozo huu haujumuishi mipangilio na chaguo zote.</translation>
+<translation id="8983720963221508955">Umewasha Kipengele cha Kuvinjari Salama Kilichoboreshwa katika akaunti yako. Sasa kiwashe kwenye Chrome.</translation>
 <translation id="8986207147630327271">Unaongeza wasifu wa kazini kwenye kivinjari hiki na unampa msimamizi wako uwezo wa kudhibiti wasifu huo wa kazini pekee.</translation>
 <translation id="8999208279178790196">{0,plural, =0{Sasisho la Chrome linapatikana}=1{Sasisho la Chrome linapatikana}other{Sasisho la Chrome limekuwepo kwa siku #}}</translation>
 <translation id="9026991721384951619">Mfumo wa Uendeshaji wa Chrome haukuweza kusawazisha data yako kwa sababu maelezo yako ya kuingia katika akaunti yanahitaji kusasishwa.</translation>
diff --git a/chrome/app/resources/google_chrome_strings_vi.xtb b/chrome/app/resources/google_chrome_strings_vi.xtb
index ef40bf6..57b507a 100644
--- a/chrome/app/resources/google_chrome_strings_vi.xtb
+++ b/chrome/app/resources/google_chrome_strings_vi.xtb
@@ -306,6 +306,7 @@
 <translation id="8914504000324227558">Chạy lại Chrome</translation>
 <translation id="8922193594870374009">Để gửi số điện thoại từ <ph name="ORIGIN" /> đến điện thoại Android của bạn, hãy đăng nhập vào Chrome trên cả hai thiết bị.</translation>
 <translation id="8927704157641862988">Tuỳ chỉnh những lựa chọn về quyền riêng tư mà Chrome coi là quan trọng nhất. Hướng dẫn này không bao gồm mọi chế độ cài đặt và tuỳ chọn.</translation>
+<translation id="8983720963221508955">Bạn đã bật tính năng Duyệt web an toàn có tăng cường bảo vệ trong tài khoản. Giờ hãy sử dụng tính năng này trên Chrome.</translation>
 <translation id="8986207147630327271">Bạn đang thêm một hồ sơ công việc vào trình duyệt này và chỉ quản trị viên của bạn mới có quyền kiểm soát hồ sơ công việc này.</translation>
 <translation id="8999208279178790196">{0,plural, =0{Đã có bản cập nhật Chrome}=1{Đã có bản cập nhật Chrome}other{Đã có bản cập nhật Chrome từ # ngày trước}}</translation>
 <translation id="9026991721384951619">Chrome OS không thể đồng bộ hóa dữ liệu của bạn do chi tiết đăng nhập tài khoản của bạn đã lỗi thời.</translation>
diff --git a/chrome/app/resources/google_chrome_strings_zu.xtb b/chrome/app/resources/google_chrome_strings_zu.xtb
index b5304c6..297c249 100644
--- a/chrome/app/resources/google_chrome_strings_zu.xtb
+++ b/chrome/app/resources/google_chrome_strings_zu.xtb
@@ -310,6 +310,7 @@
 <translation id="8914504000324227558">Phinda uqalise i-Chrome</translation>
 <translation id="8922193594870374009">Ukuze uthumele inombolo kusukela ku-<ph name="ORIGIN" /> kufoni yakho ye-Android, ngena ngemvume ku-Chrome kuwo womabili amadivayisi.</translation>
 <translation id="8927704157641862988">Yenza ngendlela oyifisayo izinketho zobumfihlo i-Chrome ezibona zibaluleke kakhulu. Lo mhlahlandlela awubandakanyi wonke amasethingi nenketho.</translation>
+<translation id="8983720963221508955">Uvule Ukuphequlula Ngokuphepha Okugqamile ku-akhawunti yakho. Manje yitholele i-Chrome.</translation>
 <translation id="8986207147630327271">Ungeza iphrofayela yomsebenzi kulesi siphequluli futhi unikeza umlawuli wakho ulawulo olungaphezulu nje kwephrofayela yomsebenzi.</translation>
 <translation id="8999208279178790196">{0,plural, =0{Isibuyekezo se-Chrome siyatholakala}=1{Isibuyekezo se-Chrome siyatholakala}one{Isibuyekezo se-Chrome sitholakale izinsuku ezingu-#}other{Isibuyekezo se-Chrome sitholakale izinsuku ezingu-#}}</translation>
 <translation id="9026991721384951619">I-Chrome OS ayikwazanga ukuvumelanisa idatha yakho ngoba imininingwane yokungena ngemvume kwe-akhawunti yakho ayikho kudethi.</translation>
diff --git a/chrome/app/resources/locale_settings.grd b/chrome/app/resources/locale_settings.grd
index 977121f..0f44e076 100644
--- a/chrome/app/resources/locale_settings.grd
+++ b/chrome/app/resources/locale_settings.grd
@@ -34,7 +34,7 @@
       <output filename="locale_settings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="locale_settings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="locale_settings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="locale_settings_am.pak" type="data_package" lang="am" />
diff --git a/chrome/app/settings_chromium_strings.grdp b/chrome/app/settings_chromium_strings.grdp
index 69843f6..701b251 100644
--- a/chrome/app/settings_chromium_strings.grdp
+++ b/chrome/app/settings_chromium_strings.grdp
@@ -91,7 +91,7 @@
   </if>
 
   <!-- Main Page -->
-  <if expr="chromeos or lacros">
+  <if expr="chromeos_ash or chromeos_lacros">
     <!-- No target="_blank" because OS settings opens its own window. -->
     <message name="IDS_SETTINGS_OS_SETTINGS_BANNER" desc="Banner displayed in browser settings page that links to OS settings.">
       If a setting doesn't show on this page, look in your <ph name="LINK_BEGIN">&lt;a href="$1<ex>https://google.com/</ex>"&gt;</ph>
diff --git a/chrome/app/settings_google_chrome_strings.grdp b/chrome/app/settings_google_chrome_strings.grdp
index 7690b57..69bc64d 100644
--- a/chrome/app/settings_google_chrome_strings.grdp
+++ b/chrome/app/settings_google_chrome_strings.grdp
@@ -92,7 +92,7 @@
   </if>
 
   <!-- Main Page -->
-  <if expr="chromeos or lacros">
+  <if expr="chromeos_ash or chromeos_lacros">
     <!-- No target="_blank" because OS settings opens its own window. -->
     <message name="IDS_SETTINGS_OS_SETTINGS_BANNER" desc="Banner displayed in browser settings page that links to OS settings.">
       If a setting doesn't show on this page, look in your <ph name="LINK_BEGIN">&lt;a href="$1<ex>https://google.com/</ex>"&gt;</ph>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index cb579ea..5c796b05 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -121,7 +121,7 @@
   <message name="IDS_SETTINGS_HOME_BUTTON_DISABLED" desc="Sub label for the Show home button setting when disabled.">
     Disabled
   </message>
-  <if expr="chromeos or lacros">
+  <if expr="chromeos_ash or chromeos_lacros">
     <message name="IDS_SETTINGS_THEMES" desc="Name of the control which allows the user to get a theme for the browser.">
       Browser themes
     </message>
@@ -145,7 +145,7 @@
       Use Classic
     </message>
   </if>
-  <if expr="not is_linux or chromeos or lacros or is_fuchsia">
+  <if expr="not is_linux or chromeos_ash or chromeos_lacros or is_fuchsia">
     <message name="IDS_SETTINGS_RESET_TO_DEFAULT_THEME" desc="Name of the control which resets the browser theme back to the default theme.">
       Reset to default
     </message>
@@ -1287,7 +1287,7 @@
   <message name="IDS_SETTINGS_PRIVACY_MORE" desc="Label on the expansion button to show more privacy settings.">
     More
   </message>
-  <if expr="chromeos or lacros">
+  <if expr="chromeos_ash or chromeos_lacros">
     <message name="IDS_SETTINGS_SECURE_DNS_OPEN_CHROME_OS_SETTINGS_LABEL" desc="Label for the section that allow users to open Secure DNS settings in Chrome OS.">
       Manage secure DNS in Chrome OS settings
     </message>
diff --git a/chrome/app/shared_settings_strings.grdp b/chrome/app/shared_settings_strings.grdp
index 2f74e34..c59a125 100644
--- a/chrome/app/shared_settings_strings.grdp
+++ b/chrome/app/shared_settings_strings.grdp
@@ -391,7 +391,7 @@
   </if>
 
   <!-- Password Prompt Dialog -->
-  <if expr="chromeos or lacros">
+  <if expr="chromeos_ash or chromeos_lacros">
     <message name="IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_TITLE" desc="Title of the password prompt dialog popup.">
       Confirm your password
     </message>
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 7f47399..ef6fa18 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -111,7 +111,7 @@
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_FATAL_ERROR" file="cros/fatal_error.png" />
       </if>
-      <if expr="chromeos or lacros">
+      <if expr="chromeos_ash or chromeos_lacros">
         <structure type="chrome_scaled_image" name="IDR_FILETYPE_AUDIO" file="cros/file_types/audio.png" />
         <structure type="chrome_scaled_image" name="IDR_FILETYPE_GENERIC" file="cros/file_types/generic.png" />
         <structure type="chrome_scaled_image" name="IDR_FILETYPE_IMAGE" file="cros/file_types/image.png" />
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index 6742753..f284ce55 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -43,6 +43,8 @@
     "credit_card.icon",
     "default_touch_favicon.icon",
     "default_touch_favicon_mask.icon",
+    "download_in_progress.icon",
+    "download_toolbar_button.icon",
     "eol.icon",
     "extension_crashed.icon",
     "eye.icon",
diff --git a/chrome/app/vector_icons/download_in_progress.icon b/chrome/app/vector_icons/download_in_progress.icon
new file mode 100644
index 0000000..38f79f9
--- /dev/null
+++ b/chrome/app/vector_icons/download_in_progress.icon
@@ -0,0 +1,15 @@
+// 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.
+
+CANVAS_DIMENSIONS, 8,
+R_MOVE_TO, 6.69f, 4.19f,
+R_LINE_TO, 1.06f, 1.06f,
+R_LINE_TO, -3.75f, 3.75f,
+R_LINE_TO, -3.75f, -3.75f,
+R_LINE_TO, 1.06f, -1.06f,
+R_LINE_TO, 1.94f, 1.94f,
+R_V_LINE_TO, -6.13f,
+R_H_LINE_TO, 1.5f,
+R_V_LINE_TO, 6.13f,
+CLOSE
diff --git a/chrome/app/vector_icons/download_toolbar_button.icon b/chrome/app/vector_icons/download_toolbar_button.icon
new file mode 100644
index 0000000..925181d
--- /dev/null
+++ b/chrome/app/vector_icons/download_toolbar_button.icon
@@ -0,0 +1,29 @@
+// 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.
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 14.5f, 14.5f,
+R_V_LINE_TO, -2.25f,
+H_LINE_TO, 16,
+R_V_LINE_TO, 2.25f,
+R_CUBIC_TO, 0, 0.83f, -0.67f, 1.5f, -1.5f, 1.5f,
+R_H_LINE_TO, -9,
+R_CUBIC_TO, -0.82f, 0, -1.5f, -0.67f, -1.5f, -1.5f,
+R_V_LINE_TO, -2.25f,
+R_H_LINE_TO, 1.5f,
+R_V_LINE_TO, 2.25f,
+R_H_LINE_TO, 9,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 12.69f, 8.19f,
+LINE_TO, 13.75f, 9.25f,
+LINE_TO, 10, 13,
+LINE_TO, 6.25f, 9.25f,
+R_LINE_TO, 1.06f, -1.06f,
+R_LINE_TO, 1.94f, 1.93f,
+V_LINE_TO, 4,
+R_H_LINE_TO, 1.5f,
+R_V_LINE_TO, 6.13f,
+R_LINE_TO, 1.94f, -1.93f,
+CLOSE
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index d12c537..428798af 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -400,6 +400,11 @@
     "domain_reliability/service_factory.h",
     "download/background_download_service_factory.cc",
     "download/background_download_service_factory.h",
+    "download/bubble/download_display.cc",
+    "download/bubble/download_display.h",
+    "download/bubble/download_display_controller.cc",
+    "download/bubble/download_display_controller.h",
+    "download/bubble/download_icon_state.h",
     "download/chrome_download_manager_delegate.cc",
     "download/chrome_download_manager_delegate.h",
     "download/deferred_client_wrapper.cc",
@@ -2043,6 +2048,7 @@
     "//components/captive_portal/core:buildflags",
     "//components/certificate_matching",
     "//components/certificate_transparency",
+    "//components/certificate_transparency:proto",
     "//components/client_hints/browser",
     "//components/cloud_devices/common",
     "//components/component_updater",
@@ -4097,6 +4103,8 @@
       "policy/local_sync_policy_handler.h",
       "policy/managed_account_policy_handler.cc",
       "policy/managed_account_policy_handler.h",
+      "prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper.cc",
+      "prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper.h",
       "privacy_sandbox/generated_floc_pref.cc",
       "privacy_sandbox/generated_floc_pref.h",
       "process_singleton_modal_dialog_lock.cc",
@@ -4578,6 +4586,8 @@
       "apps/app_service/file_utils.h",
       "apps/app_service/menu_util.cc",
       "apps/app_service/menu_util.h",
+      "apps/app_service/metrics/app_platform_input_metrics.cc",
+      "apps/app_service/metrics/app_platform_input_metrics.h",
       "apps/app_service/metrics/app_platform_metrics.cc",
       "apps/app_service/metrics/app_platform_metrics.h",
       "apps/app_service/metrics/app_platform_metrics_service.cc",
@@ -5333,6 +5343,8 @@
       "first_run/first_run_internal_win.cc",
       "first_run/upgrade_util_win.cc",
       "first_run/upgrade_util_win.h",
+      "font_prewarmer_tab_helper.cc",
+      "font_prewarmer_tab_helper.h",
       "fullscreen_win.cc",
       "google/did_run_updater_win.cc",
       "google/did_run_updater_win.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7164b37..ed4ab2c 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -623,16 +623,6 @@
     {"Autoscroll Experiment 2", kLongScreenshot_AutoscrollDragQuick,
      base::size(kLongScreenshot_AutoscrollDragQuick), nullptr}};
 
-const FeatureEntry::FeatureParam kSharingHubLinkToggle_ImageEnabled[] = {
-    {"image_enabled", "true"}};
-const FeatureEntry::FeatureParam kSharingHubLinkToggle_ScreenshotEnabled[] = {
-    {"screenshot_enabled", "true"}};
-const FeatureEntry::FeatureVariation kSharingHubLinkToggleVariations[] = {
-    {"image enabled by default", kSharingHubLinkToggle_ImageEnabled,
-     base::size(kSharingHubLinkToggle_ImageEnabled), nullptr},
-    {"screenshot enabled by default", kSharingHubLinkToggle_ScreenshotEnabled,
-     base::size(kSharingHubLinkToggle_ScreenshotEnabled), nullptr}};
-
 const FeatureEntry::FeatureParam kShowSingleRowMVTiles[] = {
     {"most_visited_max_rows_normal_screen", "1"},
     {"most_visited_max_rows_small_screen", "1"},
@@ -3151,6 +3141,10 @@
     {kLacrosStabilityInternalName, flag_descriptions::kLacrosStabilityName,
      flag_descriptions::kLacrosStabilityDescription, kOsCrOS,
      MULTI_VALUE_TYPE(kLacrosStabilityChoices)},
+    {"lacros-profile-migration-for-any-user",
+     flag_descriptions::kLacrosProfileMigrationForAnyUserName,
+     flag_descriptions::kLacrosProfileMigrationForAnyUserDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kLacrosProfileMigrationForAnyUser)},
     {kLacrosSelectionInternalName, flag_descriptions::kLacrosSelectionName,
      flag_descriptions::kLacrosSelectionDescription, kOsCrOS,
      MULTI_VALUE_TYPE(kLacrosSelectionChoices)},
@@ -3554,11 +3548,6 @@
      flag_descriptions::kPersistShareHubOnAppSwitchName,
      flag_descriptions::kPersistShareHubOnAppSwitchDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(share::kPersistShareHubOnAppSwitch)},
-    {"sharing-hub-link-toggle", flag_descriptions::kSharingHubLinkToggleName,
-     flag_descriptions::kSharingHubLinkToggleDescription, kOsAndroid,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kSharingHubLinkToggle,
-                                    kSharingHubLinkToggleVariations,
-                                    "SharingHubLinkToggle")},
     {"webnotes-publish", flag_descriptions::kWebNotesPublishName,
      flag_descriptions::kWebNotesPublishDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(content_creation::kWebNotesPublish)},
@@ -3738,6 +3727,10 @@
      flag_descriptions::kDesktopPWAsRemoveStatusBarName,
      flag_descriptions::kDesktopPWAsRemoveStatusBarDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kRemoveStatusBarInWebApps)},
+    {"enable-desktop-pwas-default-offline-page",
+     flag_descriptions::kDesktopPWAsDefaultOfflinePageName,
+     flag_descriptions::kDesktopPWAsDefaultOfflinePageDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(features::kDesktopPWAsDefaultOfflinePage)},
     {"enable-desktop-pwas-elided-extensions-menu",
      flag_descriptions::kDesktopPWAsElidedExtensionsMenuName,
      flag_descriptions::kDesktopPWAsElidedExtensionsMenuDescription, kOsDesktop,
@@ -4793,13 +4786,6 @@
      flag_descriptions::kBlockInsecurePrivateNetworkRequestsDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kBlockInsecurePrivateNetworkRequests)},
 
-    {"cross-origin-embedder-policy-credentialless",
-     flag_descriptions::kCrossOriginEmbedderPolicyCredentiallessName,
-     flag_descriptions::kCrossOriginEmbedderPolicyCredentiallessDescription,
-     kOsAll,
-     FEATURE_VALUE_TYPE(
-         network::features::kCrossOriginEmbedderPolicyCredentialless)},
-
     {"disable-keepalive-fetch", flag_descriptions::kDisableKeepaliveFetchName,
      flag_descriptions::kDisableKeepaliveFetchDescription, kOsAll,
      FEATURE_VALUE_TYPE(network::features::kDisableKeepaliveFetch)},
@@ -7747,6 +7733,12 @@
                                     "UseMultipleOverlays")},
 #endif
 
+#if defined(OS_CHROMEOS)
+    {"link-capturing-ui-update", flag_descriptions::kLinkCapturingUiUpdateName,
+     flag_descriptions::kLinkCapturingUiUpdateDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kLinkCapturingUiUpdate)}
+#endif
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/autofill_assistant/starter_android.cc b/chrome/browser/android/autofill_assistant/starter_android.cc
index 9eab4122..42fda252 100644
--- a/chrome/browser/android/autofill_assistant/starter_android.cc
+++ b/chrome/browser/android/autofill_assistant/starter_android.cc
@@ -7,6 +7,7 @@
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
+#include "base/notreached.h"
 #include "base/time/default_tick_clock.h"
 #include "chrome/android/features/autofill_assistant/jni_headers/AssistantOnboardingHelperImpl_jni.h"
 #include "chrome/android/features/autofill_assistant/jni_headers_public/Starter_jni.h"
@@ -213,8 +214,14 @@
 }
 
 bool StarterAndroid::GetMakeSearchesAndBrowsingBetterEnabled() const {
+  if (!java_object_) {
+    // Failsafe, should never happen.
+    NOTREACHED();
+    return false;
+  }
+
   return Java_Starter_getMakeSearchesAndBrowsingBetterSettingEnabled(
-      base::android::AttachCurrentThread());
+      base::android::AttachCurrentThread(), java_object_);
 }
 
 bool StarterAndroid::GetIsCustomTab() const {
@@ -225,7 +232,7 @@
 bool StarterAndroid::GetIsTabCreatedByGSA() const {
   if (!java_object_) {
     // Failsafe, should never happen.
-    DCHECK(false);
+    NOTREACHED();
     return false;
   }
   return Java_Starter_getIsTabCreatedByGSA(base::android::AttachCurrentThread(),
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index be1535e..a63e256 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -33,7 +33,6 @@
 #include "chrome/browser/android/autofill_assistant/client_android.h"
 #include "chrome/browser/android/autofill_assistant/generic_ui_root_controller_android.h"
 #include "chrome/browser/android/autofill_assistant/ui_controller_android_utils.h"
-#include "chrome/browser/android/feedback/screenshot_mode.h"
 #include "chrome/browser/autofill/android/personal_data_manager_android.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -71,7 +70,6 @@
 using ::base::android::JavaRef;
 using ::base::android::ScopedJavaLocalRef;
 using ::base::android::ToJavaArrayOfStrings;
-using ::chrome::android::ScreenshotMode;
 
 namespace autofill_assistant {
 
@@ -546,7 +544,7 @@
   Java_AutofillAssistantUiController_showFeedback(
       env, java_object_,
       ConvertUTF8ToJavaString(env, ui_delegate_->GetDebugContext()),
-      ScreenshotMode::DEFAULT);
+      /* screenshotMode */ 0);
 }
 
 void UiControllerAndroid::OnViewEvent(const EventHandler::EventKey& key) {
@@ -884,7 +882,7 @@
   Java_AutofillAssistantUiController_showFeedback(
       env, java_object_,
       ConvertUTF8ToJavaString(env, ui_delegate_->GetDebugContext()),
-      ScreenshotMode::COMPOSITOR);
+      /* screenshotMode */ 1);
 
   OnUserActionSelected(env, jcaller, index);
 }
diff --git a/chrome/browser/android/bookmarks/partner_bookmarks_shim.cc b/chrome/browser/android/bookmarks/partner_bookmarks_shim.cc
index 87ab298..89a98237 100644
--- a/chrome/browser/android/bookmarks/partner_bookmarks_shim.cc
+++ b/chrome/browser/android/bookmarks/partner_bookmarks_shim.cc
@@ -254,8 +254,7 @@
   if (!prefs_)
     return;
 
-  const base::ListValue* list =
-      prefs_->GetList(prefs::kPartnerBookmarkMappings);
+  const base::Value* list = prefs_->GetList(prefs::kPartnerBookmarkMappings);
   if (!list)
     return;
 
diff --git a/chrome/browser/android/ntp/recent_tabs_page_prefs.cc b/chrome/browser/android/ntp/recent_tabs_page_prefs.cc
index a80dd08..ed38542 100644
--- a/chrome/browser/android/ntp/recent_tabs_page_prefs.cc
+++ b/chrome/browser/android/ntp/recent_tabs_page_prefs.cc
@@ -71,9 +71,9 @@
 jboolean RecentTabsPagePrefs::GetForeignSessionCollapsed(
     JNIEnv* env,
     const JavaParamRef<jstring>& session_tag) {
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       profile_->GetPrefs()->GetDictionary(prefs::kNtpCollapsedForeignSessions);
-  return dict && dict->HasKey(ConvertJavaStringToUTF8(env, session_tag));
+  return dict && dict->FindKey(ConvertJavaStringToUTF8(env, session_tag));
 }
 
 void RecentTabsPagePrefs::SetForeignSessionCollapsed(
diff --git a/chrome/browser/android/search_permissions/search_permissions_service.cc b/chrome/browser/android/search_permissions/search_permissions_service.cc
index a89d0bf..b28548de 100644
--- a/chrome/browser/android/search_permissions/search_permissions_service.cc
+++ b/chrome/browser/android/search_permissions/search_permissions_service.cc
@@ -406,7 +406,7 @@
       // again.
       reset_disclosure = false;
 
-      const base::DictionaryValue* dict =
+      const base::Value* dict =
           pref_service_->GetDictionary(prefs::kDSEGeolocationSettingDeprecated);
 
       // If the user's content setting is being overridden by the DSE setting,
@@ -468,8 +468,8 @@
 }
 
 SearchPermissionsService::PrefValue SearchPermissionsService::GetDSEPref() {
-  const base::DictionaryValue* dict =
-      pref_service_->GetDictionary(prefs::kDSEPermissionsSettings);
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *pref_service_->GetDictionary(prefs::kDSEPermissionsSettings));
 
   PrefValue pref;
   std::u16string dse_name;
diff --git a/chrome/browser/apps/app_service/intent_util.cc b/chrome/browser/apps/app_service/intent_util.cc
index 734d788..611c4e6e 100644
--- a/chrome/browser/apps/app_service/intent_util.cc
+++ b/chrome/browser/apps/app_service/intent_util.cc
@@ -30,6 +30,8 @@
 #include "url/gurl.h"
 
 #if defined(OS_CHROMEOS)
+#include "base/files/file_path.h"
+#include "base/files/safe_base_name.h"
 #include "chromeos/crosapi/mojom/app_service_types.mojom.h"
 #endif
 
@@ -38,7 +40,6 @@
 #include "ash/components/arc/mojom/intent_helper.mojom.h"
 #include "ash/constants/ash_features.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
-#include "base/files/file_path.h"
 #include "chrome/browser/ui/app_list/arc/intent.h"
 #include "chrome/browser/web_applications/web_app_id.h"
 #include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
@@ -809,23 +810,29 @@
   if (crosapi_intent->share_title.has_value()) {
     app_service_intent->share_title = crosapi_intent->share_title.value();
   }
-#if BUILDFLAG(IS_CHROMEOS_ASH)
   if (crosapi_intent->files.has_value() && profile) {
     std::vector<apps::mojom::IntentFilePtr> intent_files;
     for (const auto& file : crosapi_intent->files.value()) {
+      auto intent_file = apps::mojom::IntentFile::New();
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
       auto file_url = apps::GetFileSystemUrl(profile, file->file_path);
       if (file_url.is_empty()) {
         continue;
       }
-      auto intent_file = apps::mojom::IntentFile::New();
       intent_file->url = file_url;
+#else
+      // The directory is omitted from the human readable file name.
+      intent_file->file_name =
+          base::SafeBaseName::Create(file->file_path.BaseName());
+#endif
+
       intent_files.push_back(std::move(intent_file));
     }
     if (intent_files.size() > 0) {
       app_service_intent->files = std::move(intent_files);
     }
   }
-#endif
   if (crosapi_intent->activity_name.has_value()) {
     app_service_intent->activity_name = crosapi_intent->activity_name.value();
   }
diff --git a/chrome/browser/apps/app_service/launch_utils.cc b/chrome/browser/apps/app_service/launch_utils.cc
index 15dcedb..a71f6b67 100644
--- a/chrome/browser/apps/app_service/launch_utils.cc
+++ b/chrome/browser/apps/app_service/launch_utils.cc
@@ -404,7 +404,7 @@
 
   if (crosapi_params->intent->files.has_value()) {
     for (const auto& file : crosapi_params->intent->files.value()) {
-      params.launch_files.push_back(std::move(file->file_path));
+      params.launch_files.push_back(file->file_path);
     }
   }
 
diff --git a/chrome/browser/apps/app_service/launch_utils_unittest.cc b/chrome/browser/apps/app_service/launch_utils_unittest.cc
index a8d57f38..9481ecae 100644
--- a/chrome/browser/apps/app_service/launch_utils_unittest.cc
+++ b/chrome/browser/apps/app_service/launch_utils_unittest.cc
@@ -285,4 +285,53 @@
   EXPECT_EQ(apps::mojom::LaunchSource::kFromIntentUrl,
             converted_params.launch_source);
 }
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+TEST_F(LaunchUtilsTest, FromCrosapiIntent) {
+  constexpr char kIntentMimeType[] = "image/*";
+  constexpr char kShareText[] = "Message";
+  constexpr char kFilePath[] = "/tmp/picture.png";
+  constexpr char kBaseName[] = "picture.png";
+
+  crosapi::mojom::LaunchParamsPtr crosapi_params =
+      crosapi::mojom::LaunchParams::New();
+  crosapi_params->container =
+      crosapi::mojom::LaunchContainer::kLaunchContainerWindow;
+  crosapi_params->disposition =
+      crosapi::mojom::WindowOpenDisposition::kNewForegroundTab;
+  crosapi_params->launch_source = apps::mojom::LaunchSource::kFromSharesheet;
+  crosapi_params->intent = crosapi::mojom::Intent::New();
+  crosapi_params->intent->action = apps_util::kIntentActionSend;
+  crosapi_params->intent->mime_type = kIntentMimeType;
+  crosapi_params->intent->share_text = kShareText;
+  {
+    std::vector<crosapi::mojom::IntentFilePtr> crosapi_files;
+    auto crosapi_file = crosapi::mojom::IntentFile::New();
+    crosapi_file->file_path = base::FilePath(kFilePath);
+    crosapi_files.push_back(std::move(crosapi_file));
+    crosapi_params->intent->files = std::move(crosapi_files);
+  }
+
+  auto converted_params =
+      apps::ConvertCrosapiToLaunchParams(crosapi_params, &profile_);
+
+  EXPECT_EQ(converted_params.container,
+            apps::mojom::LaunchContainer::kLaunchContainerWindow);
+  EXPECT_EQ(converted_params.disposition,
+            WindowOpenDisposition::NEW_FOREGROUND_TAB);
+  EXPECT_EQ(converted_params.launch_source,
+            apps::mojom::LaunchSource::kFromSharesheet);
+
+  EXPECT_EQ(converted_params.launch_files.size(), 1U);
+  EXPECT_EQ(converted_params.launch_files[0], base::FilePath(kFilePath));
+
+  EXPECT_EQ(converted_params.intent->action, apps_util::kIntentActionSend);
+  EXPECT_EQ(converted_params.intent->mime_type, kIntentMimeType);
+  EXPECT_EQ(converted_params.intent->share_text, kShareText);
+  EXPECT_EQ(converted_params.intent->files->size(), 1U);
+  EXPECT_EQ((*converted_params.intent->files)[0]->file_name,
+            base::SafeBaseName::Create(kBaseName));
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
 #endif  // defined(OS_CHROMEOS)
diff --git a/chrome/browser/apps/app_service/metrics/DEPS b/chrome/browser/apps/app_service/metrics/DEPS
new file mode 100644
index 0000000..64e2821
--- /dev/null
+++ b/chrome/browser/apps/app_service/metrics/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "app_platform_input_metrics\.cc": [
+    "+ash/shell.h",
+  ],
+}
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.cc b/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.cc
new file mode 100644
index 0000000..258122e
--- /dev/null
+++ b/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.cc
@@ -0,0 +1,157 @@
+// 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 "chrome/browser/apps/app_service/metrics/app_platform_input_metrics.h"
+
+#include "ash/shell.h"
+#include "base/metrics/histogram_macros.h"
+#include "chrome/browser/apps/app_service/web_contents_app_id_utils.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "components/services/app_service/public/cpp/instance_update.h"
+#include "components/services/app_service/public/cpp/types_util.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/common/constants.h"
+#include "ui/aura/window.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/types/event_type.h"
+#include "ui/views/widget/widget.h"
+
+namespace apps {
+
+namespace {
+
+InputEventSource GetInputEventSource(ui::EventPointerType type) {
+  switch (type) {
+    case ui::EventPointerType::kUnknown:
+      return InputEventSource::kUnknown;
+    case ui::EventPointerType::kMouse:
+      return InputEventSource::kMouse;
+    case ui::EventPointerType::kPen:
+      return InputEventSource::kStylus;
+    case ui::EventPointerType::kTouch:
+      return InputEventSource::kTouch;
+    case ui::EventPointerType::kEraser:
+      return InputEventSource::kStylus;
+  }
+}
+
+// Returns the app id for the active tab in the browser window `window`. If
+// there is no app for the active tab, returns the Chrome browser app id.
+std::string GetActiveAppIdForBrowserWindow(aura::Window* window) {
+  auto* browser = chrome::FindBrowserWithWindow(window);
+  if (!browser || !browser->tab_strip_model()) {
+    return extension_misc::kChromeAppId;
+  }
+
+  auto* web_contents = browser->tab_strip_model()->GetActiveWebContents();
+  if (!web_contents) {
+    return extension_misc::kChromeAppId;
+  }
+  auto app_id = GetAppIdForWebContents(web_contents);
+  return app_id.empty() ? extension_misc::kChromeAppId : app_id;
+}
+
+}  // namespace
+
+AppPlatformInputMetrics::AppPlatformInputMetrics(
+    Profile* profile,
+    InstanceRegistry& instance_registry)
+    : profile_(profile) {
+  InstanceRegistry::Observer::Observe(&instance_registry);
+  // Check whether ash::Shell exists before calling it to avoid crash, and keep
+  // consistent implementation with the destructor function.
+  if (ash::Shell::HasInstance()) {
+    ash::Shell::Get()->AddPreTargetHandler(this);
+  }
+}
+
+AppPlatformInputMetrics::~AppPlatformInputMetrics() {
+  // ash::Shell might be destroyed before AppPlatformInputMetrics's destroy, so
+  // check whether ash::Shell exists before calling it to avoid crash.
+  if (ash::Shell::HasInstance()) {
+    ash::Shell::Get()->RemovePreTargetHandler(this);
+  }
+}
+
+void AppPlatformInputMetrics::OnMouseEvent(ui::MouseEvent* event) {
+  if (event->type() == ui::ET_MOUSE_RELEASED) {
+    RecordEventCount(GetInputEventSource(event->pointer_details().pointer_type),
+                     event->target());
+  }
+}
+
+void AppPlatformInputMetrics::OnKeyEvent(ui::KeyEvent* event) {
+  if (event->type() == ui::ET_KEY_RELEASED) {
+    RecordEventCount(InputEventSource::kKeyboard, event->target());
+  }
+}
+
+void AppPlatformInputMetrics::OnTouchEvent(ui::TouchEvent* event) {
+  if (event->type() == ui::ET_TOUCH_RELEASED) {
+    RecordEventCount(GetInputEventSource(event->pointer_details().pointer_type),
+                     event->target());
+  }
+}
+
+void AppPlatformInputMetrics::OnInstanceUpdate(const InstanceUpdate& update) {
+  if (!update.StateChanged()) {
+    return;
+  }
+
+  if (update.State() & InstanceState::kDestroyed) {
+    window_to_app_info_.erase(update.Window());
+    return;
+  }
+
+  aura::Window* window = update.Window();
+  auto app_id = update.AppId();
+  AppTypeName app_type_name = GetAppTypeNameForWindow(
+      profile_, GetAppType(profile_, app_id), app_id, window);
+  if (app_type_name == AppTypeName::kUnknown) {
+    return;
+  }
+
+  if (IsAppOpenedInTab(app_type_name, app_id)) {
+    window = window->GetToplevelWindow();
+    app_id = extension_misc::kChromeAppId;
+  }
+
+  if (app_id == extension_misc::kChromeAppId) {
+    app_id = GetActiveAppIdForBrowserWindow(window);
+  }
+
+  window_to_app_info_[window].app_id = app_id;
+  window_to_app_info_[window].app_type_name = app_type_name;
+}
+
+void AppPlatformInputMetrics::OnInstanceRegistryWillBeDestroyed(
+    InstanceRegistry* cache) {
+  InstanceRegistry::Observer::Observe(nullptr);
+}
+
+void AppPlatformInputMetrics::RecordEventCount(InputEventSource event_source,
+                                               ui::EventTarget* event_target) {
+  views::Widget* target = views::Widget::GetTopLevelWidgetForNativeView(
+      static_cast<aura::Window*>(event_target));
+  if (!target) {
+    return;
+  }
+
+  aura::Window* top_window = target->GetNativeWindow();
+  if (!top_window) {
+    return;
+  }
+
+  auto it = window_to_app_info_.find(top_window);
+  if (it == window_to_app_info_.end()) {
+    return;
+  }
+
+  ++app_id_to_event_count_per_five_minutes_[it->second.app_id][event_source]
+                                           [it->second.app_type_name];
+}
+
+}  // namespace apps
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.h b/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.h
new file mode 100644
index 0000000..7a0bee15
--- /dev/null
+++ b/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.h
@@ -0,0 +1,99 @@
+// 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 CHROME_BROWSER_APPS_APP_SERVICE_METRICS_APP_PLATFORM_INPUT_METRICS_H_
+#define CHROME_BROWSER_APPS_APP_SERVICE_METRICS_APP_PLATFORM_INPUT_METRICS_H_
+
+#include <map>
+
+#include "base/containers/flat_map.h"
+#include "chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/services/app_service/public/cpp/instance_registry.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "ui/events/event_handler.h"
+
+namespace apps {
+
+class InstanceUpdate;
+
+// This is used for logging, so do not remove or reorder existing entries.
+enum class InputEventSource {
+  kUnknown = 0,
+  kMouse = 1,
+  kStylus = 2,
+  kTouch = 3,
+  kKeyboard = 4,
+
+  // Add any new values above this one, and update kMaxValue to the highest
+  // enumerator value.
+  kMaxValue = kKeyboard,
+};
+
+// This class is used to record the input events for the app windows.
+class AppPlatformInputMetrics : public ui::EventHandler,
+                                public InstanceRegistry::Observer {
+ public:
+  AppPlatformInputMetrics(Profile* profile,
+                          InstanceRegistry& instance_registry);
+
+  AppPlatformInputMetrics(const AppPlatformInputMetrics&) = delete;
+  AppPlatformInputMetrics& operator=(const AppPlatformInputMetrics&) = delete;
+
+  ~AppPlatformInputMetrics() override;
+
+  // ui::EventHandler:
+  void OnMouseEvent(ui::MouseEvent* event) override;
+  void OnKeyEvent(ui::KeyEvent* event) override;
+  void OnTouchEvent(ui::TouchEvent* event) override;
+
+ private:
+  struct AppInfo {
+    std::string app_id;
+    AppTypeName app_type_name;
+  };
+
+  // For web apps and Chrome apps, there might be different app type name for
+  // opening in tab or window. So record the app type name for the event count.
+  using CountPerAppType = base::flat_map<AppTypeName, int>;
+
+  // The map to record the event count for each InputEventSource.
+  using EventSourceToCounts = base::flat_map<InputEventSource, CountPerAppType>;
+
+  // InstanceRegistry::Observer:
+  void OnInstanceUpdate(const InstanceUpdate& update) override;
+  void OnInstanceRegistryWillBeDestroyed(InstanceRegistry* cache) override;
+
+  void RecordEventCount(InputEventSource event_source,
+                        ui::EventTarget* event_target);
+
+  ukm::SourceId GetSourceId(const std::string app_id);
+
+  Profile* profile_;
+
+  // The map from the window to the app info.
+  base::flat_map<aura::Window*, AppInfo> window_to_app_info_;
+
+  // Records the input event count for each app id in the past five minutes.
+  // Each app id might have multiple events. For web apps and Chrome apps, there
+  // might be different app type name, e.g. Chrome browser for apps opening in
+  // a tab, or Web app for apps opening in a window. For example:
+  // web_app_id1: {
+  //   mouse:    { Chrome browser: 5, Web app: 2}
+  //   Keyboard: { Chrome browser: 2, Web app: 3}
+  // },
+  // chrome_app_id2: {
+  //   stylus:   { Chrome browser: 2, Chrome app: 12}
+  //   Keyboard: { Chrome browser: 3, Chrome app: 30}
+  // },
+  // Arc_app_id3: {
+  //   mouse:   { Arc: 5}
+  // },
+  std::map<std::string, EventSourceToCounts>
+      app_id_to_event_count_per_five_minutes_;
+};
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_APP_SERVICE_METRICS_APP_PLATFORM_INPUT_METRICS_H_
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.cc
index ff20a78..f170621 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.cc
@@ -53,6 +53,8 @@
     InstanceRegistry& instance_registry) {
   app_platform_app_metrics_ = std::make_unique<apps::AppPlatformMetrics>(
       profile_, app_registry_cache, instance_registry);
+  app_platform_input_metrics_ = std::make_unique<apps::AppPlatformInputMetrics>(
+      profile_, instance_registry);
 
   day_id_ = profile_->GetPrefs()->GetInteger(kAppPlatformMetricsDayId);
   CheckForNewDay();
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.h b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.h
index 52921a5..0a8de9c8 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.h
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.h
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/timer/timer.h"
+#include "chrome/browser/apps/app_service/metrics/app_platform_input_metrics.h"
 #include "chrome/browser/apps/app_service/metrics/app_platform_metrics.h"
 #include "chrome/browser/profiles/profile.h"
 
@@ -61,6 +62,7 @@
   base::RepeatingTimer five_minutes_timer_;
 
   std::unique_ptr<apps::AppPlatformMetrics> app_platform_app_metrics_;
+  std::unique_ptr<apps::AppPlatformInputMetrics> app_platform_input_metrics_;
 };
 
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
index cf05ae81..60389258f 100644
--- a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
+++ b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
@@ -687,7 +687,8 @@
   }
 
   const base::ListValue* disabled_system_features_pref =
-      local_state->GetList(policy::policy_prefs::kSystemFeaturesDisableList);
+      &base::Value::AsListValue(*local_state->GetList(
+          policy::policy_prefs::kSystemFeaturesDisableList));
   if (!disabled_system_features_pref) {
     return;
   }
diff --git a/chrome/browser/ash/accessibility/accessibility_manager.cc b/chrome/browser/ash/accessibility/accessibility_manager.cc
index dfcf6fb..757e0a5 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager.cc
+++ b/chrome/browser/ash/accessibility/accessibility_manager.cc
@@ -201,9 +201,8 @@
 absl::optional<bool> GetDictationOfflineNudgePrefForLocale(
     Profile* profile,
     const std::string& dictation_locale) {
-  const base::DictionaryValue* offline_nudges =
-      profile->GetPrefs()->GetDictionary(
-          prefs::kAccessibilityDictationLocaleOfflineNudge);
+  const base::Value* offline_nudges = profile->GetPrefs()->GetDictionary(
+      prefs::kAccessibilityDictationLocaleOfflineNudge);
   return offline_nudges->FindBoolPath(dictation_locale);
 }
 
diff --git a/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc b/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc
index 8175e78..999f1c4 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc
+++ b/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc
@@ -299,9 +299,8 @@
 }
 
 absl::optional<bool> GetDictationOfflineNudgePref(const std::string& locale) {
-  const base::DictionaryValue* offline_nudges =
-      GetActiveUserPrefs()->GetDictionary(
-          prefs::kAccessibilityDictationLocaleOfflineNudge);
+  const base::Value* offline_nudges = GetActiveUserPrefs()->GetDictionary(
+      prefs::kAccessibilityDictationLocaleOfflineNudge);
   return offline_nudges->FindBoolPath(locale);
 }
 
diff --git a/chrome/browser/ash/account_manager/account_apps_availability.cc b/chrome/browser/ash/account_manager/account_apps_availability.cc
index 93795ea..1248190 100644
--- a/chrome/browser/ash/account_manager/account_apps_availability.cc
+++ b/chrome/browser/ash/account_manager/account_apps_availability.cc
@@ -50,7 +50,7 @@
 }
 
 bool IsPrefInitialized(PrefService* prefs) {
-  const base::DictionaryValue* accounts =
+  const base::Value* accounts =
       prefs->GetDictionary(account_manager::prefs::kAccountAppsAvailability);
   return accounts && (accounts->DictSize() > 0 || IsActiveDirectoryUser());
 }
@@ -96,7 +96,7 @@
 
 base::flat_set<std::string> GetGaiaIdsAvailableInArc(PrefService* prefs) {
   base::flat_set<std::string> result;
-  const base::DictionaryValue* accounts =
+  const base::Value* accounts =
       prefs->GetDictionary(account_manager::prefs::kAccountAppsAvailability);
   if (!accounts) {
     LOG(ERROR) << "Couldn't find "
@@ -123,7 +123,7 @@
 // `SetIsAccountAvailableInArc` wasn't called for this account yet).
 absl::optional<bool> IsAccountAvailableInArc(PrefService* prefs,
                                              const std::string& gaia_id) {
-  const base::DictionaryValue* accounts =
+  const base::Value* accounts =
       prefs->GetDictionary(account_manager::prefs::kAccountAppsAvailability);
   if (!accounts) {
     LOG(ERROR) << "Couldn't find "
diff --git a/chrome/browser/ash/app_mode/arc/arc_kiosk_app_data.cc b/chrome/browser/ash/app_mode/arc/arc_kiosk_app_data.cc
index 8931202..e1c37cb 100644
--- a/chrome/browser/ash/app_mode/arc/arc_kiosk_app_data.cc
+++ b/chrome/browser/ash/app_mode/arc/arc_kiosk_app_data.cc
@@ -41,8 +41,8 @@
 
 bool ArcKioskAppData::LoadFromCache() {
   PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* dict =
-      local_state->GetDictionary(dictionary_name());
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *local_state->GetDictionary(dictionary_name()));
 
   return LoadFromDictionary(*dict);
 }
diff --git a/chrome/browser/ash/app_mode/kiosk_app_data.cc b/chrome/browser/ash/app_mode/kiosk_app_data.cc
index 0a2e00199..19d28e3 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_data.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_data.cc
@@ -374,8 +374,8 @@
 
 bool KioskAppData::LoadFromCache() {
   PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* dict =
-      local_state->GetDictionary(dictionary_name());
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *local_state->GetDictionary(dictionary_name()));
 
   if (!LoadFromDictionary(*dict))
     return false;
diff --git a/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc b/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc
index e1775176..b022f9a 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc
@@ -100,8 +100,8 @@
     return s_last_error;
   s_last_error = Error::kNone;
   PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* dict =
-      local_state->GetDictionary(KioskAppManager::kKioskDictionaryName);
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *local_state->GetDictionary(KioskAppManager::kKioskDictionaryName));
 
   int error;
   if (dict->GetInteger(kKeyLaunchError, &error)) {
diff --git a/chrome/browser/ash/app_mode/kiosk_app_launch_error.h b/chrome/browser/ash/app_mode/kiosk_app_launch_error.h
index 8fa88a5c..a208470e 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_launch_error.h
+++ b/chrome/browser/ash/app_mode/kiosk_app_launch_error.h
@@ -7,12 +7,10 @@
 
 #include <string>
 
-// TODO(https://crbug.com/1164001): forward declare AuthFailure when migrated
-// to ash/components/.
-#include "ash/components/login/auth/auth_status_consumer.h"
-
 namespace ash {
 
+class AuthFailure;
+
 class KioskAppLaunchError {
  public:
   // Enum used for UMA. Do NOT reorder or remove entry. Don't forget to
diff --git a/chrome/browser/ash/app_mode/kiosk_app_launch_error_unittest.cc b/chrome/browser/ash/app_mode/kiosk_app_launch_error_unittest.cc
index b51b3a4..50ffe2a3 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_launch_error_unittest.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_launch_error_unittest.cc
@@ -16,7 +16,7 @@
 
 namespace ash {
 
-using ::chromeos::AuthFailure;
+using ::ash::AuthFailure;
 using ::chromeos::KioskAppLaunchError;
 
 namespace {
@@ -29,8 +29,9 @@
 
 // Get Kiosk dictionary value. It is replaced after each update.
 const base::DictionaryValue* GetKioskDictionary() {
-  return g_browser_process->local_state()->GetDictionary(
-      KioskAppManager::kKioskDictionaryName);
+  return &base::Value::AsDictionaryValue(
+      *g_browser_process->local_state()->GetDictionary(
+          KioskAppManager::kKioskDictionaryName));
 }
 
 }  // namespace
diff --git a/chrome/browser/ash/app_mode/kiosk_app_manager.cc b/chrome/browser/ash/app_mode/kiosk_app_manager.cc
index df0d856..9380e61 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_manager.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_manager.cc
@@ -837,8 +837,8 @@
 
 KioskAppManager::AutoLoginState KioskAppManager::GetAutoLoginState() const {
   PrefService* prefs = g_browser_process->local_state();
-  const base::DictionaryValue* dict =
-      prefs->GetDictionary(KioskAppManager::kKioskDictionaryName);
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *prefs->GetDictionary(KioskAppManager::kKioskDictionaryName));
   int value;
   if (!dict->GetInteger(kKeyAutoLoginState, &value))
     return AutoLoginState::kNone;
diff --git a/chrome/browser/ash/app_mode/kiosk_app_manager_browsertest.cc b/chrome/browser/ash/app_mode/kiosk_app_manager_browsertest.cc
index 0400084..cc9d65a7 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_manager_browsertest.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_manager_browsertest.cc
@@ -375,8 +375,8 @@
 
     // Check data is cached in local state correctly.
     PrefService* local_state = g_browser_process->local_state();
-    const base::DictionaryValue* dict =
-        local_state->GetDictionary(KioskAppManager::kKioskDictionaryName);
+    const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+        *local_state->GetDictionary(KioskAppManager::kKioskDictionaryName));
 
     std::string name;
     const std::string name_key = "apps." + app_id + ".name";
@@ -548,8 +548,8 @@
   SetExistingApp("app_1", "Cached App1 Name", "red16x16.png", "");
 
   PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* dict =
-      local_state->GetDictionary(KioskAppManager::kKioskDictionaryName);
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *local_state->GetDictionary(KioskAppManager::kKioskDictionaryName));
   const base::DictionaryValue* apps_dict;
   EXPECT_TRUE(dict->GetDictionary(KioskAppDataBase::kKeyApps, &apps_dict));
   EXPECT_TRUE(apps_dict->FindKey("app_1") != nullptr);
diff --git a/chrome/browser/ash/app_mode/kiosk_cryptohome_remover.cc b/chrome/browser/ash/app_mode/kiosk_cryptohome_remover.cc
index 17f6981..f1d1399f 100644
--- a/chrome/browser/ash/app_mode/kiosk_cryptohome_remover.cc
+++ b/chrome/browser/ash/app_mode/kiosk_cryptohome_remover.cc
@@ -70,7 +70,7 @@
   }
 
   PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* const dict =
+  const base::Value* const dict =
       local_state->GetDictionary(prefs::kAllKioskUsersToRemove);
   for (const auto it : dict->DictItems()) {
     std::string app_id;
diff --git a/chrome/browser/ash/app_mode/kiosk_profile_loader.h b/chrome/browser/ash/app_mode/kiosk_profile_loader.h
index 242162d..38c7f81 100644
--- a/chrome/browser/ash/app_mode/kiosk_profile_loader.h
+++ b/chrome/browser/ash/app_mode/kiosk_profile_loader.h
@@ -9,8 +9,6 @@
 #include <string>
 
 #include "ash/components/login/auth/login_performer.h"
-// TODO(https://crbug.com/1164001): remove when migrated to ash/components/.
-#include "ash/components/login/auth/user_context.h"
 #include "base/callback.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_launch_error.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_manager_base.h"
@@ -22,6 +20,7 @@
 namespace ash {
 
 enum class KioskAppType;
+class UserContext;
 
 // KioskProfileLoader loads a special profile for a given app. It first
 // attempts to login for the app's generated user id. If the login is
diff --git a/chrome/browser/ash/app_restore/arc_app_launch_handler.cc b/chrome/browser/ash/app_restore/arc_app_launch_handler.cc
index 4f052b3d..4679792e 100644
--- a/chrome/browser/ash/app_restore/arc_app_launch_handler.cc
+++ b/chrome/browser/ash/app_restore/arc_app_launch_handler.cc
@@ -762,6 +762,7 @@
 }
 
 void ArcAppLaunchHandler::StopCpuUsageCount() {
+  probe_service_.reset();
   cpu_tick_count_timer_.Stop();
 }
 
@@ -776,6 +777,12 @@
 
 void ArcAppLaunchHandler::OnCpuUsageUpdated(
     chromeos::cros_healthd::mojom::TelemetryInfoPtr info_ptr) {
+  // May be null in tests.
+  if (info_ptr.is_null() || info_ptr->cpu_result.is_null() ||
+      info_ptr->cpu_result->get_cpu_info().is_null()) {
+    return;
+  }
+
   CpuTick tick;
   // For simplicity, assume that device has only one physical CPU.
   for (const auto& logical_cpu :
diff --git a/chrome/browser/ash/child_accounts/child_status_reporting_service.cc b/chrome/browser/ash/child_accounts/child_status_reporting_service.cc
index 7cb84ec..83aced2 100644
--- a/chrome/browser/ash/child_accounts/child_status_reporting_service.cc
+++ b/chrome/browser/ash/child_accounts/child_status_reporting_service.cc
@@ -65,8 +65,8 @@
 
 void ChildStatusReportingService::CreateStatusUploaderIfNeeded(
     policy::CloudPolicyClient* client) {
-  const base::DictionaryValue* time_limit =
-      pref_change_registrar_->prefs()->GetDictionary(prefs::kUsageTimeLimit);
+  const base::DictionaryValue* time_limit = &base::Value::AsDictionaryValue(
+      *pref_change_registrar_->prefs()->GetDictionary(prefs::kUsageTimeLimit));
   const base::TimeDelta new_day_reset_time =
       usage_time_limit::GetTimeUsageLimitResetTime(*time_limit);
 
diff --git a/chrome/browser/ash/child_accounts/child_user_service.cc b/chrome/browser/ash/child_accounts/child_user_service.cc
index bf0949d..1cf8c6cc 100644
--- a/chrome/browser/ash/child_accounts/child_user_service.cc
+++ b/chrome/browser/ash/child_accounts/child_user_service.cc
@@ -190,7 +190,8 @@
 
 void ChildUserService ::ReportTimeLimitPolicy() const {
   const base::DictionaryValue* time_limit_prefs =
-      profile_->GetPrefs()->GetDictionary(prefs::kUsageTimeLimit);
+      &base::Value::AsDictionaryValue(
+          *profile_->GetPrefs()->GetDictionary(prefs::kUsageTimeLimit));
   DCHECK(time_limit_prefs);
 
   std::set<usage_time_limit::PolicyType> enabled_policies =
diff --git a/chrome/browser/ash/child_accounts/screen_time_controller.cc b/chrome/browser/ash/child_accounts/screen_time_controller.cc
index 1260978..e372e58 100644
--- a/chrome/browser/ash/child_accounts/screen_time_controller.cc
+++ b/chrome/browser/ash/child_accounts/screen_time_controller.cc
@@ -76,8 +76,8 @@
       clock_(base::DefaultClock::GetInstance()),
       next_state_timer_(std::make_unique<base::OneShotTimer>()),
       usage_time_limit_warning_timer_(std::make_unique<base::OneShotTimer>()),
-      last_policy_(pref_service_->GetDictionary(prefs::kUsageTimeLimit)
-                       ->CreateDeepCopy()),
+      last_policy_(base::DictionaryValue::From(base::Value::ToUniquePtrValue(
+          pref_service_->GetDictionary(prefs::kUsageTimeLimit)->Clone()))),
       time_limit_notifier_(context) {
   session_manager::SessionManager::Get()->AddObserver(this);
   UsageTimeStateNotifier::GetInstance()->AddObserver(this);
@@ -147,10 +147,10 @@
   const icu::TimeZone& time_zone =
       system::TimezoneSettings::GetInstance()->GetTimezone();
   absl::optional<usage_time_limit::State> last_state = GetLastStateFromPref();
-  const base::DictionaryValue* time_limit =
-      pref_service_->GetDictionary(prefs::kUsageTimeLimit);
-  const base::DictionaryValue* local_override =
-      pref_service_->GetDictionary(prefs::kTimeLimitLocalOverride);
+  const base::DictionaryValue* time_limit = &base::Value::AsDictionaryValue(
+      *pref_service_->GetDictionary(prefs::kUsageTimeLimit));
+  const base::DictionaryValue* local_override = &base::Value::AsDictionaryValue(
+      *pref_service_->GetDictionary(prefs::kTimeLimitLocalOverride));
 
   // TODO(agawronska): Usage timestamp should be passed instead of second |now|.
   usage_time_limit::State state = usage_time_limit::GetState(
@@ -362,7 +362,7 @@
 
 absl::optional<usage_time_limit::State>
 ScreenTimeController::GetLastStateFromPref() {
-  const base::DictionaryValue* last_state =
+  const base::Value* last_state =
       pref_service_->GetDictionary(prefs::kScreenTimeLastState);
   usage_time_limit::State result;
   if (last_state->DictEmpty())
@@ -444,10 +444,10 @@
   base::Time now = clock_->Now();
   const icu::TimeZone& time_zone =
       system::TimezoneSettings::GetInstance()->GetTimezone();
-  const base::DictionaryValue* time_limit =
-      pref_service_->GetDictionary(prefs::kUsageTimeLimit);
-  const base::DictionaryValue* local_override =
-      pref_service_->GetDictionary(prefs::kTimeLimitLocalOverride);
+  const base::DictionaryValue* time_limit = &base::Value::AsDictionaryValue(
+      *pref_service_->GetDictionary(prefs::kUsageTimeLimit));
+  const base::DictionaryValue* local_override = &base::Value::AsDictionaryValue(
+      *pref_service_->GetDictionary(prefs::kTimeLimitLocalOverride));
 
   absl::optional<base::TimeDelta> remaining_usage =
       usage_time_limit::GetRemainingTimeUsage(*time_limit, local_override, now,
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_time_controller.cc b/chrome/browser/ash/child_accounts/time_limits/app_time_controller.cc
index 99e4b667..561139d 100644
--- a/chrome/browser/ash/child_accounts/time_limits/app_time_controller.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/app_time_controller.cc
@@ -402,8 +402,9 @@
     const std::string& pref_name) {
   DCHECK_EQ(pref_name, prefs::kPerAppTimeLimitsAllowlistPolicy);
 
-  const base::DictionaryValue* policy = pref_registrar_->prefs()->GetDictionary(
-      prefs::kPerAppTimeLimitsAllowlistPolicy);
+  const base::DictionaryValue* policy =
+      &base::Value::AsDictionaryValue(*pref_registrar_->prefs()->GetDictionary(
+          prefs::kPerAppTimeLimitsAllowlistPolicy));
 
   // Figure out a way to avoid cloning
   AppTimeLimitsAllowlistPolicyWrapper wrapper(policy);
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
index 2778ea9e..bad96f0 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -31,6 +31,7 @@
 #include "ash/public/cpp/keyboard/keyboard_controller.h"
 #include "ash/shell.h"
 #include "ash/system/pcie_peripheral/pcie_peripheral_notification_controller.h"
+#include "ash/system/usb_peripheral/usb_peripheral_notification_controller.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
@@ -1259,6 +1260,9 @@
     Shell::Get()
         ->pcie_peripheral_notification_controller()
         ->OnPeripheralNotificationManagerInitialized();
+    Shell::Get()
+        ->usb_peripheral_notification_controller()
+        ->OnPeripheralNotificationManagerInitialized();
   }
 
   crostini_unsupported_action_notifier_ =
diff --git a/chrome/browser/ash/crosapi/authentication_ash.cc b/chrome/browser/ash/crosapi/authentication_ash.cc
index c72c09b1..c38c45b 100644
--- a/chrome/browser/ash/crosapi/authentication_ash.cc
+++ b/chrome/browser/ash/crosapi/authentication_ash.cc
@@ -36,8 +36,8 @@
       base::MakeRefCounted<extensions::QuickUnlockPrivateGetAuthTokenHelper>(
           ProfileManager::GetActiveUserProfile());
   // |extended_authenticator| is kept alive by |on_result_callback| binding.
-  scoped_refptr<chromeos::ExtendedAuthenticator> extended_authenticator =
-      chromeos::ExtendedAuthenticator::Create(helper.get());
+  scoped_refptr<ash::ExtendedAuthenticator> extended_authenticator =
+      ash::ExtendedAuthenticator::Create(helper.get());
   auto on_result_callback = base::BindOnce(
       &AuthenticationAsh::OnCreateQuickUnlockPrivateTokenInfoResults,
       weak_factory_.GetWeakPtr(), std::move(callback), extended_authenticator);
@@ -57,7 +57,7 @@
 
 void AuthenticationAsh::OnCreateQuickUnlockPrivateTokenInfoResults(
     CreateQuickUnlockPrivateTokenInfoCallback callback,
-    scoped_refptr<chromeos::ExtendedAuthenticator> extended_authenticator,
+    scoped_refptr<ash::ExtendedAuthenticator> extended_authenticator,
     bool success,
     std::unique_ptr<TokenInfo> token_info,
     const std::string& error_message) {
diff --git a/chrome/browser/ash/crosapi/authentication_ash.h b/chrome/browser/ash/crosapi/authentication_ash.h
index a6599f8..ad91fe38 100644
--- a/chrome/browser/ash/crosapi/authentication_ash.h
+++ b/chrome/browser/ash/crosapi/authentication_ash.h
@@ -13,9 +13,9 @@
 #include "chromeos/crosapi/mojom/authentication.mojom.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 
-namespace chromeos {
+namespace ash {
 class ExtendedAuthenticator;
-}  // namespace chromeos
+}  // namespace ash
 
 namespace extensions {
 namespace api {
@@ -53,7 +53,7 @@
   // extensions::QuickUnlockPrivateGetAuthTokenHelper::ResultCallback.
   void OnCreateQuickUnlockPrivateTokenInfoResults(
       CreateQuickUnlockPrivateTokenInfoCallback callback,
-      scoped_refptr<chromeos::ExtendedAuthenticator> extended_authenticator,
+      scoped_refptr<ash::ExtendedAuthenticator> extended_authenticator,
       bool success,
       std::unique_ptr<TokenInfo> token_info,
       const std::string& error_message);
diff --git a/chrome/browser/ash/crosapi/browser_util.cc b/chrome/browser/ash/crosapi/browser_util.cc
index e50606969..4b175d0 100644
--- a/chrome/browser/ash/crosapi/browser_util.cc
+++ b/chrome/browser/ash/crosapi/browser_util.cc
@@ -236,11 +236,6 @@
 const base::Feature kLacrosGooglePolicyRollout{
     "LacrosGooglePolicyRollout", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enable this to turn on profile migration for non-googlers. Currently the
-// feature is only limited to googlers only.
-const base::Feature kLacrosProfileMigrationForAnyUser{
-    "LacrosProfileMigrationForAnyUser", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Emergency switch to turn off profile migration via Finch.
 const base::Feature kLacrosProfileMigrationForceOff{
     "LacrosProfileMigrationForceOff", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -367,7 +362,8 @@
   //  `kLacrosProfileMigrationForAnyUser` can be enabled to allow testing with
   //  non-googler accounts.
   if (gaia::IsGoogleInternalAccountEmail(account_id.GetUserEmail()) ||
-      base::FeatureList::IsEnabled(kLacrosProfileMigrationForAnyUser))
+      base::FeatureList::IsEnabled(
+          ash::features::kLacrosProfileMigrationForAnyUser))
     return true;
 
   return false;
@@ -554,8 +550,7 @@
 
 base::Version GetDataVer(PrefService* local_state,
                          const std::string& user_id_hash) {
-  const base::DictionaryValue* data_versions =
-      local_state->GetDictionary(kDataVerPref);
+  const base::Value* data_versions = local_state->GetDictionary(kDataVerPref);
   const std::string* data_version_str =
       data_versions->FindStringPath(user_id_hash);
 
diff --git a/chrome/browser/ash/crosapi/browser_util_unittest.cc b/chrome/browser/ash/crosapi/browser_util_unittest.cc
index 51f7ad3..2e671129 100644
--- a/chrome/browser/ash/crosapi/browser_util_unittest.cc
+++ b/chrome/browser/ash/crosapi/browser_util_unittest.cc
@@ -494,8 +494,8 @@
 
   base::DictionaryValue expected;
   expected.SetString(user_id_hash, version.GetString());
-  const base::DictionaryValue* dict =
-      pref_service_.GetDictionary(browser_util::kDataVerPref);
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *pref_service_.GetDictionary(browser_util::kDataVerPref));
   EXPECT_TRUE(dict->Equals(&expected));
 }
 
@@ -510,8 +510,8 @@
   base::DictionaryValue expected;
   expected.SetString(user_id_hash, version2.GetString());
 
-  const base::DictionaryValue* dict =
-      pref_service_.GetDictionary(browser_util::kDataVerPref);
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *pref_service_.GetDictionary(browser_util::kDataVerPref));
   EXPECT_TRUE(dict->Equals(&expected));
 }
 
@@ -533,8 +533,8 @@
   expected.SetString(user_id_hash_1, version3.GetString());
   expected.SetString(user_id_hash_2, version2.GetString());
 
-  const base::DictionaryValue* dict =
-      pref_service_.GetDictionary(browser_util::kDataVerPref);
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *pref_service_.GetDictionary(browser_util::kDataVerPref));
   EXPECT_TRUE(dict->Equals(&expected));
 }
 
diff --git a/chrome/browser/ash/crostini/crostini_shelf_utils.cc b/chrome/browser/ash/crostini/crostini_shelf_utils.cc
index d841b504..c1dea43 100644
--- a/chrome/browser/ash/crostini/crostini_shelf_utils.cc
+++ b/chrome/browser/ash/crostini/crostini_shelf_utils.cc
@@ -142,8 +142,8 @@
 std::string GetCrostiniShelfAppId(const Profile* profile,
                                   const std::string* window_app_id,
                                   const std::string* window_startup_id) {
-  const base::DictionaryValue* apps =
-      profile->GetPrefs()->GetDictionary(guest_os::prefs::kGuestOsRegistry);
+  const base::DictionaryValue* apps = &base::Value::AsDictionaryValue(
+      *profile->GetPrefs()->GetDictionary(guest_os::prefs::kGuestOsRegistry));
   std::string app_id;
 
   if (window_startup_id) {
diff --git a/chrome/browser/ash/crostini/crostini_terminal.cc b/chrome/browser/ash/crostini/crostini_terminal.cc
index 15bd49d..ba32afc 100644
--- a/chrome/browser/ash/crostini/crostini_terminal.cc
+++ b/chrome/browser/ash/crostini/crostini_terminal.cc
@@ -384,7 +384,7 @@
       {"theme-variations", TerminalSetting::kThemeVariations},
   });
 
-  const base::DictionaryValue* settings = profile->GetPrefs()->GetDictionary(
+  const base::Value* settings = profile->GetPrefs()->GetDictionary(
       crostini::prefs::kCrostiniTerminalSettings);
   for (const auto item : settings->DictItems()) {
     // Only record settings for /hterm/profiles/default/.
@@ -401,14 +401,14 @@
 }
 
 std::string GetTerminalSettingBackgroundColor(Profile* profile) {
-  const base::DictionaryValue* value = profile->GetPrefs()->GetDictionary(
+  const base::Value* value = profile->GetPrefs()->GetDictionary(
       crostini::prefs::kCrostiniTerminalSettings);
   const std::string* result = value->FindStringKey(kSettingBackgroundColor);
   return result ? *result : kDefaultBackgroundColor;
 }
 
 bool GetTerminalSettingPassCtrlW(Profile* profile) {
-  const base::DictionaryValue* value = profile->GetPrefs()->GetDictionary(
+  const base::Value* value = profile->GetPrefs()->GetDictionary(
       crostini::prefs::kCrostiniTerminalSettings);
   return value->FindBoolKey(kSettingPassCtrlW).value_or(kDefaultPassCtrlW);
 }
diff --git a/chrome/browser/ash/customization/customization_document.cc b/chrome/browser/ash/customization/customization_document.cc
index 51b6dd4..a95ad3c 100644
--- a/chrome/browser/ash/customization/customization_document.cc
+++ b/chrome/browser/ash/customization/customization_document.cc
@@ -779,8 +779,8 @@
     loader->SetCurrentApps(GetDefaultAppsInProviderFormat(*root_));
     SetOemFolderName(profile, *root_);
   } else {
-    const base::DictionaryValue* root =
-        profile->GetPrefs()->GetDictionary(kServicesCustomizationKey);
+    const base::DictionaryValue* root = &base::Value::AsDictionaryValue(
+        *profile->GetPrefs()->GetDictionary(kServicesCustomizationKey));
     std::string version;
     if (root && root->GetString(kVersionAttr, &version)) {
       // If version exists, profile has cached version of customization.
diff --git a/chrome/browser/ash/display/display_prefs_browsertest.cc b/chrome/browser/ash/display/display_prefs_browsertest.cc
index f22d3dc..ac6292e 100644
--- a/chrome/browser/ash/display/display_prefs_browsertest.cc
+++ b/chrome/browser/ash/display/display_prefs_browsertest.cc
@@ -30,7 +30,7 @@
   const base::Value* GetDisplayProperties(int index) {
     int64_t display_id =
         ash::Shell::Get()->display_manager()->GetDisplayAt(index).id();
-    const base::DictionaryValue* display_properties =
+    const base::Value* display_properties =
         local_state_->GetDictionary(ash::prefs::kDisplayProperties);
     return display_properties ? display_properties->FindKeyOfType(
                                     base::NumberToString(display_id),
diff --git a/chrome/browser/ash/external_protocol_dialog.cc b/chrome/browser/ash/external_protocol_dialog.cc
index ae54ef2..a023ff0 100644
--- a/chrome/browser/ash/external_protocol_dialog.cc
+++ b/chrome/browser/ash/external_protocol_dialog.cc
@@ -37,23 +37,23 @@
   if (handled)
     return;
 
+  // If WebContents have been destroyed, do not show any dialog.
   WebContents* web_contents =
       tab_util::GetWebContentsByID(render_process_host_id, routing_id);
+  if (!web_contents)
+    return;
 
   // Display the standard ExternalProtocolDialog if Guest OS has a handler.
-  if (web_contents) {
-    absl::optional<guest_os::GuestOsRegistryService::Registration>
-        registration = guest_os::GetHandler(
-            Profile::FromBrowserContext(web_contents->GetBrowserContext()),
-            url);
-    if (registration) {
-      new ExternalProtocolDialog(web_contents, url,
-                                 base::UTF8ToUTF16(registration->Name()),
-                                 initiating_origin);
-      return;
-    }
+  absl::optional<guest_os::GuestOsRegistryService::Registration> registration =
+      guest_os::GetHandler(
+          Profile::FromBrowserContext(web_contents->GetBrowserContext()), url);
+  if (registration) {
+    new ExternalProtocolDialog(web_contents, url,
+                               base::UTF8ToUTF16(registration->Name()),
+                               initiating_origin);
+  } else {
+    new ash::ExternalProtocolNoHandlersDialog(web_contents, url);
   }
-  new ash::ExternalProtocolNoHandlersDialog(web_contents, url);
 }
 
 }  // namespace
diff --git a/chrome/browser/ash/file_manager/file_tasks.cc b/chrome/browser/ash/file_manager/file_tasks.cc
index 7c66373..51f4058c 100644
--- a/chrome/browser/ash/file_manager/file_tasks.cc
+++ b/chrome/browser/ash/file_manager/file_tasks.cc
@@ -471,7 +471,7 @@
   VLOG(1) << "Looking for default for MIME type: " << mime_type
       << " and suffix: " << suffix;
   if (!mime_type.empty()) {
-    const base::DictionaryValue* mime_task_prefs =
+    const base::Value* mime_task_prefs =
         pref_service.GetDictionary(prefs::kDefaultTasksByMimeType);
     DCHECK(mime_task_prefs);
     LOG_IF(ERROR, !mime_task_prefs) << "Unable to open MIME type prefs";
@@ -484,7 +484,7 @@
     }
   }
 
-  const base::DictionaryValue* suffix_task_prefs =
+  const base::Value* suffix_task_prefs =
       pref_service.GetDictionary(prefs::kDefaultTasksBySuffix);
   DCHECK(suffix_task_prefs);
   LOG_IF(ERROR, !suffix_task_prefs) << "Unable to open suffix prefs";
diff --git a/chrome/browser/ash/file_system_provider/registry.cc b/chrome/browser/ash/file_system_provider/registry.cc
index 862338a..38e5cd9d 100644
--- a/chrome/browser/ash/file_system_provider/registry.cc
+++ b/chrome/browser/ash/file_system_provider/registry.cc
@@ -130,7 +130,8 @@
   DCHECK(pref_service);
 
   const base::DictionaryValue* const file_systems =
-      pref_service->GetDictionary(prefs::kFileSystemProviderMounted);
+      &base::Value::AsDictionaryValue(
+          *pref_service->GetDictionary(prefs::kFileSystemProviderMounted));
   DCHECK(file_systems);
 
   const base::DictionaryValue* file_systems_per_extension = NULL;
diff --git a/chrome/browser/ash/file_system_provider/registry_unittest.cc b/chrome/browser/ash/file_system_provider/registry_unittest.cc
index 346880e..0269d1c 100644
--- a/chrome/browser/ash/file_system_provider/registry_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/registry_unittest.cc
@@ -180,7 +180,8 @@
   ASSERT_TRUE(pref_service);
 
   const base::DictionaryValue* const extensions =
-      pref_service->GetDictionary(prefs::kFileSystemProviderMounted);
+      &base::Value::AsDictionaryValue(
+          *pref_service->GetDictionary(prefs::kFileSystemProviderMounted));
   ASSERT_TRUE(extensions);
 
   const base::DictionaryValue* file_systems = NULL;
@@ -267,7 +268,8 @@
   ASSERT_TRUE(pref_service);
 
   const base::DictionaryValue* const extensions =
-      pref_service->GetDictionary(prefs::kFileSystemProviderMounted);
+      &base::Value::AsDictionaryValue(
+          *pref_service->GetDictionary(prefs::kFileSystemProviderMounted));
   ASSERT_TRUE(extensions);
 
   const base::DictionaryValue* file_systems = NULL;
@@ -299,7 +301,8 @@
   ASSERT_TRUE(pref_service);
 
   const base::DictionaryValue* const extensions =
-      pref_service->GetDictionary(prefs::kFileSystemProviderMounted);
+      &base::Value::AsDictionaryValue(
+          *pref_service->GetDictionary(prefs::kFileSystemProviderMounted));
   ASSERT_TRUE(extensions);
 
   const base::DictionaryValue* file_systems = NULL;
diff --git a/chrome/browser/ash/guest_os/guest_os_registry_service.cc b/chrome/browser/ash/guest_os/guest_os_registry_service.cc
index dd62c54..aa9df56 100644
--- a/chrome/browser/ash/guest_os/guest_os_registry_service.cc
+++ b/chrome/browser/ash/guest_os/guest_os_registry_service.cc
@@ -561,7 +561,7 @@
 
 std::map<std::string, GuestOsRegistryService::Registration>
 GuestOsRegistryService::GetAllRegisteredApps() const {
-  const base::DictionaryValue* apps =
+  const base::Value* apps =
       prefs_->GetDictionary(guest_os::prefs::kGuestOsRegistry);
   std::map<std::string, GuestOsRegistryService::Registration> result;
   // Register Terminal by merging optional prefs with app values.
@@ -632,7 +632,7 @@
 
 absl::optional<GuestOsRegistryService::Registration>
 GuestOsRegistryService::GetRegistration(const std::string& app_id) const {
-  const base::DictionaryValue* apps =
+  const base::Value* apps =
       prefs_->GetDictionary(guest_os::prefs::kGuestOsRegistry);
 
   if (app_id == crostini::kCrostiniTerminalSystemAppId) {
@@ -648,7 +648,7 @@
 }
 
 void GuestOsRegistryService::RecordStartupMetrics() {
-  const base::DictionaryValue* apps =
+  const base::Value* apps =
       prefs_->GetDictionary(guest_os::prefs::kGuestOsRegistry);
 
   base::flat_map<int, int> num_apps;
diff --git a/chrome/browser/ash/guest_os/guest_os_share_path.cc b/chrome/browser/ash/guest_os/guest_os_share_path.cc
index a8feadc..392894d 100644
--- a/chrome/browser/ash/guest_os/guest_os_share_path.cc
+++ b/chrome/browser/ash/guest_os/guest_os_share_path.cc
@@ -536,7 +536,7 @@
   CHECK(profile_);
   CHECK(profile_->GetPrefs());
   // |shared_paths| format is {'path': ['vm1', vm2']}.
-  const base::DictionaryValue* shared_paths =
+  const base::Value* shared_paths =
       profile_->GetPrefs()->GetDictionary(prefs::kGuestOSPathsSharedToVms);
   CHECK(shared_paths);
   for (const auto it : shared_paths->DictItems()) {
@@ -628,7 +628,7 @@
 
   // Check if any persisted paths match volume.mount_path() or are children
   // of it then share them with any running VMs.
-  const base::DictionaryValue* shared_paths =
+  const base::Value* shared_paths =
       profile_->GetPrefs()->GetDictionary(prefs::kGuestOSPathsSharedToVms);
   for (const auto it : shared_paths->DictItems()) {
     base::FilePath path(it.first);
diff --git a/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc b/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
index 0aa3557..2ff795d 100644
--- a/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
+++ b/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
@@ -87,7 +87,7 @@
       const base::FilePath& container_path,
       bool success,
       const std::string& failure_reason) {
-    const base::DictionaryValue* prefs =
+    const base::Value* prefs =
         profile()->GetPrefs()->GetDictionary(prefs::kGuestOSPathsSharedToVms);
     EXPECT_NE(prefs->FindKey(shared_path_.value()), nullptr);
     EXPECT_EQ(prefs->FindKey(shared_path_.value())->GetList().size(), 1U);
@@ -172,7 +172,7 @@
       const std::string& expected_failure_reason,
       bool success,
       const std::string& failure_reason) {
-    const base::DictionaryValue* prefs =
+    const base::Value* prefs =
         profile()->GetPrefs()->GetDictionary(prefs::kGuestOSPathsSharedToVms);
     if (expected_persist == Persist::YES) {
       EXPECT_NE(prefs->FindKey(path.value()), nullptr);
@@ -718,7 +718,7 @@
   profile()->GetPrefs()->Set(prefs::kGuestOSPathsSharedToVms, shared_paths);
 
   guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a/a"));
-  const base::DictionaryValue* prefs =
+  const base::Value* prefs =
       profile()->GetPrefs()->GetDictionary(prefs::kGuestOSPathsSharedToVms);
   EXPECT_EQ(prefs->DictSize(), 1U);
   EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 1U);
diff --git a/chrome/browser/ash/hats/hats_notification_controller_unittest.cc b/chrome/browser/ash/hats/hats_notification_controller_unittest.cc
index b9f3593..87eff2b 100644
--- a/chrome/browser/ash/hats/hats_notification_controller_unittest.cc
+++ b/chrome/browser/ash/hats/hats_notification_controller_unittest.cc
@@ -65,8 +65,9 @@
   scoped_refptr<HatsNotificationController> InstantiateHatsController() {
     // The initialization will fail since the function IsNewDevice() will return
     // true.
-    scoped_refptr<HatsNotificationController> hats_notification_controller =
-        new HatsNotificationController(profile(), kHatsGeneralSurvey);
+    auto hats_notification_controller =
+        base::MakeRefCounted<HatsNotificationController>(profile(),
+                                                         kHatsGeneralSurvey);
 
     // HatsController::IsNewDevice() is run on a blocking thread.
     content::RunAllTasksUntilIdle();
@@ -130,9 +131,11 @@
   // Finally check if notification was launched to confirm initialization.
   EXPECT_TRUE(display_service_->GetNotification(
       HatsNotificationController::kNotificationId));
+
+  // Simulate dismissing notification by the user to clean up the notification.
   display_service_->RemoveNotification(
       NotificationHandler::Type::TRANSIENT,
-      HatsNotificationController::kNotificationId, false);
+      HatsNotificationController::kNotificationId, /*by_user=*/true);
 }
 
 TEST_F(HatsNotificationControllerTest, NoInternet_DoNotShowNotification) {
@@ -219,9 +222,10 @@
   EXPECT_TRUE(display_service_->GetNotification(
       HatsNotificationController::kNotificationId));
 
+  // Simulate dismissing notification by the user to clean up the notification.
   display_service_->RemoveNotification(
       NotificationHandler::Type::TRANSIENT,
-      HatsNotificationController::kNotificationId, false);
+      HatsNotificationController::kNotificationId, /*by_user=*/true);
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/input_method/diacritics_checker.cc b/chrome/browser/ash/input_method/diacritics_checker.cc
new file mode 100644
index 0000000..acd7528
--- /dev/null
+++ b/chrome/browser/ash/input_method/diacritics_checker.cc
@@ -0,0 +1,40 @@
+// 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 "chrome/browser/ash/input_method/diacritics_checker.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "third_party/re2/src/re2/re2.h"
+
+namespace {
+
+// Intentionally only covering Latin-script accented letters likely found in
+// French, Spanish, Dutch, Swedish, Norwegian, Danish, and Catalan.
+constexpr char HAS_DIACRITICS_REGEX[] =
+    "["
+    "áàâäāåÁÀÂÄĀÅ"
+    "éèêëēÉÈÊËĒ"
+    "íìîïīÍÌÎÏĪ"
+    "óòôöōøÓÒÔÖŌØ"
+    "úùûüūÚÙÛÜŪ"
+    "ýỳŷÿȳÝỲŶŸȲ"
+    "çñæœÇÑÆŒ"
+    "]";
+
+}  // namespace
+
+namespace ash {
+namespace input_method {
+
+bool HasDiacritics(const std::u16string& text) {
+  if (text.empty()) {
+    return false;
+  }
+
+  std::string text_utf8 = base::UTF16ToUTF8(text);
+  return re2::RE2::PartialMatch(text_utf8, HAS_DIACRITICS_REGEX);
+}
+
+}  // namespace input_method
+}  // namespace ash
diff --git a/chrome/browser/ash/input_method/diacritics_checker.h b/chrome/browser/ash/input_method/diacritics_checker.h
new file mode 100644
index 0000000..6b65a945
--- /dev/null
+++ b/chrome/browser/ash/input_method/diacritics_checker.h
@@ -0,0 +1,19 @@
+// 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 CHROME_BROWSER_ASH_INPUT_METHOD_DIACRITICS_CHECKER_H_
+#define CHROME_BROWSER_ASH_INPUT_METHOD_DIACRITICS_CHECKER_H_
+
+#include <string>
+
+namespace ash {
+namespace input_method {
+
+// Checks if the given UTF-16 text contains some accented letters.
+bool HasDiacritics(const std::u16string& text);
+
+}  // namespace input_method
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_ASH_INPUT_METHOD_DIACRITICS_CHECKER_H_
diff --git a/chrome/browser/ash/input_method/diacritics_checker_unittest.cc b/chrome/browser/ash/input_method/diacritics_checker_unittest.cc
new file mode 100644
index 0000000..a2fb871
--- /dev/null
+++ b/chrome/browser/ash/input_method/diacritics_checker_unittest.cc
@@ -0,0 +1,44 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/input_method/diacritics_checker.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+namespace input_method {
+
+namespace {
+
+TEST(DiacriticsCheckerTest, CheckReportsThereAreSomeDiacritics) {
+  EXPECT_TRUE(HasDiacritics(u"français"));
+  EXPECT_TRUE(HasDiacritics(u"déjà"));
+  EXPECT_TRUE(HasDiacritics(u"Español"));
+  EXPECT_TRUE(HasDiacritics(u"École"));
+  EXPECT_TRUE(HasDiacritics(u"cœur"));
+  EXPECT_TRUE(HasDiacritics(u"København"));
+  EXPECT_TRUE(HasDiacritics(u"ångström"));
+  EXPECT_TRUE(HasDiacritics(u"Neuchâtel"));
+  EXPECT_TRUE(HasDiacritics(u"jamón"));
+  EXPECT_TRUE(HasDiacritics(u"NOËL"));
+}
+
+TEST(DiacriticsCheckerTest, CheckReportsThereAreNoDiacritics) {
+  EXPECT_FALSE(HasDiacritics(u""));
+  EXPECT_FALSE(HasDiacritics(u"   "));
+  EXPECT_FALSE(HasDiacritics(u"francais"));
+  EXPECT_FALSE(HasDiacritics(u"deja"));
+  EXPECT_FALSE(HasDiacritics(u"Espanol"));
+  EXPECT_FALSE(HasDiacritics(u"Ecole"));
+  EXPECT_FALSE(HasDiacritics(u"coeur"));
+  EXPECT_FALSE(HasDiacritics(u"Copenhagen"));
+  EXPECT_FALSE(HasDiacritics(u"angstrom"));
+  EXPECT_FALSE(HasDiacritics(u"Newcastle"));
+  EXPECT_FALSE(HasDiacritics(u"jambon"));
+  EXPECT_FALSE(HasDiacritics(u"Christmas"));
+}
+
+}  // namespace
+}  // namespace input_method
+}  // namespace ash
diff --git a/chrome/browser/ash/input_method/input_method_engine.cc b/chrome/browser/ash/input_method/input_method_engine.cc
index fe155fa..6b10024 100644
--- a/chrome/browser/ash/input_method/input_method_engine.cc
+++ b/chrome/browser/ash/input_method/input_method_engine.cc
@@ -977,9 +977,8 @@
 }
 
 void InputMethodEngine::OnInputMethodOptionsChanged() {
-  const base::DictionaryValue* new_settings =
-      profile_->GetPrefs()->GetDictionary(
-          prefs::kLanguageInputMethodSpecificSettings);
+  const base::Value* new_settings = profile_->GetPrefs()->GetDictionary(
+      prefs::kLanguageInputMethodSpecificSettings);
   const base::DictionaryValue& old_settings =
       base::Value::AsDictionaryValue(input_method_settings_snapshot_);
   for (const auto it : new_settings->DictItems()) {
diff --git a/chrome/browser/ash/input_method/input_method_settings.cc b/chrome/browser/ash/input_method/input_method_settings.cc
index e9eea61..108cc58 100644
--- a/chrome/browser/ash/input_method/input_method_settings.cc
+++ b/chrome/browser/ash/input_method/input_method_settings.cc
@@ -65,7 +65,9 @@
 }
 
 bool IsFstEngine(const std::string& engine_id) {
-  return base::StartsWith(engine_id, "xkb:", base::CompareCase::SENSITIVE);
+  return base::StartsWith(engine_id, "xkb:", base::CompareCase::SENSITIVE) ||
+         base::StartsWith(engine_id, "experimental_",
+                          base::CompareCase::SENSITIVE);
 }
 
 bool IsKoreanEngine(const std::string& engine_id) {
@@ -96,9 +98,12 @@
     const std::string& engine_id,
     const InputFieldContext& context) {
   auto settings = mojom::LatinSettings::New();
-  settings->autocorrect = input_method_specific_pref
-                              .FindIntKey("physicalKeyboardAutoCorrectionLevel")
-                              .value_or(0) > 0;
+  settings->autocorrect =
+      base::StartsWith(engine_id, "experimental_",
+                       base::CompareCase::SENSITIVE) ||
+      input_method_specific_pref
+              .FindIntKey("physicalKeyboardAutoCorrectionLevel")
+              .value_or(0) > 0;
   settings->predictive_writing =
       context.multiword_enabled && context.multiword_allowed &&
       !context.lacros_enabled &&
@@ -246,7 +251,7 @@
     const InputFieldContext& context) {
   // All input method settings are stored in a single pref whose value is a
   // dictionary.
-  const base::DictionaryValue& all_input_method_pref =
+  const base::Value& all_input_method_pref =
       *prefs.GetDictionary(::prefs::kLanguageInputMethodSpecificSettings);
 
   // For each input method, the dictionary contains an entry, with the key being
diff --git a/chrome/browser/ash/input_method/native_input_method_engine.cc b/chrome/browser/ash/input_method/native_input_method_engine.cc
index 26c9301..55f7a6f 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/ash/input_method/assistive_suggester_client_filter.h"
 #include "chrome/browser/ash/input_method/assistive_suggester_switch.h"
 #include "chrome/browser/ash/input_method/autocorrect_manager.h"
+#include "chrome/browser/ash/input_method/diacritics_checker.h"
 #include "chrome/browser/ash/input_method/grammar_service_client.h"
 #include "chrome/browser/ash/input_method/input_method_settings.h"
 #include "chrome/browser/ash/input_method/suggestions_service_client.h"
@@ -31,6 +32,7 @@
 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "ui/base/ime/ash/extension_ime_util.h"
 #include "ui/base/ime/ash/ime_bridge.h"
 #include "ui/base/ime/ash/input_method_manager.h"
 #include "ui/base/ime/ash/input_method_ukm.h"
@@ -43,12 +45,23 @@
 
 namespace mojom = ::chromeos::ime::mojom;
 
-bool ShouldRouteToRuleBasedEngine(const std::string& engine_id) {
+// These are persisted to logs. Entries should not be renumbered. Numeric values
+// should not be reused. Must stay in sync with IMENonAutocorrectDiacriticStatus
+// enum in: tools/metrics/histograms/enums.xml
+enum class NonAutocorrectDiacriticStatus {
+  kWithoutDiacritics = 0,
+  kWithDiacritics = 1,
+  kMaxValue = kWithDiacritics,
+};
+
+bool IsRuleBasedEngine(const std::string& engine_id) {
   return base::StartsWith(engine_id, "vkd_", base::CompareCase::SENSITIVE);
 }
 
 bool IsFstEngine(const std::string& engine_id) {
-  return base::StartsWith(engine_id, "xkb:", base::CompareCase::SENSITIVE);
+  return base::StartsWith(engine_id, "xkb:", base::CompareCase::SENSITIVE) ||
+         base::StartsWith(engine_id, "experimental_",
+                          base::CompareCase::SENSITIVE);
 }
 
 bool IsKoreanEngine(const std::string& engine_id) {
@@ -73,7 +86,7 @@
   return engine_id == "xkb:us::eng";
 }
 
-bool ShouldRouteToNativeMojoEngine(const std::string& engine_id) {
+bool CanRouteToNativeMojoEngine(const std::string& engine_id) {
   // To avoid handling tricky cases where the user types with both the virtual
   // and the physical keyboard, only run the native code path if the virtual
   // keyboard is disabled. Otherwise, just let the extension handle any physical
@@ -93,8 +106,12 @@
 
 bool IsPhysicalKeyboardAutocorrectEnabled(PrefService* prefs,
                                           const std::string& engine_id) {
-  // The FST Mojo engine is only needed if autocorrect is enabled.
-  const base::DictionaryValue* input_method_settings =
+  if (base::StartsWith(engine_id, "experimental_",
+                       base::CompareCase::SENSITIVE)) {
+    return true;
+  }
+
+  const base::Value* input_method_settings =
       prefs->GetDictionary(::prefs::kLanguageInputMethodSpecificSettings);
   const base::Value* autocorrect_setting = input_method_settings->FindPath(
       engine_id + ".physicalKeyboardAutoCorrectionLevel");
@@ -443,7 +460,18 @@
 
 }  // namespace
 
-NativeInputMethodEngine::NativeInputMethodEngine() = default;
+NativeInputMethodEngine::NativeInputMethodEngine()
+    : NativeInputMethodEngine(/*use_ime_service=*/true) {}
+
+NativeInputMethodEngine::NativeInputMethodEngine(bool use_ime_service)
+    : use_ime_service_(use_ime_service) {}
+
+// static
+std::unique_ptr<NativeInputMethodEngine>
+NativeInputMethodEngine::CreateForTesting(bool use_ime_service) {
+  return base::WrapUnique<NativeInputMethodEngine>(
+      new NativeInputMethodEngine(use_ime_service));
+}
 
 NativeInputMethodEngine::~NativeInputMethodEngine() = default;
 
@@ -487,11 +515,17 @@
       profile->GetPrefs(), std::move(observer), std::move(assistive_suggester),
       std::move(autocorrect_manager), std::move(suggestions_collector),
       std::make_unique<GrammarManager>(
-          profile, std::make_unique<GrammarServiceClient>(), this));
+          profile, std::make_unique<GrammarServiceClient>(), this),
+      use_ime_service_);
   InputMethodEngine::Initialize(std::move(native_observer), extension_id,
                                 profile);
 }
 
+bool NativeInputMethodEngine::ShouldRouteToNativeMojoEngine(
+    const std::string& engine_id) const {
+  return use_ime_service_ && CanRouteToNativeMojoEngine(engine_id);
+}
+
 void NativeInputMethodEngine::CandidateClicked(uint32_t index) {
   // The parent implementation will try to convert `index` into a candidate ID.
   // The native Mojo engine doesn't use candidate IDs, so we just treat `index`
@@ -543,21 +577,34 @@
     std::unique_ptr<AssistiveSuggester> assistive_suggester,
     std::unique_ptr<AutocorrectManager> autocorrect_manager,
     std::unique_ptr<SuggestionsCollector> suggestions_collector,
-    std::unique_ptr<GrammarManager> grammar_manager)
+    std::unique_ptr<GrammarManager> grammar_manager,
+    bool use_ime_service)
     : prefs_(prefs),
       ime_base_observer_(std::move(ime_base_observer)),
       assistive_suggester_(std::move(assistive_suggester)),
       autocorrect_manager_(std::move(autocorrect_manager)),
       suggestions_collector_(std::move(suggestions_collector)),
-      grammar_manager_(std::move(grammar_manager)) {}
+      grammar_manager_(std::move(grammar_manager)),
+      use_ime_service_(use_ime_service) {}
 
 NativeInputMethodEngine::ImeObserver::~ImeObserver() = default;
 
+bool NativeInputMethodEngine::ImeObserver::ShouldRouteToRuleBasedEngine(
+    const std::string& engine_id) const {
+  return use_ime_service_ && IsRuleBasedEngine(engine_id);
+}
+
+bool NativeInputMethodEngine::ImeObserver::ShouldRouteToNativeMojoEngine(
+    const std::string& engine_id) const {
+  return use_ime_service_ && CanRouteToNativeMojoEngine(engine_id);
+}
+
 void NativeInputMethodEngine::ImeObserver::OnActivate(
     const std::string& engine_id) {
   // TODO(b/181077907): Always launch the IME service and let IME service decide
   // whether it should shutdown or not.
   if (IsFstEngine(engine_id) && ShouldRouteToNativeMojoEngine(engine_id) &&
+      // The FST Mojo engine is only needed if autocorrect is enabled.
       !IsPhysicalKeyboardAutocorrectEnabled(prefs_, engine_id) &&
       !IsPredictiveWritingEnabled(prefs_, engine_id)) {
     remote_manager_.reset();
@@ -951,9 +998,27 @@
 }
 
 void NativeInputMethodEngine::ImeObserver::FinishComposition() {
-  ui::IMEBridge::Get()->GetInputContextHandler()->ConfirmCompositionText(
-      /*reset_engine=*/false,
-      /*keep_selection=*/true);
+  ui::IMEInputContextHandlerInterface* input_context =
+      ui::IMEBridge::Get()->GetInputContextHandler();
+
+  input_context->ConfirmCompositionText(/*reset_engine=*/false,
+                                        /*keep_selection=*/true);
+
+  auto* manager = InputMethodManager::Get();
+  if (!manager ||
+      !extension_ime_util::IsExperimentalMultilingual(
+          manager->GetActiveIMEState()->GetCurrentInputMethod().id())) {
+    return;
+  }
+
+  std::u16string composition_text = input_context->GetCompositionText();
+  base::TrimWhitespace(composition_text, base::TRIM_ALL, &composition_text);
+  bool has_diacritics = HasDiacritics(composition_text);
+
+  base::UmaHistogramEnumeration(
+      "InputMethod.MultilingualExperiment.NonAutocorrect",
+      has_diacritics ? NonAutocorrectDiacriticStatus::kWithDiacritics
+                     : NonAutocorrectDiacriticStatus::kWithoutDiacritics);
 }
 
 void NativeInputMethodEngine::ImeObserver::DeleteSurroundingText(
diff --git a/chrome/browser/ash/input_method/native_input_method_engine.h b/chrome/browser/ash/input_method/native_input_method_engine.h
index 8d5e2317..237ef81 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine.h
+++ b/chrome/browser/ash/input_method/native_input_method_engine.h
@@ -46,6 +46,13 @@
   NativeInputMethodEngine();
   ~NativeInputMethodEngine() override;
 
+  // |use_ime_service| can be |false| in browser tests to avoid connecting to
+  // IME service which may try to load libimedecoder.so unsupported in tests.
+  // TODO(crbug/1197005): Migrate native_input_method_engine_browsertest suite
+  // to e2e Tast tests and unit tests, then dismantle this for-test-only flag.
+  static std::unique_ptr<NativeInputMethodEngine> CreateForTesting(
+      bool use_ime_service);
+
   // Used to override deps for testing.
   NativeInputMethodEngine(
       std::unique_ptr<AssistiveSuggesterSwitch> suggester_switch);
@@ -94,12 +101,18 @@
     // |ime_base_observer| is to forward events to extension during this
     // migration. It will be removed when the official extension is completely
     // migrated.
+    // |use_ime_service| should always be |true| in prod code, and may only be
+    // |false| in browser tests that need to avoid connecting to the Mojo IME
+    // service which can involve loading libimedecoder.so unsupported in tests.
+    // TODO(crbug/1197005): Migrate native_input_method_engine_browsertest suite
+    // to e2e Tast tests and unit tests, then dismantle this for-test-only flag.
     ImeObserver(PrefService* prefs,
                 std::unique_ptr<InputMethodEngineObserver> ime_base_observer,
                 std::unique_ptr<AssistiveSuggester> assistive_suggester,
                 std::unique_ptr<AutocorrectManager> autocorrect_manager,
                 std::unique_ptr<SuggestionsCollector> suggestions_collector,
-                std::unique_ptr<GrammarManager> grammar_manager);
+                std::unique_ptr<GrammarManager> grammar_manager,
+                bool use_ime_service);
     ~ImeObserver() override;
 
     // InputMethodEngineObserver:
@@ -188,6 +201,9 @@
     void SendSurroundingTextToNativeMojoEngine(
         const SurroundingText& surrounding_text);
 
+    bool ShouldRouteToRuleBasedEngine(const std::string& engine_id) const;
+    bool ShouldRouteToNativeMojoEngine(const std::string& engine_id) const;
+
     PrefService* prefs_ = nullptr;
 
     std::unique_ptr<InputMethodEngineObserver> ime_base_observer_;
@@ -203,12 +219,28 @@
     ui::CharacterComposer character_composer_;
 
     SurroundingText last_surrounding_text_;
+
+    // |use_ime_service| should always be |true| in prod code, and may only be
+    // |false| in browser tests that need to avoid connecting to the Mojo IME
+    // service which can involve loading libimedecoder.so unsupported in tests.
+    // TODO(crbug/1197005): Migrate native_input_method_engine_browsertest suite
+    // to e2e Tast tests and unit tests, then dismantle this for-test-only flag.
+    bool use_ime_service_ = true;
   };
 
+  // |use_ime_service| should always be |true| in prod code, and may only be
+  // |false| in browser tests that need to avoid connecting to the Mojo IME
+  // service which can involve loading libimedecoder.so unsupported in tests.
+  // TODO(crbug/1197005): Migrate native_input_method_engine_browsertest suite
+  // to e2e Tast tests and unit tests, then dismantle this for-test-only flag.
+  explicit NativeInputMethodEngine(bool use_ime_service);
+
   ImeObserver* GetNativeObserver() const;
 
   void OnInputMethodOptionsChanged() override;
 
+  bool ShouldRouteToNativeMojoEngine(const std::string& engine_id) const;
+
   AssistiveSuggester* assistive_suggester_ = nullptr;
   AutocorrectManager* autocorrect_manager_ = nullptr;
   base::ScopedObservation<ChromeKeyboardControllerClient,
@@ -217,6 +249,13 @@
 
   // Optional dependency overrides used in testing.
   std::unique_ptr<AssistiveSuggesterSwitch> suggester_switch_;
+
+  // |use_ime_service| should always be |true| in prod code, and may only be
+  // |false| in browser tests that need to avoid connecting to the Mojo IME
+  // service (which can involve loading libimedecoder.so unsupported in tests).
+  // TODO(crbug/1197005): Migrate native_input_method_engine_browsertest suite
+  // to e2e Tast tests and unit tests, then dismantle this for-test-only flag.
+  bool use_ime_service_ = true;
 };
 
 }  // namespace input_method
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_with_ime_service_browsertest.cc b/chrome/browser/ash/input_method/native_input_method_engine_with_ime_service_browsertest.cc
new file mode 100644
index 0000000..459b112
--- /dev/null
+++ b/chrome/browser/ash/input_method/native_input_method_engine_with_ime_service_browsertest.cc
@@ -0,0 +1,232 @@
+// 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 "chrome/browser/ash/input_method/native_input_method_engine.h"
+
+#include "base/values.h"
+#include "chrome/browser/ash/input_method/stub_input_method_engine_observer.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/test/browser_test.h"
+#include "mojo/core/embedder/embedder.h"
+#include "ui/base/ime/ash/ime_bridge.h"
+#include "ui/base/ime/ash/ime_engine_handler_interface.h"
+#include "ui/base/ime/ash/input_method_ash.h"
+#include "ui/base/ime/dummy_text_input_client.h"
+#include "ui/base/ime/input_method_delegate.h"
+
+namespace ash {
+namespace input_method {
+namespace {
+
+class TestObserver : public StubInputMethodEngineObserver {
+ public:
+  TestObserver() = default;
+  ~TestObserver() override = default;
+  TestObserver(const TestObserver&) = delete;
+  TestObserver& operator=(const TestObserver&) = delete;
+
+  void OnKeyEvent(
+      const std::string& engine_id,
+      const ui::KeyEvent& event,
+      ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback) override {
+    std::move(callback).Run(/*handled=*/false);
+  }
+};
+
+class KeyProcessingWaiter {
+ public:
+  ui::IMEEngineHandlerInterface::KeyEventDoneCallback CreateCallback() {
+    return base::BindOnce(&KeyProcessingWaiter::OnKeyEventDone,
+                          base::Unretained(this));
+  }
+
+  void OnKeyEventDone(bool consumed) { run_loop_.Quit(); }
+
+  void Wait() { run_loop_.Run(); }
+
+ private:
+  base::RunLoop run_loop_;
+};
+
+// These use the browser test framework but tamper with the environment through
+// global singletons, effectively bypassing CrOS IMF "input method management".
+// Test subject is a bespoke NativeInputMethodEngine instance manually attached
+// to the environment, shadowing those created and managed by CrOS IMF (an
+// integral part of the "browser" environment set up by the browser test).
+// TODO(crbug/1197005): Migrate all these to e2e Tast tests.
+class NativeInputMethodEngineWithImeServiceTest
+    : public InProcessBrowserTest,
+      public ui::internal::InputMethodDelegate {
+ public:
+  NativeInputMethodEngineWithImeServiceTest() : input_method_(this) {}
+
+ protected:
+  void SetUp() override {
+    mojo::core::Init();
+    InProcessBrowserTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    // Passing |true| for |use_ime_service| means NativeInputMethodEngine will
+    // launch the IME Service which typically tries to load libimedecoder.so
+    // unsupported in browser tests. However, it's luckily okay for these tests
+    // as they only test "m17n" whose implementation is directly in the IME
+    // Service container, thus not trigger libimedecoder.so loading attempt.
+    // TODO(crbug/1197005): Migrate to Tast to avoid reliance on delicate luck.
+    engine_ =
+        NativeInputMethodEngine::CreateForTesting(/*use_ime_service=*/true);
+    ui::IMEBridge::Get()->SetInputContextHandler(&input_method_);
+    ui::IMEBridge::Get()->SetCurrentEngineHandler(engine_.get());
+
+    auto observer = std::make_unique<TestObserver>();
+    Profile* profile = browser()->profile();
+    PrefService* prefs = profile->GetPrefs();
+    prefs->Set(::prefs::kLanguageInputMethodSpecificSettings,
+               base::DictionaryValue());
+    engine_->Initialize(std::move(observer), /*extension_id=*/"", profile);
+
+    InProcessBrowserTest::SetUpOnMainThread();
+  }
+
+  void TearDownOnMainThread() override {
+    // Reset the engine before shutting down the browser because the engine
+    // observes ChromeKeyboardControllerClient, which is tied to the browser
+    // lifetime.
+    engine_.reset();
+    ui::IMEBridge::Get()->SetInputContextHandler(nullptr);
+    ui::IMEBridge::Get()->SetCurrentEngineHandler(nullptr);
+    InProcessBrowserTest::TearDownOnMainThread();
+  }
+
+  // Overridden from ui::internal::InputMethodDelegate:
+  ui::EventDispatchDetails DispatchKeyEventPostIME(
+      ui::KeyEvent* event) override {
+    return ui::EventDispatchDetails();
+  }
+
+  void DispatchKeyPress(ui::KeyboardCode code,
+                        bool need_flush,
+                        int flags = ui::EF_NONE) {
+    KeyProcessingWaiter waiterPressed;
+    KeyProcessingWaiter waiterReleased;
+    engine_->ProcessKeyEvent({ui::ET_KEY_PRESSED, code, flags},
+                             waiterPressed.CreateCallback());
+    engine_->ProcessKeyEvent({ui::ET_KEY_RELEASED, code, flags},
+                             waiterReleased.CreateCallback());
+    if (need_flush)
+      engine_->FlushForTesting();
+
+    waiterPressed.Wait();
+    waiterReleased.Wait();
+  }
+
+  void SetFocus(ui::TextInputClient* client) {
+    input_method_.SetFocusedTextInputClient(client);
+  }
+
+  std::unique_ptr<NativeInputMethodEngine> engine_;
+
+ private:
+  ui::InputMethodAsh input_method_;
+};
+
+// ID is specified in google_xkb_manifest.json.
+constexpr char kEngineIdArabic[] = "vkd_ar";
+constexpr char kEngineIdVietnameseTelex[] = "vkd_vi_telex";
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithImeServiceTest,
+                       VietnameseTelex_SimpleTransform) {
+  engine_->Enable(kEngineIdVietnameseTelex);
+  engine_->FlushForTesting();
+  EXPECT_TRUE(engine_->IsConnectedForTesting());
+
+  // Create a fake text field.
+  ui::DummyTextInputClient text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
+  SetFocus(&text_input_client);
+
+  DispatchKeyPress(ui::VKEY_A, true, ui::EF_SHIFT_DOWN);
+  DispatchKeyPress(ui::VKEY_S, true);
+  DispatchKeyPress(ui::VKEY_SPACE, true);
+
+  // Expect to commit 'Á '.
+  ASSERT_EQ(text_input_client.composition_history().size(), 2U);
+  EXPECT_EQ(text_input_client.composition_history()[0].text, u"A");
+  EXPECT_EQ(text_input_client.composition_history()[1].text, u"\u00c1");
+  ASSERT_EQ(text_input_client.insert_text_history().size(), 1U);
+  EXPECT_EQ(text_input_client.insert_text_history()[0], u"\u00c1 ");
+
+  SetFocus(nullptr);
+}
+
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithImeServiceTest,
+                       VietnameseTelex_Reset) {
+  engine_->Enable(kEngineIdVietnameseTelex);
+  engine_->FlushForTesting();
+  EXPECT_TRUE(engine_->IsConnectedForTesting());
+
+  // Create a fake text field.
+  ui::DummyTextInputClient text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
+  SetFocus(&text_input_client);
+
+  DispatchKeyPress(ui::VKEY_A, true);
+  engine_->Reset();
+  DispatchKeyPress(ui::VKEY_S, true);
+
+  // Expect to commit 's'.
+  ASSERT_EQ(text_input_client.composition_history().size(), 1U);
+  EXPECT_EQ(text_input_client.composition_history()[0].text, u"a");
+  ASSERT_EQ(text_input_client.insert_text_history().size(), 1U);
+  EXPECT_EQ(text_input_client.insert_text_history()[0], u"s");
+
+  SetFocus(nullptr);
+}
+
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithImeServiceTest,
+                       SwitchActiveController) {
+  // Swap between two controllers.
+  engine_->Enable(kEngineIdVietnameseTelex);
+  engine_->FlushForTesting();
+  engine_->Disable();
+  engine_->Enable(kEngineIdArabic);
+  engine_->FlushForTesting();
+
+  // Create a fake text field.
+  ui::DummyTextInputClient text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
+  SetFocus(&text_input_client);
+
+  DispatchKeyPress(ui::VKEY_A, true);
+
+  // Expect to commit 'ش'.
+  ASSERT_EQ(text_input_client.composition_history().size(), 0U);
+  ASSERT_EQ(text_input_client.insert_text_history().size(), 1U);
+  EXPECT_EQ(text_input_client.insert_text_history()[0], u"ش");
+
+  SetFocus(nullptr);
+}
+
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithImeServiceTest,
+                       NoActiveController) {
+  engine_->Enable(kEngineIdVietnameseTelex);
+  engine_->FlushForTesting();
+  engine_->Disable();
+
+  // Create a fake text field.
+  ui::DummyTextInputClient text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
+  SetFocus(&text_input_client);
+
+  DispatchKeyPress(ui::VKEY_A, true);
+  engine_->Reset();
+
+  // Expect no changes.
+  ASSERT_EQ(text_input_client.composition_history().size(), 0U);
+  ASSERT_EQ(text_input_client.insert_text_history().size(), 0U);
+
+  SetFocus(nullptr);
+}
+
+}  // namespace input_method
+}  // namespace ash
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_browsertest.cc b/chrome/browser/ash/input_method/native_input_method_engine_without_ime_service_browsertest.cc
similarity index 88%
rename from chrome/browser/ash/input_method/native_input_method_engine_browsertest.cc
rename to chrome/browser/ash/input_method/native_input_method_engine_without_ime_service_browsertest.cc
index e294262e..c435af51 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine_browsertest.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine_without_ime_service_browsertest.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -35,7 +35,6 @@
 #include "components/signin/public/identity_manager/identity_test_utils.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
-#include "mojo/core/embedder/embedder.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/ime/ash/ime_bridge.h"
 #include "ui/base/ime/ash/ime_engine_handler_interface.h"
@@ -87,9 +86,7 @@
   ~TestPersonalDataManagerObserver() override = default;
 
   // Waits for the PersonalDataManager's list of profiles to be updated.
-  void Wait() {
-    run_loop_.Run();
-  }
+  void Wait() { run_loop_.Run(); }
 
   // PersonalDataManagerObserver:
   void OnPersonalDataChanged() override { run_loop_.Quit(); }
@@ -122,10 +119,11 @@
 // to the environment, shadowing those created and managed by CrOS IMF (an
 // integral part of the "browser" environment set up by the browser test).
 // TODO(crbug/1197005): Migrate all these to unit tests.
-class NativeInputMethodEngineTest : public InProcessBrowserTest,
-                                    public ui::internal::InputMethodDelegate {
+class NativeInputMethodEngineWithoutImeServiceTest
+    : public InProcessBrowserTest,
+      public ui::internal::InputMethodDelegate {
  public:
-  NativeInputMethodEngineTest() : input_method_(this) {
+  NativeInputMethodEngineWithoutImeServiceTest() : input_method_(this) {
     feature_list_.InitWithFeatures(
         /*enabled_features=*/{features::kAssistPersonalInfo,
                               features::kAssistPersonalInfoEmail,
@@ -136,13 +134,15 @@
   }
 
  protected:
-  void SetUp() override {
-    mojo::core::Init();
-    InProcessBrowserTest::SetUp();
-  }
+  void SetUp() override { InProcessBrowserTest::SetUp(); }
 
   void SetUpOnMainThread() override {
-    engine_ = std::make_unique<NativeInputMethodEngine>();
+    // Passing |false| for |use_ime_service| so NativeInputMethodEngine won't
+    // launch the IME Service which typically tries to load libimedecoder.so
+    // unsupported in browser tests. None of these tests require the IME Service
+    // so just avoid it outright instead of relying on implicit luck.
+    engine_ =
+        NativeInputMethodEngine::CreateForTesting(/*use_ime_service=*/false);
     ui::IMEBridge::Get()->SetInputContextHandler(&input_method_);
     ui::IMEBridge::Get()->SetCurrentEngineHandler(engine_.get());
 
@@ -239,100 +239,12 @@
 };
 
 // ID is specified in google_xkb_manifest.json.
-constexpr char kEngineIdArabic[] = "vkd_ar";
 constexpr char kEngineIdUs[] = "xkb:us::eng";
-constexpr char kEngineIdVietnameseTelex[] = "vkd_vi_telex";
 
 }  // namespace
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
-                       VietnameseTelex_SimpleTransform) {
-  engine_->Enable(kEngineIdVietnameseTelex);
-  engine_->FlushForTesting();
-  EXPECT_TRUE(engine_->IsConnectedForTesting());
-
-  // Create a fake text field.
-  ui::DummyTextInputClient text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
-  SetFocus(&text_input_client);
-
-  DispatchKeyPress(ui::VKEY_A, true, ui::EF_SHIFT_DOWN);
-  DispatchKeyPress(ui::VKEY_S, true);
-  DispatchKeyPress(ui::VKEY_SPACE, true);
-
-  // Expect to commit 'Á '.
-  ASSERT_EQ(text_input_client.composition_history().size(), 2U);
-  EXPECT_EQ(text_input_client.composition_history()[0].text, u"A");
-  EXPECT_EQ(text_input_client.composition_history()[1].text, u"\u00c1");
-  ASSERT_EQ(text_input_client.insert_text_history().size(), 1U);
-  EXPECT_EQ(text_input_client.insert_text_history()[0], u"\u00c1 ");
-
-  SetFocus(nullptr);
-}
-
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest, VietnameseTelex_Reset) {
-  engine_->Enable(kEngineIdVietnameseTelex);
-  engine_->FlushForTesting();
-  EXPECT_TRUE(engine_->IsConnectedForTesting());
-
-  // Create a fake text field.
-  ui::DummyTextInputClient text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
-  SetFocus(&text_input_client);
-
-  DispatchKeyPress(ui::VKEY_A, true);
-  engine_->Reset();
-  DispatchKeyPress(ui::VKEY_S, true);
-
-  // Expect to commit 's'.
-  ASSERT_EQ(text_input_client.composition_history().size(), 1U);
-  EXPECT_EQ(text_input_client.composition_history()[0].text, u"a");
-  ASSERT_EQ(text_input_client.insert_text_history().size(), 1U);
-  EXPECT_EQ(text_input_client.insert_text_history()[0], u"s");
-
-  SetFocus(nullptr);
-}
-
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest, SwitchActiveController) {
-  // Swap between two controllers.
-  engine_->Enable(kEngineIdVietnameseTelex);
-  engine_->FlushForTesting();
-  engine_->Disable();
-  engine_->Enable(kEngineIdArabic);
-  engine_->FlushForTesting();
-
-  // Create a fake text field.
-  ui::DummyTextInputClient text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
-  SetFocus(&text_input_client);
-
-  DispatchKeyPress(ui::VKEY_A, true);
-
-  // Expect to commit 'ش'.
-  ASSERT_EQ(text_input_client.composition_history().size(), 0U);
-  ASSERT_EQ(text_input_client.insert_text_history().size(), 1U);
-  EXPECT_EQ(text_input_client.insert_text_history()[0], u"ش");
-
-  SetFocus(nullptr);
-}
-
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest, NoActiveController) {
-  engine_->Enable(kEngineIdVietnameseTelex);
-  engine_->FlushForTesting();
-  engine_->Disable();
-
-  // Create a fake text field.
-  ui::DummyTextInputClient text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
-  SetFocus(&text_input_client);
-
-  DispatchKeyPress(ui::VKEY_A, true);
-  engine_->Reset();
-
-  // Expect no changes.
-  ASSERT_EQ(text_input_client.composition_history().size(), 0U);
-  ASSERT_EQ(text_input_client.insert_text_history().size(), 0U);
-
-  SetFocus(nullptr);
-}
-
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest, SuggestUserEmail) {
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
+                       SuggestUserEmail) {
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectTotalCount(
       "InputMethod.Assistive.TimeToAccept.PersonalInfo", 0);
@@ -374,7 +286,7 @@
   SetFocus(nullptr);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        DoesntSuggestWhenTheCursorIsWithinGrammarError) {
   ui::MockIMEInputContextHandler mock_ime_input_context_handler;
   ui::IMEBridge::Get()->SetInputContextHandler(&mock_ime_input_context_handler);
@@ -407,7 +319,7 @@
   SetFocus(nullptr);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        SuggestsWhenTheCursorIsOutsideGrammarError) {
   ui::MockIMEInputContextHandler mock_ime_input_context_handler;
   ui::IMEBridge::Get()->SetInputContextHandler(&mock_ime_input_context_handler);
@@ -441,7 +353,7 @@
   SetFocus(nullptr);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        DismissPersonalInfoSuggestion) {
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectTotalCount(
@@ -485,7 +397,8 @@
   SetFocus(nullptr);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest, SuggestUserName) {
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
+                       SuggestUserName) {
   base::HistogramTester histogram_tester;
 
   TestPersonalDataManagerObserver personal_data_observer(profile_);
@@ -537,7 +450,7 @@
   SetFocus(nullptr);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        PersonalInfoDisabledReasonkUserSettingsOff) {
   base::HistogramTester histogram_tester;
   prefs_->SetBoolean(prefs::kAssistPersonalInfoEnabled, false);
@@ -551,7 +464,7 @@
       DisabledReason::kUserSettingsOff, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        PersonalInfoDisabledReasonkUrlOrAppNotAllowed) {
   base::HistogramTester histogram_tester;
 
@@ -572,7 +485,8 @@
                                       AssistiveType::kPersonalName, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest, SuggestEmoji) {
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
+                       SuggestEmoji) {
   base::HistogramTester histogram_tester;
   engine_->Enable(kEngineIdUs);
   TextInputTestHelper helper(GetBrowserInputMethod());
@@ -602,7 +516,7 @@
   SetFocus(nullptr);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        DismissEmojiSuggestionWhenUsersContinueTyping) {
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectTotalCount("InputMethod.Assistive.TimeToDismiss.Emoji",
@@ -629,7 +543,7 @@
   SetFocus(nullptr);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        EmojiSuggestionDisabledReasonkEnterpriseSettingsOff) {
   base::HistogramTester histogram_tester;
   prefs_->SetBoolean(prefs::kEmojiSuggestionEnterpriseAllowed, false);
@@ -643,7 +557,7 @@
                                       1);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        EmojiSuggestionDisabledReasonkUserSettingsOff) {
   base::HistogramTester histogram_tester;
   prefs_->SetBoolean(prefs::kEmojiSuggestionEnabled, false);
@@ -656,7 +570,7 @@
                                       DisabledReason::kUserSettingsOff, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        EmojiSuggestionDisabledReasonkUrlOrAppNotAllowed) {
   base::HistogramTester histogram_tester;
 
@@ -669,7 +583,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(
-    NativeInputMethodEngineTest,
+    NativeInputMethodEngineWithoutImeServiceTest,
     OnLearnMoreButtonClickedOpensEmojiSuggestionSettingsPage) {
   base::UserActionTester user_action_tester;
   ui::ime::AssistiveWindowButton button;
@@ -683,7 +597,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(
-    NativeInputMethodEngineTest,
+    NativeInputMethodEngineWithoutImeServiceTest,
     OnSettingLinkButtonClickedOpensPersonalInfoSuggestionSettingsPage) {
   base::UserActionTester user_action_tester;
   ui::ime::AssistiveWindowButton button;
@@ -696,7 +610,7 @@
                 "ChromeOS.Settings.SmartInputs.PersonalInfoSuggestions.Open"));
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        FiresOnInputMethodOptionsChangedEvent) {
   base::DictionaryValue settings;
 
@@ -716,13 +630,14 @@
   EXPECT_EQ(observer_->changed_engine_id(), "pinyin");
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest, DestroyProfile) {
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
+                       DestroyProfile) {
   EXPECT_NE(engine_->GetPrefChangeRegistrarForTesting(), nullptr);
   profile_->MaybeSendDestroyedNotification();
   EXPECT_EQ(engine_->GetPrefChangeRegistrarForTesting(), nullptr);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        HighlightsOnAutocorrectThenDismissesHighlight) {
   engine_->Enable(kEngineIdUs);
   ui::DummyTextInputClient text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
@@ -760,7 +675,7 @@
   SetFocus(nullptr);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        ShowsAndHidesAutocorrectUndoWindow) {
   engine_->Enable(kEngineIdUs);
   TextInputTestHelper helper(GetBrowserInputMethod());
@@ -789,7 +704,8 @@
   SetFocus(nullptr);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest, RevertsAutocorrect) {
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
+                       RevertsAutocorrect) {
   engine_->Enable(kEngineIdUs);
   TextInputTestHelper helper(GetBrowserInputMethod());
   SetUpTextInput(helper);
@@ -828,7 +744,7 @@
   SetFocus(nullptr);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        RevertsAutocorrectWithKeyboard) {
   engine_->Enable(kEngineIdUs);
 
@@ -868,7 +784,7 @@
   SetFocus(nullptr);
 }
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        SendsAutocorrectMetricsforUnderline) {
   base::HistogramTester histogram_tester;
   engine_->Enable(kEngineIdUs);
@@ -918,7 +834,7 @@
 }
 
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        SendsMetricsForExperimentalMultilingual) {
   base::HistogramTester histogram_tester;
 
@@ -1015,7 +931,7 @@
 #endif
 
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineTest,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceTest,
                        SendsDiacriticalMetricsForExperimentalMultilingual) {
   base::HistogramTester histogram_tester;
 
@@ -1117,18 +1033,24 @@
 // to the environment, shadowing those created and managed by CrOS IMF (an
 // integral part of the "browser" environment set up by the browser test).
 // TODO(crbug/1197005): Migrate all these to unit tests.
-class NativeInputMethodEngineAssistiveOff : public InProcessBrowserTest {
+class NativeInputMethodEngineWithoutImeServiceAssistiveOff
+    : public InProcessBrowserTest {
  public:
-  NativeInputMethodEngineAssistiveOff() {
+  NativeInputMethodEngineWithoutImeServiceAssistiveOff() {
     feature_list_.InitWithFeatures(
         /*enabled_features=*/{features::kAssistPersonalInfoName},
         /*disabled_features=*/{features::kAssistPersonalInfo});
   }
-  ~NativeInputMethodEngineAssistiveOff() override = default;
+  ~NativeInputMethodEngineWithoutImeServiceAssistiveOff() override = default;
 
  protected:
   void SetUpOnMainThread() override {
-    engine_ = std::make_unique<NativeInputMethodEngine>();
+    // Passing |false| for |use_ime_service| so NativeInputMethodEngine won't
+    // launch the IME Service which typically tries to load libimedecoder.so
+    // unsupported in browser tests. None of these tests require the IME Service
+    // so just avoid it outright instead of relying on implicit luck.
+    engine_ =
+        NativeInputMethodEngine::CreateForTesting(/*use_ime_service=*/false);
     ui::IMEBridge::Get()->SetCurrentEngineHandler(engine_.get());
 
     auto observer = std::make_unique<TestObserver>();
@@ -1157,7 +1079,7 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineAssistiveOff,
+IN_PROC_BROWSER_TEST_F(NativeInputMethodEngineWithoutImeServiceAssistiveOff,
                        PersonalInfoSuggestionDisabledReasonkFeatureFlagOff) {
   base::HistogramTester histogram_tester;
 
diff --git a/chrome/browser/ash/lock_screen_apps/first_app_run_toast_manager.cc b/chrome/browser/ash/lock_screen_apps/first_app_run_toast_manager.cc
index 6ff2960..0729551 100644
--- a/chrome/browser/ash/lock_screen_apps/first_app_run_toast_manager.cc
+++ b/chrome/browser/ash/lock_screen_apps/first_app_run_toast_manager.cc
@@ -78,9 +78,8 @@
   DCHECK(app_window->GetNativeWindow());
 
   const extensions::Extension* app = app_window->GetExtension();
-  const base::DictionaryValue* toast_shown =
-      profile_->GetPrefs()->GetDictionary(
-          prefs::kNoteTakingAppsLockScreenToastShown);
+  const base::Value* toast_shown = profile_->GetPrefs()->GetDictionary(
+      prefs::kNoteTakingAppsLockScreenToastShown);
   if (toast_shown->FindBoolPath(app->id()).value_or(false)) {
     return;
   }
diff --git a/chrome/browser/ash/lock_screen_apps/state_controller.cc b/chrome/browser/ash/lock_screen_apps/state_controller.cc
index 9cf2e07..2dc31e8 100644
--- a/chrome/browser/ash/lock_screen_apps/state_controller.cc
+++ b/chrome/browser/ash/lock_screen_apps/state_controller.cc
@@ -13,6 +13,7 @@
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/path_service.h"
 #include "base/time/default_tick_clock.h"
@@ -30,7 +31,9 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user.h"
+#include "content/public/browser/lock_screen_storage.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
 #include "crypto/symmetric_key.h"
 #include "extensions/browser/api/lock_screen_data/lock_screen_item_storage.h"
 #include "extensions/browser/app_window/app_delegate.h"
@@ -149,6 +152,14 @@
   }
 
   InitializeWithCryptoKey(profile, key);
+  if (base::FeatureList::IsEnabled(features::kWebLockScreenApi)) {
+    base::FilePath base_path;
+    base::PathService::Get(chrome::DIR_USER_DATA, &base_path);
+    base_path = base_path.AppendASCII("web_lock_screen_api_data");
+    base_path = base_path.Append(
+        chromeos::ProfileHelper::GetUserIdHashFromProfile(profile));
+    content::LockScreenStorage::GetInstance()->Init(profile, base_path);
+  }
 }
 
 void StateController::Shutdown() {
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.h b/chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.h
index baf93cf..4f397c2 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.h
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.h
@@ -11,8 +11,6 @@
 #include <memory>
 #include <string>
 
-// TODO(https://crbug.com/1164001): move to forward declaration
-#include "ash/components/login/auth/user_context.h"
 #include "base/callback.h"
 #include "base/containers/circular_deque.h"
 #include "base/memory/weak_ptr.h"
@@ -29,6 +27,8 @@
 
 namespace ash {
 
+class UserContext;
+
 // A class to manage Easy unlock cryptohome keys.
 class EasyUnlockKeyManager {
  public:
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc
index 5c33631..85e1962 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc
@@ -226,7 +226,7 @@
   if (!local_state)
     return false;
 
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       local_state->GetDictionary(prefs::kEasyUnlockHardlockState);
   if (!dict)
     return false;
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc
index 40d75792..cbf5776f 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc
@@ -306,8 +306,8 @@
 }
 
 const base::ListValue* EasyUnlockServiceRegular::GetRemoteDevices() const {
-  const base::DictionaryValue* pairing_dict =
-      profile()->GetPrefs()->GetDictionary(prefs::kEasyUnlockPairing);
+  const base::DictionaryValue* pairing_dict = &base::Value::AsDictionaryValue(
+      *profile()->GetPrefs()->GetDictionary(prefs::kEasyUnlockPairing));
   const base::ListValue* devices = NULL;
   if (pairing_dict && pairing_dict->GetList(kKeyDevices, &devices))
     return devices;
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager.cc
index 51b8068..29434dd 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager.cc
@@ -262,7 +262,7 @@
     const AccountId& account_id) {
   if (!local_state_)
     return std::string();
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       local_state_->GetDictionary(prefs::kEasyUnlockLocalStateTpmKeys);
   const std::string* key = nullptr;
   if (dict)
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_user_login_flow.h b/chrome/browser/ash/login/easy_unlock/easy_unlock_user_login_flow.h
index 440041f..bd610d2 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_user_login_flow.h
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_user_login_flow.h
@@ -5,14 +5,14 @@
 #ifndef CHROME_BROWSER_ASH_LOGIN_EASY_UNLOCK_EASY_UNLOCK_USER_LOGIN_FLOW_H_
 #define CHROME_BROWSER_ASH_LOGIN_EASY_UNLOCK_EASY_UNLOCK_USER_LOGIN_FLOW_H_
 
-// TODO(https://crbug.com/1164001): move to forward declaration
-#include "ash/components/login/auth/user_context.h"
 #include "chrome/browser/ash/login/user_flow.h"
 
 class AccountId;
 
 namespace ash {
 
+class UserContext;
+
 // Handler for login flow initiazted by Easy Signin login attempt.
 // The only difference to the default login flow is hanlding of the auth
 // failure.
diff --git a/chrome/browser/ash/login/enterprise_user_session_metrics.h b/chrome/browser/ash/login/enterprise_user_session_metrics.h
index 11aa14e..6512445 100644
--- a/chrome/browser/ash/login/enterprise_user_session_metrics.h
+++ b/chrome/browser/ash/login/enterprise_user_session_metrics.h
@@ -5,14 +5,15 @@
 #ifndef CHROME_BROWSER_ASH_LOGIN_ENTERPRISE_USER_SESSION_METRICS_H_
 #define CHROME_BROWSER_ASH_LOGIN_ENTERPRISE_USER_SESSION_METRICS_H_
 
-// TODO(https://crbug.com/1164001): move to forward declaration.h
-#include "ash/components/login/auth/user_context.h"
 #include "base/time/time.h"
 #include "components/user_manager/user_type.h"
 
 class PrefRegistrySimple;
 
 namespace ash {
+
+class UserContext;
+
 namespace enterprise_user_session_metrics {
 
 // Enum for logins metrics on an enrolled device.
diff --git a/chrome/browser/ash/login/existing_user_controller.cc b/chrome/browser/ash/login/existing_user_controller.cc
index 2fff492..0231b9f 100644
--- a/chrome/browser/ash/login/existing_user_controller.cc
+++ b/chrome/browser/ash/login/existing_user_controller.cc
@@ -1024,7 +1024,7 @@
 
   profile_prepared_ = true;
 
-  chromeos::UserContext user_context =
+  UserContext user_context =
       UserContext(*ProfileHelper::Get()->GetUserByProfile(profile));
   auto* profile_connector = profile->GetProfilePolicyConnector();
   bool is_enterprise_managed =
diff --git a/chrome/browser/ash/login/hwid_checker.cc b/chrome/browser/ash/login/hwid_checker.cc
index 05fbd0f..bdb9b56 100644
--- a/chrome/browser/ash/login/hwid_checker.cc
+++ b/chrome/browser/ash/login/hwid_checker.cc
@@ -9,6 +9,7 @@
 #include "ash/constants/ash_switches.h"
 #include "base/command_line.h"
 #include "base/logging.h"
+#include "base/notreached.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/system/sys_info.h"
@@ -150,8 +151,16 @@
 
 bool IsMachineHWIDCorrect() {
   base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
-  if (cmd_line->HasSwitch(switches::kForceHWIDCheckFailureForTest))
-    return false;
+  if (cmd_line->HasSwitch(switches::kForceHWIDCheckResultForTest)) {
+    const std::string check_result =
+        cmd_line->GetSwitchValueASCII(switches::kForceHWIDCheckResultForTest);
+    if (check_result == "failure")
+      return false;
+    if (check_result == "success")
+      return true;
+    NOTREACHED() << "Wrong " << switches::kForceHWIDCheckResultForTest
+                 << "value: " << check_result;
+  }
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   if (cmd_line->HasSwitch(::switches::kTestType))
     return true;
diff --git a/chrome/browser/ash/login/lock/screen_locker.cc b/chrome/browser/ash/login/lock/screen_locker.cc
index 5ada7555..de84e20b8 100644
--- a/chrome/browser/ash/login/lock/screen_locker.cc
+++ b/chrome/browser/ash/login/lock/screen_locker.cc
@@ -481,8 +481,7 @@
   }
 }
 
-void ScreenLocker::ContinueAuthenticate(
-    const chromeos::UserContext& user_context) {
+void ScreenLocker::ContinueAuthenticate(const UserContext& user_context) {
   if (user_context.GetAccountId().GetAccountType() ==
           AccountType::ACTIVE_DIRECTORY &&
       user_context.GetKey()->GetKeyType() == Key::KEY_TYPE_PASSWORD_PLAIN) {
@@ -561,8 +560,7 @@
   return users_to_show;
 }
 
-void ScreenLocker::SetLoginStatusConsumer(
-    chromeos::AuthStatusConsumer* consumer) {
+void ScreenLocker::SetLoginStatusConsumer(AuthStatusConsumer* consumer) {
   auth_status_consumer_ = consumer;
 }
 
diff --git a/chrome/browser/ash/login/lock/screen_locker.h b/chrome/browser/ash/login/lock/screen_locker.h
index 535b222..341fc3d7 100644
--- a/chrome/browser/ash/login/lock/screen_locker.h
+++ b/chrome/browser/ash/login/lock/screen_locker.h
@@ -11,11 +11,7 @@
 #include <vector>
 
 #include "ash/components/login/auth/auth_status_consumer.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/authenticator.h"
 #include "ash/components/login/auth/challenge_response_key.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/extended_authenticator.h"
 #include "ash/components/login/auth/user_context.h"
 #include "ash/public/cpp/login_types.h"
 #include "base/callback_forward.h"
@@ -39,6 +35,9 @@
 class PrefChangeRegistrar;
 
 namespace ash {
+
+class Authenticator;
+class ExtendedAuthenticator;
 class ViewsScreenLocker;
 
 // ScreenLocker displays the lock UI and takes care of authenticating the user
@@ -87,7 +86,7 @@
   void Init();
 
   // AuthStatusConsumer:
-  void OnAuthFailure(const chromeos::AuthFailure& error) override;
+  void OnAuthFailure(const AuthFailure& error) override;
   void OnAuthSuccess(const UserContext& user_context) override;
 
   // Called when an account password (not PIN/quick unlock) has been used to
@@ -141,7 +140,7 @@
 
   // Allow a AuthStatusConsumer to listen for
   // the same login events that ScreenLocker does.
-  void SetLoginStatusConsumer(chromeos::AuthStatusConsumer* consumer);
+  void SetLoginStatusConsumer(AuthStatusConsumer* consumer);
 
   // Initialize or uninitialize the ScreenLocker class. It observes
   // SessionManager so that the screen locker accepts lock requests only after a
diff --git a/chrome/browser/ash/login/lock/views_screen_locker.cc b/chrome/browser/ash/login/lock/views_screen_locker.cc
index fbd2da1..0e24be5 100644
--- a/chrome/browser/ash/login/lock/views_screen_locker.cc
+++ b/chrome/browser/ash/login/lock/views_screen_locker.cc
@@ -114,7 +114,7 @@
   DCHECK(user);
   UserContext user_context(*user);
   user_context.SetKey(
-      Key(chromeos::Key::KEY_TYPE_PASSWORD_PLAIN, std::string(), password));
+      Key(Key::KEY_TYPE_PASSWORD_PLAIN, std::string(), password));
   user_context.SetIsUsingPin(authenticated_by_pin);
   user_context.SetSyncPasswordData(password_manager::PasswordHashData(
       account_id.GetUserEmail(), base::UTF8ToUTF16(password),
diff --git a/chrome/browser/ash/login/lock_screen_utils.cc b/chrome/browser/ash/login/lock_screen_utils.cc
index 9ad9314..e837166 100644
--- a/chrome/browser/ash/login/lock_screen_utils.cc
+++ b/chrome/browser/ash/login/lock_screen_utils.cc
@@ -95,7 +95,7 @@
 
   // Try to use old values.
   PrefService* const local_state = g_browser_process->local_state();
-  const base::DictionaryValue* users_last_input_methods =
+  const base::Value* users_last_input_methods =
       local_state->GetDictionary(::prefs::kUsersLastInputMethod);
 
   if (!users_last_input_methods) {
diff --git a/chrome/browser/ash/login/login_manager_test.h b/chrome/browser/ash/login/login_manager_test.h
index 8c7c08a..b794625 100644
--- a/chrome/browser/ash/login/login_manager_test.h
+++ b/chrome/browser/ash/login/login_manager_test.h
@@ -7,8 +7,6 @@
 
 #include <string>
 
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/user_context.h"
 #include "chrome/browser/ash/login/test/embedded_test_server_setup_mixin.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 
@@ -16,6 +14,8 @@
 
 namespace ash {
 
+class UserContext;
+
 // Base class for Chrome OS Login tests. Should be used if you need to start at
 // the Chrome OS Login screen (especially with existing users). For the tests
 // that are focused more on OOBE - prefer OobeBaseTest. Use LoginManagerMixin to
diff --git a/chrome/browser/ash/login/quick_unlock/auth_token.cc b/chrome/browser/ash/login/quick_unlock/auth_token.cc
index 43379b8..420933aa 100644
--- a/chrome/browser/ash/login/quick_unlock/auth_token.cc
+++ b/chrome/browser/ash/login/quick_unlock/auth_token.cc
@@ -14,10 +14,10 @@
 
 const int AuthToken::kTokenExpirationSeconds = 5 * 60;
 
-AuthToken::AuthToken(const chromeos::UserContext& user_context)
+AuthToken::AuthToken(const UserContext& user_context)
     : identifier_(base::UnguessableToken::Create()),
       creation_time_(base::TimeTicks::Now()),
-      user_context_(std::make_unique<chromeos::UserContext>(user_context)) {
+      user_context_(std::make_unique<UserContext>(user_context)) {
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE, base::BindOnce(&AuthToken::Reset, weak_factory_.GetWeakPtr()),
       base::Seconds(kTokenExpirationSeconds));
diff --git a/chrome/browser/ash/login/quick_unlock/auth_token.h b/chrome/browser/ash/login/quick_unlock/auth_token.h
index b2eb310..f139053 100644
--- a/chrome/browser/ash/login/quick_unlock/auth_token.h
+++ b/chrome/browser/ash/login/quick_unlock/auth_token.h
@@ -12,11 +12,10 @@
 #include "base/unguessable_token.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-namespace chromeos {
-class UserContext;
-}
-
 namespace ash {
+
+class UserContext;
+
 namespace quick_unlock {
 
 // Security token with an identifyier string and a predetermined life time,
@@ -28,7 +27,7 @@
   // How long the token lives.
   static const int kTokenExpirationSeconds;
 
-  explicit AuthToken(const chromeos::UserContext& user_context);
+  explicit AuthToken(const UserContext& user_context);
 
   AuthToken(const AuthToken&) = delete;
   AuthToken& operator=(const AuthToken&) = delete;
@@ -43,9 +42,7 @@
   absl::optional<base::TimeDelta> GetAge() const;
 
   // The UserContext returned here can be null if Reset() was called.
-  const chromeos::UserContext* user_context() const {
-    return user_context_.get();
-  }
+  const UserContext* user_context() const { return user_context_.get(); }
 
  private:
   friend class QuickUnlockStorageUnitTest;
@@ -56,7 +53,7 @@
 
   base::UnguessableToken identifier_;
   base::TimeTicks creation_time_;
-  std::unique_ptr<chromeos::UserContext> user_context_;
+  std::unique_ptr<UserContext> user_context_;
 
   base::WeakPtrFactory<AuthToken> weak_factory_{this};
 };
diff --git a/chrome/browser/ash/login/quick_unlock/pin_backend.cc b/chrome/browser/ash/login/quick_unlock/pin_backend.cc
index 2747139..6290611 100644
--- a/chrome/browser/ash/login/quick_unlock/pin_backend.cc
+++ b/chrome/browser/ash/login/quick_unlock/pin_backend.cc
@@ -85,7 +85,7 @@
     return pin;
 
   Key key(pin);
-  key.Transform(chromeos::Key::KEY_TYPE_SALTED_PBKDF2_AES256_1234, salt);
+  key.Transform(Key::KEY_TYPE_SALTED_PBKDF2_AES256_1234, salt);
   return key.GetSecret();
 }
 
diff --git a/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.h b/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.h
index 74e6576c..d04b058 100644
--- a/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.h
+++ b/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.h
@@ -6,9 +6,8 @@
 #define CHROME_BROWSER_ASH_LOGIN_QUICK_UNLOCK_PIN_STORAGE_CRYPTOHOME_H_
 
 #include <string>
+#include <vector>
 
-// TODO(https://crbug.com/1164001): move to forward declaration
-#include "ash/components/login/auth/user_context.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -16,6 +15,10 @@
 class AccountId;
 
 namespace ash {
+
+class Key;
+class UserContext;
+
 namespace quick_unlock {
 
 class PinStorageCryptohome {
diff --git a/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome_unittest.cc b/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome_unittest.cc
index 068589c6..7669153 100644
--- a/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome_unittest.cc
+++ b/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome_unittest.cc
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "ash/components/login/auth/cryptohome_key_constants.h"
+#include "ash/components/login/auth/user_context.h"
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/run_loop.h"
@@ -18,6 +19,7 @@
 #include "chromeos/dbus/cryptohome/rpc.pb.h"
 #include "chromeos/dbus/userdataauth/fake_cryptohome_misc_client.h"
 #include "chromeos/dbus/userdataauth/fake_userdataauth_client.h"
+#include "components/account_id/account_id.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/ash/login/quick_unlock/quick_unlock_storage.cc b/chrome/browser/ash/login/quick_unlock/quick_unlock_storage.cc
index 3cabae4..5cabf37 100644
--- a/chrome/browser/ash/login/quick_unlock/quick_unlock_storage.cc
+++ b/chrome/browser/ash/login/quick_unlock/quick_unlock_storage.cc
@@ -70,7 +70,7 @@
 }
 
 std::string QuickUnlockStorage::CreateAuthToken(
-    const chromeos::UserContext& user_context) {
+    const UserContext& user_context) {
   auth_token_ = std::make_unique<AuthToken>(user_context);
   DCHECK(auth_token_->Identifier().has_value());
   return *auth_token_->Identifier();
diff --git a/chrome/browser/ash/login/quick_unlock/quick_unlock_storage.h b/chrome/browser/ash/login/quick_unlock/quick_unlock_storage.h
index 7bf69db..08aec02 100644
--- a/chrome/browser/ash/login/quick_unlock/quick_unlock_storage.h
+++ b/chrome/browser/ash/login/quick_unlock/quick_unlock_storage.h
@@ -61,7 +61,7 @@
   // Creates a new authentication token to be used by the quickSettingsPrivate
   // API for authenticating requests. Resets the expiration timer and
   // invalidates any previously issued tokens.
-  std::string CreateAuthToken(const chromeos::UserContext& user_context);
+  std::string CreateAuthToken(const UserContext& user_context);
 
   // Returns true if the current authentication token has expired.
   bool GetAuthTokenExpired();
diff --git a/chrome/browser/ash/login/quick_unlock/quick_unlock_storage_unittest.cc b/chrome/browser/ash/login/quick_unlock/quick_unlock_storage_unittest.cc
index c9226ab..d196127b 100644
--- a/chrome/browser/ash/login/quick_unlock/quick_unlock_storage_unittest.cc
+++ b/chrome/browser/ash/login/quick_unlock/quick_unlock_storage_unittest.cc
@@ -175,7 +175,7 @@
       QuickUnlockFactory::GetForProfile(profile_.get());
   EXPECT_FALSE(quick_unlock_storage->GetAuthToken());
 
-  chromeos::UserContext context;
+  UserContext context;
   std::string auth_token = quick_unlock_storage->CreateAuthToken(context);
   EXPECT_NE(std::string(), auth_token);
   EXPECT_TRUE(quick_unlock_storage->GetAuthToken());
diff --git a/chrome/browser/ash/login/quick_unlock/quick_unlock_utils.cc b/chrome/browser/ash/login/quick_unlock/quick_unlock_utils.cc
index a945d41..26c1695d 100644
--- a/chrome/browser/ash/login/quick_unlock/quick_unlock_utils.cc
+++ b/chrome/browser/ash/login/quick_unlock/quick_unlock_utils.cc
@@ -40,7 +40,7 @@
 constexpr int kDefaultMinimumPinLength = 6;
 
 bool HasPolicyValue(const PrefService* pref_service, const char* value) {
-  const base::ListValue* quick_unlock_allowlist =
+  const base::Value* quick_unlock_allowlist =
       pref_service->GetList(prefs::kQuickUnlockModeAllowlist);
   // TODO(crbug.com/1187106): Use base::Contains once |quick_unlock_allowlist|
   // is not a ListValue.
diff --git a/chrome/browser/ash/login/reporting/login_logout_reporter.cc b/chrome/browser/ash/login/reporting/login_logout_reporter.cc
index f54b8e8..4b57d25 100644
--- a/chrome/browser/ash/login/reporting/login_logout_reporter.cc
+++ b/chrome/browser/ash/login/reporting/login_logout_reporter.cc
@@ -33,8 +33,7 @@
          type == policy::DeviceLocalAccount::TYPE_SAML_PUBLIC_SESSION;
 }
 
-LoginFailureReason GetLoginFailureReasonForReport(
-    const chromeos::AuthFailure& error) {
+LoginFailureReason GetLoginFailureReasonForReport(const AuthFailure& error) {
   switch (error.reason()) {
     case AuthFailure::OWNER_REQUIRED:
       return LoginFailureReason::OWNER_REQUIRED;
@@ -151,7 +150,7 @@
   MaybeReportEvent(std::move(record), user->GetAccountId());
 }
 
-void LoginLogoutReporter::OnLoginFailure(const chromeos::AuthFailure& error) {
+void LoginLogoutReporter::OnLoginFailure(const AuthFailure& error) {
   AccountId account_id = delegate_->GetLastLoginAttemptAccountId();
   if (account_id == EmptyAccountId()) {
     return;
diff --git a/chrome/browser/ash/login/reporting/login_logout_reporter.h b/chrome/browser/ash/login/reporting/login_logout_reporter.h
index 32a3cdc..fc18b14 100644
--- a/chrome/browser/ash/login/reporting/login_logout_reporter.h
+++ b/chrome/browser/ash/login/reporting/login_logout_reporter.h
@@ -56,7 +56,7 @@
       std::unique_ptr<Delegate> delegate);
 
   // Report user device failed login attempt.
-  void OnLoginFailure(const chromeos::AuthFailure& error) override;
+  void OnLoginFailure(const AuthFailure& error) override;
 
   // Report user device successful login.
   void OnLogin(Profile* profile) override;
diff --git a/chrome/browser/ash/login/reporting/login_logout_reporter_unittest.cc b/chrome/browser/ash/login/reporting/login_logout_reporter_unittest.cc
index 22e7cbd..365d32e 100644
--- a/chrome/browser/ash/login/reporting/login_logout_reporter_unittest.cc
+++ b/chrome/browser/ash/login/reporting/login_logout_reporter_unittest.cc
@@ -380,7 +380,7 @@
       std::move(reporter_helper),
       std::make_unique<LoginLogoutReporterTestDelegate>(
           AccountId::FromUserEmail(std::string(user_email))));
-  reporter->OnLoginFailure(chromeos::AuthFailure(AuthFailure::OWNER_REQUIRED));
+  reporter->OnLoginFailure(AuthFailure(AuthFailure::OWNER_REQUIRED));
 
   EXPECT_THAT(priority, testing::Eq(::reporting::Priority::SECURITY));
   EXPECT_TRUE(record.has_event_timestamp_sec());
@@ -424,7 +424,7 @@
       std::make_unique<LoginLogoutReporterTestDelegate>(
           AccountId::FromUserEmail(std::string(user_email))));
   reporter->OnLoginFailure(
-      chromeos::AuthFailure(AuthFailure::COULD_NOT_MOUNT_CRYPTOHOME));
+      AuthFailure(AuthFailure::COULD_NOT_MOUNT_CRYPTOHOME));
 
   EXPECT_THAT(priority, testing::Eq(::reporting::Priority::SECURITY));
   EXPECT_TRUE(record.has_event_timestamp_sec());
@@ -467,7 +467,7 @@
       std::move(reporter_helper),
       std::make_unique<LoginLogoutReporterTestDelegate>(
           AccountId::FromUserEmail(std::string(user_email))));
-  reporter->OnLoginFailure(chromeos::AuthFailure(AuthFailure::TPM_ERROR));
+  reporter->OnLoginFailure(AuthFailure(AuthFailure::TPM_ERROR));
 
   EXPECT_THAT(priority, testing::Eq(::reporting::Priority::SECURITY));
   EXPECT_TRUE(record.has_event_timestamp_sec());
@@ -508,8 +508,7 @@
       std::make_unique<LoginLogoutReporterTestDelegate>(
           AccountId::FromUserEmail(GenerateDeviceLocalAccountUserId(
               "guest", policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION))));
-  reporter->OnLoginFailure(
-      chromeos::AuthFailure(AuthFailure::COULD_NOT_MOUNT_TMPFS));
+  reporter->OnLoginFailure(AuthFailure(AuthFailure::COULD_NOT_MOUNT_TMPFS));
 
   EXPECT_THAT(priority, testing::Eq(::reporting::Priority::SECURITY));
   EXPECT_TRUE(record.has_event_timestamp_sec());
diff --git a/chrome/browser/ash/login/saml/in_session_password_change_manager.h b/chrome/browser/ash/login/saml/in_session_password_change_manager.h
index e66c3a94..a966bcaf 100644
--- a/chrome/browser/ash/login/saml/in_session_password_change_manager.h
+++ b/chrome/browser/ash/login/saml/in_session_password_change_manager.h
@@ -9,10 +9,6 @@
 #include <string>
 
 #include "ash/components/login/auth/auth_status_consumer.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/cryptohome_authenticator.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/user_context.h"
 #include "ash/public/cpp/session/session_activation_observer.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/observer_list.h"
@@ -27,6 +23,9 @@
 
 namespace ash {
 
+class CryptohomeAuthenticator;
+class UserContext;
+
 // There is at most one instance of this task, which is part of the
 // InSessionPasswordChangeManager singleton. Having a separate class means that
 // pointers to this class can be invalidated without affecting the manager.
diff --git a/chrome/browser/ash/login/saml/in_session_password_sync_manager.cc b/chrome/browser/ash/login/saml/in_session_password_sync_manager.cc
index 2d3ad13..dc95021 100644
--- a/chrome/browser/ash/login/saml/in_session_password_sync_manager.cc
+++ b/chrome/browser/ash/login/saml/in_session_password_sync_manager.cc
@@ -233,8 +233,7 @@
 
 // TODO(crbug.com/1163777): Add UMA histograms for lockscreen online
 // re-authentication.
-void InSessionPasswordSyncManager::OnAuthFailure(
-    const chromeos::AuthFailure& error) {
+void InSessionPasswordSyncManager::OnAuthFailure(const AuthFailure& error) {
   password_changed_callback_.Run();
 }
 
diff --git a/chrome/browser/ash/login/saml/in_session_password_sync_manager.h b/chrome/browser/ash/login/saml/in_session_password_sync_manager.h
index 1fa3d80..8f516ba 100644
--- a/chrome/browser/ash/login/saml/in_session_password_sync_manager.h
+++ b/chrome/browser/ash/login/saml/in_session_password_sync_manager.h
@@ -9,10 +9,6 @@
 #include <string>
 
 #include "ash/components/login/auth/auth_status_consumer.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/cryptohome_authenticator.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/extended_authenticator.h"
 #include "ash/components/login/auth/user_context.h"
 #include "ash/components/proximity_auth/screenlock_bridge.h"
 #include "base/callback_forward.h"
@@ -31,6 +27,9 @@
 
 namespace ash {
 
+class CryptohomeAuthenticator;
+class ExtendedAuthenticator;
+
 using PasswordChangedCallback = ::base::RepeatingClosure;
 
 // Manages SAML password sync for multiple customer devices. Handles online
@@ -100,7 +99,7 @@
 
   // AuthStatusConsumer:
   // Shows password changed dialog.
-  void OnAuthFailure(const chromeos::AuthFailure& error) override;
+  void OnAuthFailure(const AuthFailure& error) override;
 
   // Unlocks the screen if active user successfully verified the password
   // with an IdP.
diff --git a/chrome/browser/ash/login/screens/active_directory_login_screen.h b/chrome/browser/ash/login/screens/active_directory_login_screen.h
index 02249482..e41cde5 100644
--- a/chrome/browser/ash/login/screens/active_directory_login_screen.h
+++ b/chrome/browser/ash/login/screens/active_directory_login_screen.h
@@ -8,8 +8,6 @@
 #include <memory>
 #include <string>
 
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/key.h"
 #include "base/callback.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/ash/authpolicy/authpolicy_helper.h"
@@ -21,6 +19,8 @@
 
 namespace ash {
 
+class Key;
+
 // Controller for the active directory login screen.
 class ActiveDirectoryLoginScreen
     : public BaseScreen,
diff --git a/chrome/browser/ash/login/screens/active_directory_password_change_screen.h b/chrome/browser/ash/login/screens/active_directory_password_change_screen.h
index 7ff1170..0f4f4d50e 100644
--- a/chrome/browser/ash/login/screens/active_directory_password_change_screen.h
+++ b/chrome/browser/ash/login/screens/active_directory_password_change_screen.h
@@ -8,8 +8,6 @@
 #include <memory>
 #include <string>
 
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/key.h"
 #include "chrome/browser/ash/authpolicy/authpolicy_helper.h"
 #include "chrome/browser/ash/login/screen_manager.h"
 #include "chrome/browser/ash/login/screens/base_screen.h"
@@ -22,6 +20,8 @@
 
 namespace ash {
 
+class Key;
+
 // Controller for the active directory password change screen.
 class ActiveDirectoryPasswordChangeScreen : public BaseScreen {
  public:
diff --git a/chrome/browser/ash/login/screens/encryption_migration_screen.h b/chrome/browser/ash/login/screens/encryption_migration_screen.h
index 7f101936..eed3fac 100644
--- a/chrome/browser/ash/login/screens/encryption_migration_screen.h
+++ b/chrome/browser/ash/login/screens/encryption_migration_screen.h
@@ -27,6 +27,8 @@
 
 namespace ash {
 
+class UserContext;
+
 class EncryptionMigrationScreen : public BaseScreen,
                                   public PowerManagerClient::Observer,
                                   public UserDataAuthClient::Observer {
diff --git a/chrome/browser/ash/login/screens/pin_setup_screen.cc b/chrome/browser/ash/login/screens/pin_setup_screen.cc
index b55155e6..cc98e5d 100644
--- a/chrome/browser/ash/login/screens/pin_setup_screen.cc
+++ b/chrome/browser/ash/login/screens/pin_setup_screen.cc
@@ -185,8 +185,8 @@
       std::move(context()->extra_factors_auth_session);
 
   // Due to crbug.com/1203420 we need to mark the key as a wildcard (no label).
-  if (user_context->GetKey()->GetLabel() == chromeos::kCryptohomeGaiaKeyLabel) {
-    user_context->GetKey()->SetLabel(chromeos::kCryptohomeWildcardLabel);
+  if (user_context->GetKey()->GetLabel() == kCryptohomeGaiaKeyLabel) {
+    user_context->GetKey()->SetLabel(kCryptohomeWildcardLabel);
   }
 
   const std::string token =
diff --git a/chrome/browser/ash/login/screens/wrong_hwid_screen_browsertest.cc b/chrome/browser/ash/login/screens/wrong_hwid_screen_browsertest.cc
index 1dcabf3..096c813 100644
--- a/chrome/browser/ash/login/screens/wrong_hwid_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/wrong_hwid_screen_browsertest.cc
@@ -22,7 +22,8 @@
   ~WrongHWIDScreenTest() override = default;
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(switches::kForceHWIDCheckFailureForTest);
+    command_line->AppendSwitchASCII(switches::kForceHWIDCheckResultForTest,
+                                    "failure");
     OobeBaseTest::SetUpCommandLine(command_line);
   }
 };
diff --git a/chrome/browser/ash/login/session/user_session_manager.cc b/chrome/browser/ash/login/session/user_session_manager.cc
index 0913bbb..e0910008 100644
--- a/chrome/browser/ash/login/session/user_session_manager.cc
+++ b/chrome/browser/ash/login/session/user_session_manager.cc
@@ -619,7 +619,7 @@
       authenticator_ = injected_authenticator_builder_->Create(consumer);
     } else if (base::FeatureList::IsEnabled(
                    ash::features::kUseAuthsessionAuthentication)) {
-      authenticator_ = new chromeos::AuthSessionAuthenticator(
+      authenticator_ = new AuthSessionAuthenticator(
           consumer, std::make_unique<ChromeSafeModeDelegate>());
     } else {
       authenticator_ =
diff --git a/chrome/browser/ash/login/session/user_session_manager.h b/chrome/browser/ash/login/session/user_session_manager.h
index c6d680e..60e0f31 100644
--- a/chrome/browser/ash/login/session/user_session_manager.h
+++ b/chrome/browser/ash/login/session/user_session_manager.h
@@ -13,11 +13,7 @@
 #include <vector>
 
 #include "ash/components/arc/net/always_on_vpn_manager.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/auth_status_consumer.h"
 #include "ash/components/login/auth/authenticator.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/stub_authenticator_builder.h"
 #include "ash/components/login/auth/user_context.h"
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
@@ -64,8 +60,10 @@
 }  // namespace user_manager
 
 namespace ash {
+class AuthStatusConsumer;
 class LoginDisplayHost;
 class OnboardingUserActivityCounter;
+class StubAuthenticatorBuilder;
 class TokenHandleFetcher;
 
 namespace test {
diff --git a/chrome/browser/ash/login/signin/oauth2_browsertest.cc b/chrome/browser/ash/login/signin/oauth2_browsertest.cc
index c702fa7..fe8fe10 100644
--- a/chrome/browser/ash/login/signin/oauth2_browsertest.cc
+++ b/chrome/browser/ash/login/signin/oauth2_browsertest.cc
@@ -356,7 +356,7 @@
   user_manager::User::OAuthTokenStatus GetOAuthStatusFromLocalState(
       const std::string& email) const {
     PrefService* local_state = g_browser_process->local_state();
-    const base::DictionaryValue* prefs_oauth_status =
+    const base::Value* prefs_oauth_status =
         local_state->GetDictionary("OAuthTokenStatus");
     if (!prefs_oauth_status)
       return user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN;
diff --git a/chrome/browser/ash/login/test/login_manager_mixin.h b/chrome/browser/ash/login/test/login_manager_mixin.h
index 76befd9..661227b 100644
--- a/chrome/browser/ash/login/test/login_manager_mixin.h
+++ b/chrome/browser/ash/login/test/login_manager_mixin.h
@@ -8,8 +8,6 @@
 #include <memory>
 #include <vector>
 
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/stub_authenticator_builder.h"
 #include "ash/components/login/auth/user_context.h"
 #include "chrome/browser/ash/login/test/fake_gaia_mixin.h"
 #include "chrome/browser/ash/login/test/local_state_mixin.h"
@@ -28,6 +26,8 @@
 
 }  // namespace test
 
+class StubAuthenticatorBuilder;
+
 // Mixin browser tests can use for setting up test login manager environment.
 // It sets up command line so test starts on the login screen UI, and
 // initializes user manager with a list of pre-registered users.
diff --git a/chrome/browser/ash/login/test/profile_prepared_waiter.h b/chrome/browser/ash/login/test/profile_prepared_waiter.h
index af53f5a56..ed8d832d 100644
--- a/chrome/browser/ash/login/test/profile_prepared_waiter.h
+++ b/chrome/browser/ash/login/test/profile_prepared_waiter.h
@@ -22,7 +22,7 @@
   ~ProfilePreparedWaiter() override;
 
   // AuthStatusConsumer
-  void OnAuthSuccess(const chromeos::UserContext& user_context) override;
+  void OnAuthSuccess(const UserContext& user_context) override;
   void OnAuthFailure(const AuthFailure& error) override;
 
   void Wait();
diff --git a/chrome/browser/ash/login/ui/fake_login_display_host.h b/chrome/browser/ash/login/ui/fake_login_display_host.h
index fdfd4ca..ada319b 100644
--- a/chrome/browser/ash/login/ui/fake_login_display_host.h
+++ b/chrome/browser/ash/login/ui/fake_login_display_host.h
@@ -47,7 +47,7 @@
   void OnPreferencesChanged() override;
   void StartKiosk(const KioskAppId& kiosk_app_id, bool is_auto_launch) override;
   void AttemptShowEnableConsumerKioskScreen() override;
-  void CompleteLogin(const chromeos::UserContext& user_context) override;
+  void CompleteLogin(const UserContext& user_context) override;
   void OnGaiaScreenReady() override;
   void SetDisplayEmail(const std::string& email) override;
   void SetDisplayAndGivenName(const std::string& display_name,
diff --git a/chrome/browser/ash/login/ui/login_display.h b/chrome/browser/ash/login/ui/login_display.h
index 4a66bee6..395a65ba4 100644
--- a/chrome/browser/ash/login/ui/login_display.h
+++ b/chrome/browser/ash/login/ui/login_display.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_H_
 #define CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_H_
 
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/user_context.h"
 #include "base/callback.h"
 #include "chrome/browser/ash/login/help_app_launcher.h"
 #include "chrome/browser/ash/login/signin_specifics.h"
@@ -18,6 +16,8 @@
 
 namespace ash {
 
+class UserContext;
+
 // TODO(nkostylev): Extract interface, create a BaseLoginDisplay class.
 // An abstract class that defines login UI implementation.
 class LoginDisplay {
diff --git a/chrome/browser/ash/login/ui/login_display_host_mojo.cc b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
index cd18a45e..e39bde2 100644
--- a/chrome/browser/ash/login/ui/login_display_host_mojo.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
@@ -518,8 +518,7 @@
   DCHECK(user);
   UserContext user_context(*user);
   user_context.SetIsUsingPin(authenticated_by_pin);
-  user_context.SetKey(
-      Key(chromeos::Key::KEY_TYPE_PASSWORD_PLAIN, "" /*salt*/, password));
+  user_context.SetKey(Key(Key::KEY_TYPE_PASSWORD_PLAIN, "" /*salt*/, password));
   user_context.SetPasswordKey(Key(password));
   user_context.SetLoginInputMethodIdUsed(input_method::InputMethodManager::Get()
                                              ->GetActiveIMEState()
diff --git a/chrome/browser/ash/login/user_flow.h b/chrome/browser/ash/login/user_flow.h
index e14a997..6deead12 100644
--- a/chrome/browser/ash/login/user_flow.h
+++ b/chrome/browser/ash/login/user_flow.h
@@ -6,13 +6,13 @@
 #define CHROME_BROWSER_ASH_LOGIN_USER_FLOW_H_
 
 #include "ash/components/login/auth/auth_status_consumer.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/user_context.h"
 #include "components/account_id/account_id.h"
 #include "components/user_manager/user.h"
 
 namespace ash {
 
+class UserContext;
+
 // Defines possible variants of user flow upon logging in.
 // See UserManager::SetUserFlow for usage contract.
 class UserFlow {
diff --git a/chrome/browser/ash/login/users/avatar/user_image_manager_browsertest.cc b/chrome/browser/ash/login/users/avatar/user_image_manager_browsertest.cc
index 6350c73..576e8fd 100644
--- a/chrome/browser/ash/login/users/avatar/user_image_manager_browsertest.cc
+++ b/chrome/browser/ash/login/users/avatar/user_image_manager_browsertest.cc
@@ -223,7 +223,8 @@
                            int image_index,
                            const base::FilePath& image_path) {
     const base::DictionaryValue* images_pref =
-        local_state_->GetDictionary(UserImageManagerImpl::kUserImageProperties);
+        &base::Value::AsDictionaryValue(*local_state_->GetDictionary(
+            UserImageManagerImpl::kUserImageProperties));
     ASSERT_TRUE(images_pref);
     const base::DictionaryValue* image_properties = NULL;
     images_pref->GetDictionaryWithoutPathExpansion(account_id.GetUserEmail(),
diff --git a/chrome/browser/ash/login/users/avatar/user_image_manager_impl.cc b/chrome/browser/ash/login/users/avatar/user_image_manager_impl.cc
index 2865f0a..5d98c0f7 100644
--- a/chrome/browser/ash/login/users/avatar/user_image_manager_impl.cc
+++ b/chrome/browser/ash/login/users/avatar/user_image_manager_impl.cc
@@ -417,8 +417,8 @@
   // Because the user ID (i.e. email address) contains '.', the code here
   // cannot use the dots notation (path expantion) hence is verbose.
   PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* prefs_images =
-      local_state->GetDictionary(kUserImageProperties);
+  const base::DictionaryValue* prefs_images = &base::Value::AsDictionaryValue(
+      *local_state->GetDictionary(kUserImageProperties));
   if (prefs_images) {
     const base::DictionaryValue* image_properties = nullptr;
     prefs_images->GetDictionaryWithoutPathExpansion(account_id().GetUserEmail(),
@@ -493,8 +493,8 @@
 
 void UserImageManagerImpl::LoadUserImage() {
   PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* prefs_images =
-      local_state->GetDictionary(kUserImageProperties);
+  const base::DictionaryValue* prefs_images = &base::Value::AsDictionaryValue(
+      *local_state->GetDictionary(kUserImageProperties));
   if (!prefs_images)
     return;
   user_manager::User* user = GetUserAndModify();
diff --git a/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc b/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc
index 28a8767..234b021a 100644
--- a/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc
+++ b/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc
@@ -166,7 +166,8 @@
 
 bool UserImageSyncObserver::GetSyncedImageIndex(int* index) {
   *index = user_manager::User::USER_IMAGE_INVALID;
-  const base::DictionaryValue* dict = prefs_->GetDictionary(kUserImageInfo);
+  const base::DictionaryValue* dict =
+      &base::Value::AsDictionaryValue(*prefs_->GetDictionary(kUserImageInfo));
   return dict && dict->GetInteger(kImageIndex, index);
 }
 
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
index 3bafe11f..21c2a46 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
@@ -718,7 +718,8 @@
 void ChromeUserManagerImpl::LoadDeviceLocalAccounts(
     std::set<AccountId>* device_local_accounts_set) {
   const base::ListValue* prefs_device_local_accounts =
-      GetLocalState()->GetList(kDeviceLocalAccountsWithSavedData);
+      &base::Value::AsListValue(
+          *GetLocalState()->GetList(kDeviceLocalAccountsWithSavedData));
   std::vector<AccountId> device_local_accounts;
   ParseUserList(*prefs_device_local_accounts, std::set<AccountId>(),
                 &device_local_accounts, device_local_accounts_set);
@@ -1302,7 +1303,7 @@
 }
 
 bool ChromeUserManagerImpl::ShouldReportUser(const std::string& user_id) const {
-  const base::ListValue& reporting_users =
+  const base::Value& reporting_users =
       *(GetLocalState()->GetList(::prefs::kReportingUsers));
   base::Value user_id_value(FullyCanonicalize(user_id));
   // TODO(crbug.com/1187106): Use base::Contains once |reporting_users| is not a
diff --git a/chrome/browser/ash/login/users/multi_profile_user_controller.cc b/chrome/browser/ash/login/users/multi_profile_user_controller.cc
index ee7cb61..fc0a51ed 100644
--- a/chrome/browser/ash/login/users/multi_profile_user_controller.cc
+++ b/chrome/browser/ash/login/users/multi_profile_user_controller.cc
@@ -168,7 +168,7 @@
 
 std::string MultiProfileUserController::GetCachedValue(
     const std::string& user_email) const {
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       local_state_->GetDictionary(prefs::kCachedMultiProfileUserBehavior);
   if (!dict)
     return std::string(kBehaviorUnrestricted);
diff --git a/chrome/browser/ash/login/users/supervised_user_manager_impl.cc b/chrome/browser/ash/login/users/supervised_user_manager_impl.cc
index 8f61dc7..b82a2b9 100644
--- a/chrome/browser/ash/login/users/supervised_user_manager_impl.cc
+++ b/chrome/browser/ash/login/users/supervised_user_manager_impl.cc
@@ -110,7 +110,7 @@
 std::u16string SupervisedUserManagerImpl::GetManagerDisplayName(
     const std::string& user_id) const {
   PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* manager_names =
+  const base::Value* manager_names =
       local_state->GetDictionary(kSupervisedUserManagerNames);
   const std::string* result = manager_names->FindStringKey(user_id);
   if (result && !result->empty())
@@ -190,7 +190,7 @@
     const char* key,
     std::string* out_value) const {
   PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* dictionary = local_state->GetDictionary(key);
+  const base::Value* dictionary = local_state->GetDictionary(key);
   const std::string* value = dictionary->FindStringKey(user_id);
   if (!value)
     return false;
@@ -203,7 +203,7 @@
                                                     const char* key,
                                                     int* out_value) const {
   PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* dictionary = local_state->GetDictionary(key);
+  const base::Value* dictionary = local_state->GetDictionary(key);
   absl::optional<int> value = dictionary->FindIntKey(user_id);
   if (!value)
     return false;
@@ -216,7 +216,7 @@
                                                     const char* key,
                                                     bool* out_value) const {
   PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* dictionary = local_state->GetDictionary(key);
+  const base::Value* dictionary = local_state->GetDictionary(key);
   absl::optional<bool> flag = dictionary->FindBoolKey(user_id);
   if (!flag)
     return false;
diff --git a/chrome/browser/ash/login/wizard_context.h b/chrome/browser/ash/login/wizard_context.h
index 06542ca..b3824492 100644
--- a/chrome/browser/ash/login/wizard_context.h
+++ b/chrome/browser/ash/login/wizard_context.h
@@ -7,13 +7,13 @@
 
 #include <memory>
 
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "ash/components/login/auth/user_context.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
 
 namespace ash {
 
+class UserContext;
+
 // Structure that defines data that need to be passed between screens during
 // WizardController flows.
 class WizardContext {
diff --git a/chrome/browser/ash/login/wizard_controller.cc b/chrome/browser/ash/login/wizard_controller.cc
index 7135e5a3..75e7d52 100644
--- a/chrome/browser/ash/login/wizard_controller.cc
+++ b/chrome/browser/ash/login/wizard_controller.cc
@@ -1181,8 +1181,8 @@
   switch (result) {
     case GuestTosScreen::Result::ACCEPT:
       ash::LoginDisplayHost::default_host()->GetExistingUserController()->Login(
-          chromeos::UserContext(user_manager::USER_TYPE_GUEST,
-                                user_manager::GuestAccountId()),
+          UserContext(user_manager::USER_TYPE_GUEST,
+                      user_manager::GuestAccountId()),
           chromeos::SigninSpecifics());
       break;
     case GuestTosScreen::Result::BACK:
diff --git a/chrome/browser/ash/net/network_throttling_observer.cc b/chrome/browser/ash/net/network_throttling_observer.cc
index d9fb401..301e4066 100644
--- a/chrome/browser/ash/net/network_throttling_observer.cc
+++ b/chrome/browser/ash/net/network_throttling_observer.cc
@@ -40,7 +40,8 @@
   DCHECK(pref_name == prefs::kNetworkThrottlingEnabled);
 
   const base::DictionaryValue* throttling_policy =
-      local_state_->GetDictionary(prefs::kNetworkThrottlingEnabled);
+      &base::Value::AsDictionaryValue(
+          *local_state_->GetDictionary(prefs::kNetworkThrottlingEnabled));
 
   // Default is to disable throttling if the policy is not found.
   bool enabled = false;
diff --git a/chrome/browser/ash/platform_keys/key_permissions/key_permissions_pref_util.cc b/chrome/browser/ash/platform_keys/key_permissions/key_permissions_pref_util.cc
index 4e8c12a..f25b074 100644
--- a/chrome/browser/ash/platform_keys/key_permissions/key_permissions_pref_util.cc
+++ b/chrome/browser/ash/platform_keys/key_permissions/key_permissions_pref_util.cc
@@ -39,7 +39,7 @@
   if (!profile_prefs)
     return nullptr;
 
-  const base::DictionaryValue* platform_keys =
+  const base::Value* platform_keys =
       profile_prefs->GetDictionary(prefs::kPlatformKeys);
   if (!platform_keys)
     return nullptr;
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_diagnostics.cc b/chrome/browser/ash/plugin_vm/plugin_vm_diagnostics.cc
index 89173f0..cf4b4a6 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_diagnostics.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_diagnostics.cc
@@ -123,7 +123,7 @@
       switch (is_allowed_diagnostics.policy_configured) {
         case PolicyConfigured::kOk: {
           // Additional check for image policy. See b/185281662#comment2.
-          const base::DictionaryValue* image_policy =
+          const base::Value* image_policy =
               active_profile_->GetPrefs()->GetDictionary(prefs::kPluginVmImage);
           const base::Value* url =
               image_policy->FindKey(prefs::kPluginVmImageUrlKeyName);
diff --git a/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc b/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
index 6f6518a1..7fd1306 100644
--- a/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
+++ b/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
@@ -190,8 +190,8 @@
   const user_manager::UserType user_type =
       is_active_directory ? user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY
                           : user_manager::UserType::USER_TYPE_REGULAR;
-  chromeos::UserContext user_context(user_type, account_id);
-  user_context.SetKey(chromeos::Key("password"));
+  ash::UserContext user_context(user_type, account_id);
+  user_context.SetKey(ash::Key("password"));
   if (account_id.GetUserEmail() == kEnterpriseUserEmail) {
     user_context.SetRefreshToken(kFakeRefreshToken);
   }
diff --git a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
index 40c86e2..d05e643 100644
--- a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
+++ b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
@@ -659,8 +659,8 @@
     auto* controller = ash::ExistingUserController::current_controller();
     ASSERT_TRUE(controller);
 
-    chromeos::UserContext user_context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
-                                       account_id_1_);
+    ash::UserContext user_context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
+                                  account_id_1_);
     user_context.SetPublicSessionLocale(locale);
     user_context.SetPublicSessionInputMethod(input_method);
     controller->Login(user_context, ash::SigninSpecifics());
@@ -1481,8 +1481,8 @@
   EXPECT_FALSE(user->HasDefaultImage());
   EXPECT_EQ(user_manager::User::USER_IMAGE_EXTERNAL, user->image_index());
   EXPECT_TRUE(ash::test::AreImagesEqual(policy_image, user->GetImage()));
-  const base::DictionaryValue* images_pref =
-      g_browser_process->local_state()->GetDictionary("user_image_info");
+  const base::DictionaryValue* images_pref = &base::Value::AsDictionaryValue(
+      *g_browser_process->local_state()->GetDictionary("user_image_info"));
   ASSERT_TRUE(images_pref);
   const base::DictionaryValue* image_properties;
   ASSERT_TRUE(images_pref->GetDictionaryWithoutPathExpansion(
diff --git a/chrome/browser/ash/policy/core/device_policy_cros_browser_test.cc b/chrome/browser/ash/policy/core/device_policy_cros_browser_test.cc
index cd129316..228ad8a 100644
--- a/chrome/browser/ash/policy/core/device_policy_cros_browser_test.cc
+++ b/chrome/browser/ash/policy/core/device_policy_cros_browser_test.cc
@@ -107,7 +107,7 @@
 DictionaryLocalStateValueWaiter::~DictionaryLocalStateValueWaiter() {}
 
 bool DictionaryLocalStateValueWaiter::ExpectedValueFound() {
-  const base::DictionaryValue* pref =
+  const base::Value* pref =
       pref_change_registrar_.prefs()->GetDictionary(pref_.c_str());
   if (!pref) {
     // Can't use ASSERT_* in non-void functions so this is the next best
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc
index 1cb29c1..58d04e9 100644
--- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc
+++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc
@@ -113,6 +113,9 @@
       GetAreaConfidentialContentsInfo(area, DlpContentRestriction::kScreenshot);
   MaybeReportEvent(info.restriction_info,
                    DlpRulesManager::Restriction::kScreenshot);
+  if (IsWarn(info.restriction_info))
+    ReportWarningEvent(info.restriction_info.url,
+                       DlpRulesManager::Restriction::kScreenshot);
   DlpBooleanHistogram(dlp::kScreenshotBlockedUMA,
                       IsBlocked(info.restriction_info));
   // TODO(crbug.com/1252736): Properly handle WARN for screenshots API.
@@ -166,8 +169,18 @@
   // before, warn the user before saving the file.
   if (running_video_capture_info_.has_value() &&
       !running_video_capture_info_->confidential_contents.IsEmpty()) {
+    const GURL& url =
+        running_video_capture_info_->confidential_contents.GetContents()
+            .begin()
+            ->url;
+
+    ReportWarningEvent(url, DlpRulesManager::Restriction::kScreenshot);
+
+    auto reporting_callback = base::BindOnce(
+        &MaybeReportWarningProceededEvent, url,
+        DlpRulesManager::Restriction::kScreenshot, reporting_manager_);
     warn_notifier_->ShowDlpVideoCaptureWarningDialog(
-        std::move(callback),
+        std::move(reporting_callback).Then(std::move(callback)),
         running_video_capture_info_->confidential_contents);
   } else {
     std::move(callback).Run(/*proceed=*/true);
@@ -768,16 +781,26 @@
                           DlpRulesManager::Restriction::kScreenshot);
     if (info.confidential_contents.IsEmpty()) {
       // The user already allowed all the visible content.
+      ReportWarningProceededEvent(info.restriction_info.url,
+                                  DlpRulesManager::Restriction::kScreenshot,
+                                  reporting_manager_);
       std::move(callback).Run(true);
       return;
     }
+
+    ReportWarningEvent(info.restriction_info.url,
+                       DlpRulesManager::Restriction::kScreenshot);
+
+    auto reporting_callback = base::BindOnce(
+        &MaybeReportWarningProceededEvent, info.restriction_info.url,
+        DlpRulesManager::Restriction::kScreenshot, reporting_manager_);
     // base::Unretained(this) is safe here because DlpContentManagerAsh is
     // initialized as a singleton that's always available in the system.
     warn_notifier_->ShowDlpScreenCaptureWarningDialog(
         base::BindOnce(&DlpContentManagerAsh::OnDlpWarnDialogReply,
                        base::Unretained(this), info.confidential_contents,
                        DlpRulesManager::Restriction::kScreenshot,
-                       std::move(callback)),
+                       std::move(reporting_callback).Then(std::move(callback))),
         info.confidential_contents);
     return;
   }
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc
index ca690d6..f6c1f5a 100644
--- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc
+++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc
@@ -233,6 +233,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DlpContentManagerAshBrowserTest, ScreenshotsWarned) {
+  SetupReporting();
   DlpContentManagerAsh* manager = helper_->GetContentManager();
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kExampleUrl)));
   content::WebContents* web_contents =
@@ -256,12 +257,16 @@
   EXPECT_FALSE(manager->IsScreenshotApiRestricted(window));
   EXPECT_FALSE(manager->IsScreenshotApiRestricted(partial_in));
   EXPECT_FALSE(manager->IsScreenshotApiRestricted(partial_out));
+  CheckEvents(DlpRulesManager::Restriction::kScreenshot,
+              DlpRulesManager::Level::kWarn, 0u);
 
   helper_->ChangeConfidentiality(web_contents, kScreenshotWarned);
   EXPECT_TRUE(manager->IsScreenshotApiRestricted(fullscreen));
   EXPECT_TRUE(manager->IsScreenshotApiRestricted(window));
   EXPECT_TRUE(manager->IsScreenshotApiRestricted(partial_in));
   EXPECT_FALSE(manager->IsScreenshotApiRestricted(partial_out));
+  CheckEvents(DlpRulesManager::Restriction::kScreenshot,
+              DlpRulesManager::Level::kWarn, 3u);
 
   web_contents->WasHidden();
   helper_->ChangeVisibility(web_contents);
@@ -269,6 +274,8 @@
   EXPECT_TRUE(manager->IsScreenshotApiRestricted(window));
   EXPECT_FALSE(manager->IsScreenshotApiRestricted(partial_in));
   EXPECT_FALSE(manager->IsScreenshotApiRestricted(partial_out));
+  CheckEvents(DlpRulesManager::Restriction::kScreenshot,
+              DlpRulesManager::Level::kWarn, 4u);
 
   web_contents->WasShown();
   helper_->ChangeVisibility(web_contents);
@@ -276,11 +283,15 @@
   EXPECT_TRUE(manager->IsScreenshotApiRestricted(window));
   EXPECT_TRUE(manager->IsScreenshotApiRestricted(partial_in));
   EXPECT_FALSE(manager->IsScreenshotApiRestricted(partial_out));
+  CheckEvents(DlpRulesManager::Restriction::kScreenshot,
+              DlpRulesManager::Level::kWarn, 7u);
 
   helper_->DestroyWebContents(web_contents);
   EXPECT_FALSE(manager->IsScreenshotApiRestricted(fullscreen));
   EXPECT_FALSE(manager->IsScreenshotApiRestricted(partial_in));
   EXPECT_FALSE(manager->IsScreenshotApiRestricted(partial_out));
+  CheckEvents(DlpRulesManager::Restriction::kScreenshot,
+              DlpRulesManager::Level::kWarn, 7u);
 }
 
 IN_PROC_BROWSER_TEST_F(DlpContentManagerAshBrowserTest, ScreenshotsReported) {
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_unittest.cc b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_unittest.cc
index ef44788..23dcd37 100644
--- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_unittest.cc
+++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_unittest.cc
@@ -592,8 +592,7 @@
                   DlpRulesManager::Level::kWarn)));
   EXPECT_THAT(events_[1],
               IsDlpPolicyEvent(CreateDlpPolicyWarningProceededEvent(
-                  kSrcPattern, DlpRulesManager::Restriction::kPrinting,
-                  DlpRulesManager::Level::kWarn)));
+                  kSrcPattern, DlpRulesManager::Restriction::kPrinting)));
 
   // Check again: allow based on cached user's response - no dialog is shown.
   GetManager()->CheckPrintingRestriction(
@@ -605,8 +604,7 @@
   EXPECT_EQ(events_.size(), 3u);
   EXPECT_THAT(events_[2],
               IsDlpPolicyEvent(CreateDlpPolicyWarningProceededEvent(
-                  kSrcPattern, DlpRulesManager::Restriction::kPrinting,
-                  DlpRulesManager::Level::kWarn)));
+                  kSrcPattern, DlpRulesManager::Restriction::kPrinting)));
 
   // Web contents are destroyed: allow, no dialog is shown.
   helper_.DestroyWebContents(web_contents.get());
@@ -741,6 +739,12 @@
   // The warning should be shown only once.
   EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(1);
 
+  SetReportQueueForReportingManager();
+  SetupDlpRulesManager();
+  EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern)
+      .Times(3)
+      .WillRepeatedly(::testing::Return(kSrcPattern));
+
   // No restrictions are enforced: allow.
   std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
   EXPECT_EQ(GetManager()->GetConfidentialRestrictions(web_contents.get()),
@@ -750,6 +754,7 @@
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
   VerifyAndResetActionAllowed(true /*expected*/);
   EXPECT_FALSE(helper_.HasAnyContentCached());
+  EXPECT_TRUE(events_.empty());
 
   // Warn restriction is enforced: allow and remember that the user proceeded.
   helper_.ChangeConfidentiality(web_contents.get(), kScreenshotWarned);
@@ -760,6 +765,14 @@
   VerifyAndResetActionAllowed(true /*expected*/);
   EXPECT_TRUE(helper_.HasContentCachedForRestriction(
       web_contents.get(), DlpRulesManager::Restriction::kScreenshot));
+  EXPECT_EQ(events_.size(), 2u);
+  EXPECT_THAT(events_[0],
+              IsDlpPolicyEvent(CreateDlpPolicyEvent(
+                  kSrcPattern, DlpRulesManager::Restriction::kScreenshot,
+                  DlpRulesManager::Level::kWarn)));
+  EXPECT_THAT(events_[1],
+              IsDlpPolicyEvent(CreateDlpPolicyWarningProceededEvent(
+                  kSrcPattern, DlpRulesManager::Restriction::kScreenshot)));
 
   // Check again: allow based on cached user's response - no dialog is shown.
   GetManager()->CheckCaptureModeInitRestriction(
@@ -767,6 +780,10 @@
   VerifyAndResetActionAllowed(true /*expected*/);
   EXPECT_TRUE(helper_.HasContentCachedForRestriction(
       web_contents.get(), DlpRulesManager::Restriction::kScreenshot));
+  EXPECT_EQ(events_.size(), 3u);
+  EXPECT_THAT(events_[2],
+              IsDlpPolicyEvent(CreateDlpPolicyWarningProceededEvent(
+                  kSrcPattern, DlpRulesManager::Restriction::kScreenshot)));
 
   // Web contents are destroyed: allow, no dialog is shown.
   helper_.DestroyWebContents(web_contents.get());
@@ -775,6 +792,7 @@
   GetManager()->CheckCaptureModeInitRestriction(
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
   VerifyAndResetActionAllowed(true /*expected*/);
+  EXPECT_EQ(events_.size(), 3u);
 }
 
 TEST_F(DlpContentManagerAshCheckRestrictionTest,
@@ -785,6 +803,12 @@
   // If the user cancels, the warning can be shown again for the same contents.
   EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(2);
 
+  SetReportQueueForReportingManager();
+  SetupDlpRulesManager();
+  EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern)
+      .Times(2)
+      .WillRepeatedly(::testing::Return(kSrcPattern));
+
   // No restrictions are enforced: allow.
   std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
   EXPECT_EQ(GetManager()->GetConfidentialRestrictions(web_contents.get()),
@@ -794,6 +818,7 @@
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
   VerifyAndResetActionAllowed(true /*expected*/);
   EXPECT_FALSE(helper_.HasAnyContentCached());
+  EXPECT_TRUE(events_.empty());
 
   // Warn restriction is enforced: reject since the user canceled.
   helper_.ChangeConfidentiality(web_contents.get(), kScreenshotWarned);
@@ -803,12 +828,22 @@
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
   VerifyAndResetActionAllowed(false /*expected*/);
   EXPECT_FALSE(helper_.HasAnyContentCached());
+  EXPECT_EQ(events_.size(), 1u);
+  EXPECT_THAT(events_[0],
+              IsDlpPolicyEvent(CreateDlpPolicyEvent(
+                  kSrcPattern, DlpRulesManager::Restriction::kScreenshot,
+                  DlpRulesManager::Level::kWarn)));
 
   // Check again: since the user previously cancelled, dialog is shown again.
   GetManager()->CheckCaptureModeInitRestriction(
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
   VerifyAndResetActionAllowed(false /*expected*/);
   EXPECT_FALSE(helper_.HasAnyContentCached());
+  EXPECT_EQ(events_.size(), 2u);
+  EXPECT_THAT(events_[1],
+              IsDlpPolicyEvent(CreateDlpPolicyEvent(
+                  kSrcPattern, DlpRulesManager::Restriction::kScreenshot,
+                  DlpRulesManager::Level::kWarn)));
 
   // Web contents are destroyed: allow, no dialog is shown.
   helper_.DestroyWebContents(web_contents.get());
@@ -818,6 +853,7 @@
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
 
   VerifyAndResetActionAllowed(true /*expected*/);
+  EXPECT_EQ(events_.size(), 2u);
 }
 
 TEST_F(DlpContentManagerAshCheckRestrictionTest, ScreenshotRestricted) {
@@ -883,6 +919,12 @@
   // The warning should be shown only once.
   EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(1);
 
+  SetReportQueueForReportingManager();
+  SetupDlpRulesManager();
+  EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern)
+      .Times(3)
+      .WillRepeatedly(::testing::Return(kSrcPattern));
+
   ScreenshotArea area = ScreenshotArea::CreateForAllRootWindows();
 
   // No restrictions are enforced: allow.
@@ -895,6 +937,7 @@
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
   VerifyAndResetActionAllowed(true /*expected*/);
   EXPECT_FALSE(helper_.HasAnyContentCached());
+  EXPECT_TRUE(events_.empty());
 
   // Warn restriction is enforced: allow and remember that the user proceeded.
   helper_.ChangeConfidentiality(web_contents.get(), kScreenshotWarned);
@@ -906,6 +949,14 @@
   VerifyAndResetActionAllowed(true /*expected*/);
   EXPECT_TRUE(helper_.HasContentCachedForRestriction(
       web_contents.get(), DlpRulesManager::Restriction::kScreenshot));
+  EXPECT_EQ(events_.size(), 2u);
+  EXPECT_THAT(events_[0],
+              IsDlpPolicyEvent(CreateDlpPolicyEvent(
+                  kSrcPattern, DlpRulesManager::Restriction::kScreenshot,
+                  DlpRulesManager::Level::kWarn)));
+  EXPECT_THAT(events_[1],
+              IsDlpPolicyEvent(CreateDlpPolicyWarningProceededEvent(
+                  kSrcPattern, DlpRulesManager::Restriction::kScreenshot)));
 
   // Check again: allow based on cached user's response - no dialog is shown.
   GetManager()->CheckScreenshotRestriction(
@@ -914,6 +965,10 @@
   VerifyAndResetActionAllowed(true /*expected*/);
   EXPECT_TRUE(helper_.HasContentCachedForRestriction(
       web_contents.get(), DlpRulesManager::Restriction::kScreenshot));
+  EXPECT_EQ(events_.size(), 3u);
+  EXPECT_THAT(events_[2],
+              IsDlpPolicyEvent(CreateDlpPolicyWarningProceededEvent(
+                  kSrcPattern, DlpRulesManager::Restriction::kScreenshot)));
 
   // Web contents are destroyed: allow, no dialog is shown.
   helper_.DestroyWebContents(web_contents.get());
@@ -923,6 +978,7 @@
       area,
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
   VerifyAndResetActionAllowed(true /*expected*/);
+  EXPECT_EQ(events_.size(), 3u);
 }
 
 TEST_F(DlpContentManagerAshCheckRestrictionTest, ScreenshotWarnedCancelled) {
@@ -932,6 +988,12 @@
   // If the user cancels, the warning can be shown again for the same contents.
   EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(2);
 
+  SetReportQueueForReportingManager();
+  SetupDlpRulesManager();
+  EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern)
+      .Times(2)
+      .WillRepeatedly(::testing::Return(kSrcPattern));
+
   ScreenshotArea area = ScreenshotArea::CreateForAllRootWindows();
 
   // No restrictions are enforced: allow.
@@ -944,6 +1006,7 @@
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
   VerifyAndResetActionAllowed(true /*expected*/);
   EXPECT_FALSE(helper_.HasAnyContentCached());
+  EXPECT_TRUE(events_.empty());
 
   // Warn restriction is enforced: reject since the user canceled.
   helper_.ChangeConfidentiality(web_contents.get(), kScreenshotWarned);
@@ -954,6 +1017,11 @@
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
   VerifyAndResetActionAllowed(false /*expected*/);
   EXPECT_FALSE(helper_.HasAnyContentCached());
+  EXPECT_EQ(events_.size(), 1u);
+  EXPECT_THAT(events_[0],
+              IsDlpPolicyEvent(CreateDlpPolicyEvent(
+                  kSrcPattern, DlpRulesManager::Restriction::kScreenshot,
+                  DlpRulesManager::Level::kWarn)));
 
   // Check again: since the user previously cancelled, dialog is shown again.
   GetManager()->CheckScreenshotRestriction(
@@ -961,6 +1029,11 @@
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
   VerifyAndResetActionAllowed(false /*expected*/);
   EXPECT_FALSE(helper_.HasAnyContentCached());
+  EXPECT_EQ(events_.size(), 2u);
+  EXPECT_THAT(events_[1],
+              IsDlpPolicyEvent(CreateDlpPolicyEvent(
+                  kSrcPattern, DlpRulesManager::Restriction::kScreenshot,
+                  DlpRulesManager::Level::kWarn)));
 
   // Web contents are destroyed: allow, no dialog is shown.
   helper_.DestroyWebContents(web_contents.get());
@@ -969,8 +1042,8 @@
   GetManager()->CheckScreenshotRestriction(
       area,
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-
   VerifyAndResetActionAllowed(true /*expected*/);
+  EXPECT_EQ(events_.size(), 2u);
 }
 
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer.cc b/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer.cc
index 9fc897a..334e69d 100644
--- a/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer.cc
+++ b/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer.cc
@@ -137,8 +137,8 @@
   // signal present that indicates the device should enroll.
 
   // Gather enrollment signals from various sources.
-  const base::DictionaryValue* device_state =
-      local_state_->GetDictionary(prefs::kServerBackedDeviceState);
+  const base::DictionaryValue* device_state = &base::Value::AsDictionaryValue(
+      *local_state_->GetDictionary(prefs::kServerBackedDeviceState));
   std::string device_state_mode;
   std::string device_state_management_domain;
   absl::optional<bool> is_license_packaged_with_device;
diff --git a/chrome/browser/ash/policy/networking/policy_cert_service_factory.cc b/chrome/browser/ash/policy/networking/policy_cert_service_factory.cc
index 79761918..01f7222 100644
--- a/chrome/browser/ash/policy/networking/policy_cert_service_factory.cc
+++ b/chrome/browser/ash/policy/networking/policy_cert_service_factory.cc
@@ -64,7 +64,7 @@
     const std::string& user_email,
     Profile* profile) {
   base::Value user_email_value(user_email);
-  const base::ListValue* list =
+  const base::Value* list =
       g_browser_process->local_state()->GetList(prefs::kUsedPolicyCertificates);
   if (!list) {
     NOTREACHED();
diff --git a/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc b/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
index 4853e5f..70c4825 100644
--- a/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
+++ b/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
@@ -745,8 +745,8 @@
     // Login into the public session.
     auto* controller = ash::ExistingUserController::current_controller();
     ASSERT_TRUE(controller);
-    chromeos::UserContext user_context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
-                                       device_local_account_id_);
+    ash::UserContext user_context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
+                                  device_local_account_id_);
     controller->Login(user_context, ash::SigninSpecifics());
   }
 };
diff --git a/chrome/browser/ash/policy/reporting/extension_install_event_logger.cc b/chrome/browser/ash/policy/reporting/extension_install_event_logger.cc
index 0d57405..59a7c4c5 100644
--- a/chrome/browser/ash/policy/reporting/extension_install_event_logger.cc
+++ b/chrome/browser/ash/policy/reporting/extension_install_event_logger.cc
@@ -58,7 +58,7 @@
 }
 
 void ExtensionInstallEventLogger::OnForcedExtensionsPrefChanged() {
-  const base::DictionaryValue* value =
+  const base::Value* value =
       pref_service_->GetDictionary(extensions::pref_names::kInstallForceList);
   if (!value)
     return;
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc
index e970c351..67049ee 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc
@@ -28,26 +28,26 @@
   reporting::MemoryEncryptionState reporting_encryption_state;
   cros_healthd::CryptoAlgorithm healthd_encryption_algorithm;
   reporting::MemoryEncryptionAlgorithm reporting_encryption_algorithm;
-  uint32_t max_keys;
-  uint32_t key_length;
+  int64_t max_keys;
+  int64_t key_length;
 };
 
 namespace reporting {
 namespace test {
 
 // Memory constants.
-constexpr uint32_t kTmeMaxKeys = 2;
-constexpr uint32_t kTmeKeysLength = 4;
+constexpr int64_t kTmeMaxKeys = 2;
+constexpr int64_t kTmeKeysLength = 4;
 
 // Wifi constants.
 constexpr char kInterfaceName[] = "interface_name";
 constexpr char kAccessPointAddress[] = "access_point";
 constexpr bool kPowerManagementEnabled = true;
 constexpr bool kEncryptionOn = true;
-constexpr uint32_t kTxBitRateMbps = 8;
-constexpr uint32_t kRxBitRateMbps = 4;
-constexpr uint32_t kTxPowerDbm = 2;
-constexpr uint32_t kLinkQuality = 1;
+constexpr int64_t kTxBitRateMbps = 8;
+constexpr int64_t kRxBitRateMbps = 4;
+constexpr int64_t kTxPowerDbm = 2;
+constexpr int64_t kLinkQuality = 1;
 constexpr int kSignalLevelDbm = 10;
 
 cros_healthd::KeylockerInfoPtr CreateKeylockerInfo(bool configured) {
@@ -91,11 +91,11 @@
     const std::string& interface_name,
     bool power_management_enabled,
     const std::string& access_point_address,
-    uint32_t tx_bit_rate_mbps,
-    uint32_t rx_bit_rate_mbps,
-    uint32_t tx_power_dbm,
+    int64_t tx_bit_rate_mbps,
+    int64_t rx_bit_rate_mbps,
+    int64_t tx_power_dbm,
     bool encryption_on,
-    uint32_t link_quality,
+    int64_t link_quality,
     int signal_level_dbm) {
   auto telemetry_info = cros_healthd::TelemetryInfo::New();
   std::vector<cros_healthd::NetworkInterfaceInfoPtr> network_interfaces;
@@ -122,10 +122,10 @@
     bool input_mute,
     uint64_t output_volume,
     const std::string& output_device_name,
-    uint32_t input_gain,
+    int64_t input_gain,
     const std::string& input_device_name,
-    uint32_t underruns,
-    uint32_t severe_underruns) {
+    int64_t underruns,
+    int64_t severe_underruns) {
   return cros_healthd::AudioInfo::New(
       output_mute, input_mute, output_volume, output_device_name, input_gain,
       input_device_name, underruns, severe_underruns);
@@ -141,8 +141,8 @@
 
 cros_healthd::MemoryEncryptionInfoPtr CreateMemoryEncryptionInfo(
     cros_healthd::EncryptionState encryption_state,
-    uint32_t max_keys,
-    uint32_t key_length,
+    int64_t max_keys,
+    int64_t key_length,
     cros_healthd::CryptoAlgorithm encryption_algorithm) {
   return cros_healthd::MemoryEncryptionInfo::New(
       encryption_state, max_keys, key_length, encryption_algorithm);
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc
index 4be9c84..ed01d45 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc
@@ -6,42 +6,58 @@
 
 #include "ash/components/settings/cros_settings_names.h"
 #include "base/logging.h"
-#include "base/memory/ptr_util.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/network/https_latency_sampler.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/network/network_events_observer.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "components/reporting/client/report_queue.h"
 #include "components/reporting/client/report_queue_factory.h"
 #include "components/reporting/metrics/metric_data_collector.h"
+#include "components/reporting/metrics/metric_event_observer_manager.h"
 #include "components/reporting/metrics/metric_rate_controller.h"
 #include "components/reporting/metrics/metric_report_queue.h"
+#include "components/reporting/metrics/sampler.h"
 #include "components/user_manager/user.h"
 
+namespace em = enterprise_management;
+
 namespace reporting {
 namespace {
 
+constexpr base::TimeDelta kDefaultReportUploadFrequencyForTesting =
+    base::Minutes(5);
+constexpr base::TimeDelta kDefaultCollectionRateForTesting = base::Minutes(2);
+constexpr base::TimeDelta kDefaultEventCheckingRateForTesting =
+    base::Minutes(1);
+
 constexpr base::TimeDelta kDefaultReportUploadFrequency = base::Hours(3);
 constexpr base::TimeDelta kDefaultNetworkTelemetryCollectionRate =
     base::Minutes(10);
+constexpr base::TimeDelta kDefaultNetworkTelemetryEventCheckingRate =
+    base::Minutes(2);
 
-base::TimeDelta GetDefaultReportUploadFrequency() {
-  // If telemetry testing rates flag is enabled, use an upload interval, to
-  // avoid waiting for at least 60 mins which is the status upload minimum rate
-  // in the admin console.
+base::TimeDelta GetDefaultRate(base::TimeDelta default_rate,
+                               base::TimeDelta testing_rate) {
+  // If telemetry testing rates flag is enabled, use `testing_rate` to reduce
+  // time before metric collection and reporting.
   return base::FeatureList::IsEnabled(
              MetricRateController::kEnableTelemetryTestingRates)
-             ? base::Minutes(5)
-             : kDefaultReportUploadFrequency;
+             ? testing_rate
+             : default_rate;
+}
+
+base::TimeDelta GetDefaultReportUploadFrequency() {
+  return GetDefaultRate(kDefaultReportUploadFrequency,
+                        kDefaultReportUploadFrequencyForTesting);
 }
 
 base::TimeDelta GetDefaulCollectionRate(base::TimeDelta default_rate) {
-  // If telemetry testing rates flag is enabled, use a 2 mins collection to
-  // avoid waiting for long time for collection in the case of low default rate
-  // and uncontrollable rate policy.
-  return base::FeatureList::IsEnabled(
-             MetricRateController::kEnableTelemetryTestingRates)
-             ? base::Minutes(2)
-             : default_rate;
+  return GetDefaultRate(default_rate, kDefaultCollectionRateForTesting);
+}
+
+base::TimeDelta GetDefaulEventCheckingRate(base::TimeDelta default_rate) {
+  return GetDefaultRate(default_rate, kDefaultEventCheckingRateForTesting);
 }
 
 }  // namespace
@@ -50,62 +66,30 @@
 const base::Feature MetricReportingManager::kEnableNetworkTelemetryReporting{
     "EnableNetworkTelemetryReporting", base::FEATURE_DISABLED_BY_DEFAULT};
 
-MetricReportingManager::Delegate::Delegate() = default;
-
-MetricReportingManager::Delegate::~Delegate() = default;
-
-std::unique_ptr<MetricReportQueue>
-MetricReportingManager::Delegate::CreateInfoReportQueue() {
-  // Pass empty dm token value so the report queue get and use the device dm
-  // token.
-  auto report_queue = ReportQueueFactory::CreateSpeculativeReportQueue(
-      /*dm_token_value=*/"", Destination::INFO_METRIC);
-
-  return std::make_unique<MetricReportQueue>(std::move(report_queue),
-                                             Priority::SLOW_BATCH);
-}
-
-std::unique_ptr<MetricReportQueue>
-MetricReportingManager::Delegate::CreateEventReportQueue() {
-  // Pass empty dm token value so the report queue get and use the device dm
-  // token.
-  auto report_queue = ReportQueueFactory::CreateSpeculativeReportQueue(
-      /*dm_token_value=*/"", Destination::EVENT_METRIC);
-
-  return std::make_unique<MetricReportQueue>(std::move(report_queue),
-                                             Priority::FAST_BATCH);
-}
-
-std::unique_ptr<MetricReportQueue>
-MetricReportingManager::Delegate::CreateTelemetryReportQueue(
-    ReportingSettings* reporting_settings,
-    const std::string& rate_setting_path,
-    base::TimeDelta default_rate) {
-  // Pass empty dm token value so the report queue get and use the device dm
-  // token.
-  auto report_queue = ReportQueueFactory::CreateSpeculativeReportQueue(
-      /*dm_token_value=*/"", Destination::TELEMETRY_METRIC);
-
-  // TODO(b/177847653):: Add new manual priority specific to telemetry
-  // upload and use it here instead of the general `Priority::MANUAL_BATCH`.
-  return std::make_unique<MetricReportQueue>(
-      std::move(report_queue), Priority::MANUAL_BATCH, reporting_settings,
-      rate_setting_path, default_rate);
-}
-
-Sampler* MetricReportingManager::Delegate::AddSampler(
-    std::unique_ptr<Sampler> sampler) {
-  auto* sampler_ptr = sampler.get();
-  samplers_.emplace_back(std::move(sampler));
-  return sampler_ptr;
-}
-
 bool MetricReportingManager::Delegate::IsAffiliated(Profile* profile) {
   const user_manager::User* const user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
   return user && user->IsAffiliated();
 }
 
+std::unique_ptr<::reporting::ReportQueue, base::OnTaskRunnerDeleter>
+MetricReportingManager::Delegate::CreateReportQueue(Destination destination) {
+  return ReportQueueFactory::CreateSpeculativeReportQueue(EventType::kDevice,
+                                                          destination);
+}
+
+std::unique_ptr<Sampler>
+MetricReportingManager::Delegate::CreateHttpsLatencySampler() {
+  return std::make_unique<HttpsLatencySampler>();
+}
+
+bool MetricReportingManager::Delegate::IsDeprovisioned() {
+  return ::ash::DeviceSettingsService::IsInitialized() &&
+         ::ash::DeviceSettingsService::Get()->policy_data() &&
+         ::ash::DeviceSettingsService::Get()->policy_data()->state() ==
+             em::PolicyData::DEPROVISIONED;
+}
+
 // static
 std::unique_ptr<MetricReportingManager> MetricReportingManager::Create(
     policy::ManagedSessionService* managed_session_service) {
@@ -132,49 +116,172 @@
   InitOnAffiliatedLogin();
 }
 
+void MetricReportingManager::DeviceSettingsUpdated() {
+  if (delegate_->IsDeprovisioned()) {
+    Shutdown();
+  }
+}
+
 MetricReportingManager::MetricReportingManager(
     std::unique_ptr<Delegate> delegate,
     policy::ManagedSessionService* managed_session_service)
     : delegate_(std::move(delegate)) {
+  if (delegate_->IsDeprovisioned()) {
+    return;
+  }
+  Init();
   if (managed_session_service) {
     managed_session_observation_.Observe(managed_session_service);
   }
-  Init();
+  if (::ash::DeviceSettingsService::IsInitialized()) {
+    device_settings_observation_.Observe(::ash::DeviceSettingsService::Get());
+  }
+}
+
+void MetricReportingManager::Shutdown() {
+  one_shot_collectors_.clear();
+  periodic_collectors_.clear();
+  event_observer_managers_.clear();
+  samplers_.clear();
+  info_report_queue_.reset();
+  telemetry_report_queue_.reset();
+  event_report_queue_.reset();
 }
 
 void MetricReportingManager::Init() {
-  telemetry_report_queue_ = delegate_->CreateTelemetryReportQueue(
-      &reporting_settings_, ash::kReportUploadFrequency,
-      GetDefaultReportUploadFrequency());
+  info_report_queue_ =
+      CreateMetricReportQueue(Destination::INFO_METRIC, Priority::SLOW_BATCH);
+  telemetry_report_queue_ = CreateTelemetryQueue();
+  event_report_queue_ =
+      CreateMetricReportQueue(Destination::EVENT_METRIC, Priority::SLOW_BATCH);
+
+  if (base::FeatureList::IsEnabled(kEnableNetworkTelemetryReporting)) {
+    // Network health info.
+    CreateOneShotCollector(std::make_unique<NetworkInfoSampler>(),
+                           info_report_queue_.get(),
+                           ::ash::kReportDeviceNetworkConfiguration);
+  }
 }
 
 void MetricReportingManager::InitOnAffiliatedLogin() {
   InitNetworkCollectors();
 }
 
+std::unique_ptr<MetricReportQueue>
+MetricReportingManager::CreateMetricReportQueue(Destination destination,
+                                                Priority priority) {
+  std::unique_ptr<MetricReportQueue> metric_report_queue;
+  auto report_queue = delegate_->CreateReportQueue(destination);
+  if (report_queue) {
+    metric_report_queue =
+        std::make_unique<MetricReportQueue>(std::move(report_queue), priority);
+  } else {
+    DVLOG(1) << "Cannot create metric report queue, report queue is null";
+  }
+  return metric_report_queue;
+}
+
+std::unique_ptr<MetricReportQueue>
+MetricReportingManager::CreateTelemetryQueue() {
+  std::unique_ptr<MetricReportQueue> telemetry_report_queue;
+  auto report_queue =
+      delegate_->CreateReportQueue(Destination::TELEMETRY_METRIC);
+  if (report_queue) {
+    telemetry_report_queue = std::make_unique<MetricReportQueue>(
+        std::move(report_queue), Priority::MANUAL_BATCH, &reporting_settings_,
+        ::ash::kReportUploadFrequency, GetDefaultReportUploadFrequency());
+  } else {
+    DVLOG(1) << "Cannot create telemetry report queue, report queue is null";
+  }
+  return telemetry_report_queue;
+}
+
+void MetricReportingManager::CreateOneShotCollector(
+    std::unique_ptr<Sampler> sampler,
+    MetricReportQueue* metric_report_queue,
+    const std::string& enable_setting_path) {
+  auto* const sampler_ptr = sampler.get();
+  samplers_.emplace_back(std::move(sampler));
+  if (!metric_report_queue) {
+    return;
+  }
+  one_shot_collectors_.emplace_back(std::make_unique<OneShotCollector>(
+      sampler_ptr, metric_report_queue, &reporting_settings_,
+      enable_setting_path));
+}
+
 void MetricReportingManager::CreatePeriodicCollector(
-    Sampler* sampler,
+    std::unique_ptr<Sampler> sampler,
     const std::string& enable_setting_path,
     const std::string& rate_setting_path,
     base::TimeDelta default_rate,
     int rate_unit_to_ms) {
+  auto* const sampler_ptr = sampler.get();
+  samplers_.emplace_back(std::move(sampler));
+  if (!telemetry_report_queue_) {
+    return;
+  }
   periodic_collectors_.emplace_back(std::make_unique<PeriodicCollector>(
-      sampler, telemetry_report_queue_.get(), &reporting_settings_,
+      sampler_ptr, telemetry_report_queue_.get(), &reporting_settings_,
       enable_setting_path, rate_setting_path, default_rate, rate_unit_to_ms));
 }
 
+void MetricReportingManager::CreatePeriodicEventCollector(
+    std::unique_ptr<Sampler> sampler,
+    std::unique_ptr<EventDetector> event_detector,
+    std::vector<Sampler*> additional_samplers,
+    const std::string& enable_setting_path,
+    const std::string& rate_setting_path,
+    base::TimeDelta default_rate,
+    int rate_unit_to_ms) {
+  auto* const sampler_ptr = sampler.get();
+  samplers_.emplace_back(std::move(sampler));
+  if (!event_report_queue_) {
+    return;
+  }
+  periodic_collectors_.emplace_back(std::make_unique<PeriodicEventCollector>(
+      sampler_ptr, std::move(event_detector), std::move(additional_samplers),
+      event_report_queue_.get(), &reporting_settings_, enable_setting_path,
+      rate_setting_path, default_rate, rate_unit_to_ms));
+}
+
+void MetricReportingManager::CreateEventObserverManager(
+    std::unique_ptr<MetricEventObserver> event_observer,
+    const std::string& enable_setting_path,
+    std::vector<Sampler*> additional_samplers) {
+  if (!event_report_queue_) {
+    return;
+  }
+  event_observer_managers_.emplace_back(
+      std::make_unique<MetricEventObserverManager>(
+          std::move(event_observer), event_report_queue_.get(),
+          &reporting_settings_, enable_setting_path,
+          std::move(additional_samplers)));
+}
+
 void MetricReportingManager::InitNetworkCollectors() {
   if (!base::FeatureList::IsEnabled(kEnableNetworkTelemetryReporting)) {
     return;
   }
 
-  auto* https_latency_sampler =
-      delegate_->AddSampler(std::make_unique<HttpsLatencySampler>());
-  auto* network_telemetry_sampler = delegate_->AddSampler(
-      std::make_unique<NetworkTelemetrySampler>(https_latency_sampler));
+  auto https_latency_sampler = delegate_->CreateHttpsLatencySampler();
+  auto network_telemetry_sampler =
+      std::make_unique<NetworkTelemetrySampler>(https_latency_sampler.get());
+  // Network health telemetry.
   CreatePeriodicCollector(
-      network_telemetry_sampler, ash::kReportDeviceNetworkStatus,
-      ash::kReportDeviceNetworkTelemetryCollectionRateMs,
+      std::move(network_telemetry_sampler), ::ash::kReportDeviceNetworkStatus,
+      ::ash::kReportDeviceNetworkTelemetryCollectionRateMs,
       GetDefaulCollectionRate(kDefaultNetworkTelemetryCollectionRate));
+  // HttpsLatency events.
+  CreatePeriodicEventCollector(
+      std::move(https_latency_sampler),
+      std::make_unique<HttpsLatencyEventDetector>(), /*additional_samplers=*/{},
+      ::ash::kReportDeviceNetworkStatus,
+      ::ash::kReportDeviceNetworkTelemetryEventCheckingRateMs,
+      GetDefaulEventCheckingRate(kDefaultNetworkTelemetryEventCheckingRate));
+  // Network health events observer.
+  CreateEventObserverManager(std::make_unique<NetworkEventsObserver>(),
+                             ::ash::kReportDeviceNetworkStatus);
 }
+
 }  // namespace reporting
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h
index a45cd07..319b1c18 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h
@@ -11,21 +11,29 @@
 
 #include "base/feature_list.h"
 #include "base/scoped_observation.h"
+#include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/cros_reporting_settings.h"
 #include "chrome/browser/ash/policy/status_collector/managed_session_service.h"
+#include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/reporting/proto/synced/record_constants.pb.h"
 
 namespace reporting {
 
+class EventDetector;
+class MetricEventObserver;
+class MetricEventObserverManager;
 class MetricReportQueue;
+class OneShotCollector;
 class PeriodicCollector;
+class ReportQueue;
 class Sampler;
 
 // Class to initialize and start info, event, and telemetry collection and
 // reporting.
-class MetricReportingManager : public policy::ManagedSessionService::Observer {
+class MetricReportingManager : public policy::ManagedSessionService::Observer,
+                               public ::ash::DeviceSettingsService::Observer {
  public:
   static const base::Feature kEnableNetworkTelemetryReporting;
 
@@ -33,28 +41,21 @@
   // for testing purposes.
   class Delegate {
    public:
-    Delegate();
+    Delegate() = default;
 
     Delegate(const Delegate& other) = delete;
     Delegate& operator=(const Delegate& other) = delete;
 
-    virtual ~Delegate();
-
-    virtual std::unique_ptr<MetricReportQueue> CreateInfoReportQueue();
-
-    virtual std::unique_ptr<MetricReportQueue> CreateEventReportQueue();
-
-    virtual std::unique_ptr<MetricReportQueue> CreateTelemetryReportQueue(
-        ReportingSettings* reporting_settings,
-        const std::string& rate_setting_path,
-        base::TimeDelta default_rate);
-
-    virtual Sampler* AddSampler(std::unique_ptr<Sampler> sampler);
+    virtual ~Delegate() = default;
 
     virtual bool IsAffiliated(Profile* profile);
 
-   private:
-    std::vector<std::unique_ptr<Sampler>> samplers_;
+    virtual std::unique_ptr<ReportQueue, base::OnTaskRunnerDeleter>
+    CreateReportQueue(Destination destination);
+
+    virtual std::unique_ptr<Sampler> CreateHttpsLatencySampler();
+
+    virtual bool IsDeprovisioned();
   };
 
   static std::unique_ptr<MetricReportingManager> Create(
@@ -66,13 +67,19 @@
 
   ~MetricReportingManager() override;
 
+  // ManagedSessionService::Observer
   void OnLogin(Profile* profile) override;
 
+  // DeviceSettingsService::Observer
+  void DeviceSettingsUpdated() override;
+
  private:
   MetricReportingManager(
       std::unique_ptr<Delegate> delegate,
       policy::ManagedSessionService* managed_session_service);
 
+  void Shutdown();
+
   // Init reporting queues and collectors that need to start before login,
   // should only be called once on construction.
   void Init();
@@ -80,25 +87,56 @@
   // affiliated user login, should only be called once on login.
   void InitOnAffiliatedLogin();
 
-  void CreatePeriodicCollector(Sampler* sampler,
+  std::unique_ptr<MetricReportQueue> CreateMetricReportQueue(
+      Destination destination,
+      Priority priority);
+  std::unique_ptr<MetricReportQueue> CreateTelemetryQueue();
+
+  void CreateOneShotCollector(std::unique_ptr<Sampler> sampler,
+                              MetricReportQueue* report_queue,
+                              const std::string& enable_setting_path);
+  void CreatePeriodicCollector(std::unique_ptr<Sampler> sampler,
                                const std::string& enable_setting_path,
                                const std::string& rate_setting_path,
                                base::TimeDelta default_rate,
                                int rate_unit_to_ms = 1);
+  void CreatePeriodicEventCollector(
+      std::unique_ptr<Sampler> sampler,
+      std::unique_ptr<EventDetector> event_detector,
+      std::vector<Sampler*> additional_samplers,
+      const std::string& enable_setting_path,
+      const std::string& rate_setting_path,
+      base::TimeDelta default_rate,
+      int rate_unit_to_ms = 1);
+  void CreateEventObserverManager(
+      std::unique_ptr<MetricEventObserver> event_observer,
+      const std::string& enable_setting_path,
+      std::vector<Sampler*> additional_samplers = {});
 
   void InitNetworkCollectors();
 
   CrosReportingSettings reporting_settings_;
 
+  std::vector<std::unique_ptr<Sampler>> samplers_;
+
   std::vector<std::unique_ptr<PeriodicCollector>> periodic_collectors_;
+  std::vector<std::unique_ptr<OneShotCollector>> one_shot_collectors_;
+  std::vector<std::unique_ptr<MetricEventObserverManager>>
+      event_observer_managers_;
+
+  std::unique_ptr<MetricReportQueue> info_report_queue_;
+  std::unique_ptr<MetricReportQueue> telemetry_report_queue_;
+  std::unique_ptr<MetricReportQueue> event_report_queue_;
 
   const std::unique_ptr<Delegate> delegate_;
 
-  std::unique_ptr<MetricReportQueue> telemetry_report_queue_;
-
   base::ScopedObservation<policy::ManagedSessionService,
                           policy::ManagedSessionService::Observer>
       managed_session_observation_{this};
+
+  base::ScopedObservation<::ash::DeviceSettingsService,
+                          ::ash::DeviceSettingsService::Observer>
+      device_settings_observation_{this};
 };
 }  // namespace reporting
 
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc
index f47c223c..66ede7e 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc
@@ -11,17 +11,29 @@
 #include "ash/components/settings/cros_settings_names.h"
 #include "ash/components/settings/cros_settings_provider.h"
 #include "base/feature_list.h"
+#include "base/run_loop.h"
+#include "base/task/thread_pool.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
+#include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
 #include "chrome/browser/ash/settings/stub_cros_settings_provider.h"
 #include "chrome/browser/profiles/profile.h"
-#include "components/reporting/metrics/fake_metric_report_queue.h"
+#include "chromeos/dbus/cros_healthd/cros_healthd_client.h"
+#include "chromeos/dbus/cros_healthd/fake_cros_healthd_client.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_handler_test_helper.h"
+#include "chromeos/services/cros_healthd/public/cpp/service_connection.h"
+#include "chromeos/services/network_health/public/mojom/network_health.mojom.h"
+#include "components/reporting/client/mock_report_queue.h"
 #include "components/reporting/metrics/fake_sampler.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 
 namespace reporting {
+namespace {
 
 class FakeDelegate : public MetricReportingManager::Delegate {
  public:
@@ -32,139 +44,316 @@
 
   ~FakeDelegate() override = default;
 
-  std::unique_ptr<MetricReportQueue> CreateInfoReportQueue() override {
-    auto report_queue = std::make_unique<test::FakeMetricReportQueue>();
-    info_queue_ = report_queue.get();
-    return report_queue;
+  std::unique_ptr<ReportQueue, base::OnTaskRunnerDeleter> CreateReportQueue(
+      Destination destination) override {
+    switch (destination) {
+      case INFO_METRIC:
+        return std::move(info_queue_);
+      case TELEMETRY_METRIC:
+        return std::move(telemetry_queue_);
+      case EVENT_METRIC:
+        return std::move(event_queue_);
+      default:
+        NOTREACHED();
+        return std::unique_ptr<ReportQueue, base::OnTaskRunnerDeleter>(
+            nullptr,
+            base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get()));
+    }
   }
 
-  std::unique_ptr<MetricReportQueue> CreateEventReportQueue() override {
-    auto report_queue = std::make_unique<test::FakeMetricReportQueue>();
-    event_queue_ = report_queue.get();
-    return report_queue;
+  std::unique_ptr<Sampler> CreateHttpsLatencySampler() override {
+    if (!latency_sampler_) {
+      latency_sampler_ = std::make_unique<test::FakeSampler>();
+    }
+    return std::move(latency_sampler_);
   }
 
-  std::unique_ptr<MetricReportQueue> CreateTelemetryReportQueue(
-      ReportingSettings* reporting_settings,
-      const std::string& rate_setting_path,
-      base::TimeDelta default_rate) override {
-    auto report_queue = std::make_unique<test::FakeMetricReportQueue>(
-        Priority::MANUAL_BATCH, reporting_settings, rate_setting_path,
-        default_rate);
-    telemetry_queue_ = report_queue.get();
-    return report_queue;
-  }
-
-  Sampler* AddSampler(std::unique_ptr<Sampler>) override {
-    return Delegate::AddSampler(std::make_unique<test::FakeSampler>());
-  }
+  bool IsDeprovisioned() override { return is_deprovisioned_; }
 
   bool IsAffiliated(Profile* profile) override { return is_affiliated_; }
 
   void SetIsAffiliated(bool is_affiliated) { is_affiliated_ = is_affiliated; }
 
-  test::FakeMetricReportQueue* GetInfoQueue() { return info_queue_; }
+  void SetInfoQueue(
+      std::unique_ptr<ReportQueue, base::OnTaskRunnerDeleter> info_queue) {
+    info_queue_ = std::move(info_queue);
+  }
 
-  test::FakeMetricReportQueue* GetEventQueue() { return event_queue_; }
+  void SetTelemetryQueue(
+      std::unique_ptr<ReportQueue, base::OnTaskRunnerDeleter> telemetry_queue) {
+    telemetry_queue_ = std::move(telemetry_queue);
+  }
 
-  test::FakeMetricReportQueue* GetTelemetryQueue() { return telemetry_queue_; }
+  void SetEventQueue(
+      std::unique_ptr<ReportQueue, base::OnTaskRunnerDeleter> event_queue) {
+    event_queue_ = std::move(event_queue);
+  }
+
+  void SetHttpsLatencySampler(
+      std::unique_ptr<test::FakeSampler> latency_sampler) {
+    latency_sampler_ = std::move(latency_sampler);
+  }
+
+  void SetIsDeprovisioned(bool is_deprovisioned) {
+    is_deprovisioned_ = is_deprovisioned;
+  }
 
  private:
   bool is_affiliated_ = true;
+  bool is_deprovisioned_ = false;
 
-  test::FakeMetricReportQueue* info_queue_;
-  test::FakeMetricReportQueue* event_queue_;
-  test::FakeMetricReportQueue* telemetry_queue_;
+  std::unique_ptr<ReportQueue, base::OnTaskRunnerDeleter> info_queue_{
+      nullptr,
+      base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get())};
+  std::unique_ptr<ReportQueue, base::OnTaskRunnerDeleter> telemetry_queue_{
+      nullptr,
+      base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get())};
+  std::unique_ptr<ReportQueue, base::OnTaskRunnerDeleter> event_queue_{
+      nullptr,
+      base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get())};
+
+  std::unique_ptr<test::FakeSampler> latency_sampler_;
 };
 
-void NetworkCollectorsTestHelper(
-    bool is_affiliated,
-    const std::vector<base::Feature>& enabled_features,
-    const std::vector<base::Feature>& disabled_features,
-    bool telemetry_policy_enabled,
-    int telemetry_collection_rate_ms,
-    const base::TimeDelta time_forward,
-    size_t expected_telemetry_reports_count) {
-  base::test::SingleThreadTaskEnvironment task_environment{
+struct NetworkHealthReportingTestCase {
+  std::string test_name;
+  bool is_feature_enabled;
+  bool is_deprovisioned;
+  bool is_affiliated;
+  bool info_policy_enabled;
+  bool telemetry_policy_enabled;
+  RoutineVerdict latency_verdict;
+  int expected_info_count;
+  int expected_telemetry_count;
+  int expected_event_count;
+  int expected_flush_per_period;
+};
+
+constexpr int kNetworkHealthRateMs = 60000;
+
+class NetworkHealthReportingTest
+    : public ::testing::TestWithParam<NetworkHealthReportingTestCase> {
+ protected:
+  NetworkHealthReportingTest() = default;
+
+  NetworkHealthReportingTest(const NetworkHealthReportingTest&) = delete;
+  NetworkHealthReportingTest& operator=(const NetworkHealthReportingTest&) =
+      delete;
+
+  ~NetworkHealthReportingTest() override = default;
+
+  void SetUp() override {
+    ::ash::CrosHealthdClient::InitializeFake();
+
+    task_runner_ = base::ThreadPool::CreateSequencedTaskRunner({});
+
+    scoped_testing_cros_settings_.device_settings()->SetInteger(
+        ::ash::kReportUploadFrequency, kNetworkHealthRateMs);
+    scoped_testing_cros_settings_.device_settings()->SetInteger(
+        ::ash::kReportDeviceNetworkTelemetryCollectionRateMs,
+        kNetworkHealthRateMs);
+    scoped_testing_cros_settings_.device_settings()->SetInteger(
+        ::ash::kReportDeviceNetworkTelemetryEventCheckingRateMs,
+        kNetworkHealthRateMs);
+
+    network_handler_test_helper_.device_test()->ClearDevices();
+    network_handler_test_helper_.device_test()->AddDevice(
+        "ethernet/path", shill::kTypeEthernet, "ethernet");
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void TearDown() override {
+    ::ash::CrosHealthdClient::Shutdown();
+    ::ash::cros_healthd::ServiceConnection::GetInstance()->FlushForTesting();
+  }
+
+  void EmitSignalStrengthEvent() {
+    base::RunLoop run_loop;
+    ::ash::cros_healthd::FakeCrosHealthdClient::Get()
+        ->EmitSignalStrengthChangedEventForTesting(
+            "guid", ::chromeos::network_health::mojom::UInt32Value::New(50));
+    base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                     run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
+  base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
 
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(enabled_features, disabled_features);
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
-  ash::ScopedTestingCrosSettings scoped_testing_cros_settings;
-  scoped_testing_cros_settings.device_settings()->SetInteger(
-      ash::kReportUploadFrequency, time_forward.InMilliseconds());
-  scoped_testing_cros_settings.device_settings()->SetBoolean(
-      ash::kReportDeviceNetworkStatus, telemetry_policy_enabled);
-  scoped_testing_cros_settings.device_settings()->SetInteger(
-      ash::kReportDeviceNetworkTelemetryCollectionRateMs,
-      telemetry_collection_rate_ms);
+  ::ash::ScopedTestingCrosSettings scoped_testing_cros_settings_;
+
+ private:
+  ::ash::NetworkHandlerTestHelper network_handler_test_helper_;
+
+  ::ash::ScopedTestDeviceSettingsService test_device_settings_service_;
+};
+
+TEST_P(NetworkHealthReportingTest, Info_Telemetry_LatencyEvent) {
+  const NetworkHealthReportingTestCase& test_case = GetParam();
+
+  scoped_testing_cros_settings_.device_settings()->SetBoolean(
+      ::ash::kReportDeviceNetworkConfiguration, test_case.info_policy_enabled);
+  scoped_testing_cros_settings_.device_settings()->SetBoolean(
+      ::ash::kReportDeviceNetworkStatus, test_case.telemetry_policy_enabled);
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureState(
+      MetricReportingManager::kEnableNetworkTelemetryReporting,
+      test_case.is_feature_enabled);
 
   auto fake_delegate = std::make_unique<FakeDelegate>();
-  fake_delegate->SetIsAffiliated(is_affiliated);
   auto* const fake_delegate_ptr = fake_delegate.get();
+
+  fake_delegate->SetIsAffiliated(test_case.is_affiliated);
+  fake_delegate->SetIsDeprovisioned(test_case.is_deprovisioned);
+  auto https_latency_sampler = std::make_unique<test::FakeSampler>();
+  MetricData https_latency_data;
+  https_latency_data.mutable_telemetry_data()
+      ->mutable_networks_telemetry()
+      ->mutable_https_latency_data()
+      ->set_verdict(test_case.latency_verdict);
+  https_latency_sampler->SetMetricData(std::move(https_latency_data));
+  fake_delegate->SetHttpsLatencySampler(std::move(https_latency_sampler));
+  auto info_queue = std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>(
+      new ::testing::NiceMock<MockReportQueue>(),
+      base::OnTaskRunnerDeleter(task_runner_));
+  auto telemetry_queue =
+      std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>(
+          new ::testing::NiceMock<MockReportQueue>(),
+          base::OnTaskRunnerDeleter(task_runner_));
+  auto event_queue =
+      std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>(
+          new ::testing::NiceMock<MockReportQueue>(),
+          base::OnTaskRunnerDeleter(task_runner_));
+  auto* const info_queue_ptr = info_queue.get();
+  auto* const telemetry_queue_ptr = telemetry_queue.get();
+  auto* const event_queue_ptr = event_queue.get();
+
+  fake_delegate->SetInfoQueue(std::move(info_queue));
+  fake_delegate->SetTelemetryQueue(std::move(telemetry_queue));
+  fake_delegate->SetEventQueue(std::move(event_queue));
+
+  EXPECT_CALL(*info_queue_ptr, AddRecord).Times(test_case.expected_info_count);
   auto metric_reporting_manager = MetricReportingManager::CreateForTesting(
       std::move(fake_delegate), nullptr);
 
-  task_environment.FastForwardBy(time_forward);
-  EXPECT_TRUE(
-      fake_delegate_ptr->GetTelemetryQueue()->GetMetricDataReported().empty());
+  EXPECT_CALL(*telemetry_queue_ptr, AddRecord).Times(0);
+  EXPECT_CALL(*telemetry_queue_ptr, Flush)
+      .Times(test_case.expected_flush_per_period);
+  EXPECT_CALL(*event_queue_ptr, AddRecord).Times(0);
+  task_environment_.FastForwardBy(base::Milliseconds(kNetworkHealthRateMs));
 
   metric_reporting_manager->OnLogin(nullptr);
 
-  task_environment.FastForwardBy(time_forward);
+  EXPECT_CALL(*telemetry_queue_ptr, AddRecord)
+      .Times(test_case.expected_telemetry_count);
+  EXPECT_CALL(*telemetry_queue_ptr, Flush)
+      .Times(test_case.expected_flush_per_period);
+  EXPECT_CALL(*event_queue_ptr, AddRecord)
+      .Times(test_case.expected_event_count);
+  task_environment_.FastForwardBy(base::Milliseconds(kNetworkHealthRateMs));
 
-  EXPECT_EQ(
-      fake_delegate_ptr->GetTelemetryQueue()->GetMetricDataReported().size(),
-      expected_telemetry_reports_count);
-  EXPECT_EQ(fake_delegate_ptr->GetTelemetryQueue()->GetNumFlush(), 2);
+  fake_delegate_ptr->SetIsDeprovisioned(true);
+  metric_reporting_manager->DeviceSettingsUpdated();
+
+  // Device is deprovisioned, so no reporting.
+  EXPECT_CALL(*telemetry_queue_ptr, AddRecord).Times(0);
+  EXPECT_CALL(*telemetry_queue_ptr, Flush).Times(0);
+  EXPECT_CALL(*event_queue_ptr, AddRecord).Times(0);
+  task_environment_.FastForwardBy(base::Milliseconds(kNetworkHealthRateMs));
 }
 
-TEST(MetricReportingManagerTest, NetworkCollectors_FeatureDisabled) {
-  NetworkCollectorsTestHelper(
-      /*is_affiliated=*/true,
-      /*enabled_features=*/{},
-      /*disabled_features=*/
-      {MetricReportingManager::kEnableNetworkTelemetryReporting},
-      /*telemetry_policy_enabled=*/true,
-      /*telemetry_collection_rate_ms=*/60000,
-      /*time_forward=*/base::Milliseconds(120000),
-      /*expected_telemetry_reports_count=*/0ul);
+TEST_F(NetworkHealthReportingTest, NetworkEventsOvserver) {
+  scoped_testing_cros_settings_.device_settings()->SetBoolean(
+      ::ash::kReportDeviceNetworkStatus, true);
+
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureState(
+      MetricReportingManager::kEnableNetworkTelemetryReporting, true);
+
+  auto fake_delegate = std::make_unique<FakeDelegate>();
+  fake_delegate->SetIsAffiliated(true);
+  auto event_queue =
+      std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>(
+          new ::testing::NiceMock<MockReportQueue>(),
+          base::OnTaskRunnerDeleter(task_runner_));
+  auto* const event_queue_ptr = event_queue.get();
+  fake_delegate->SetEventQueue(std::move(event_queue));
+
+  auto metric_reporting_manager = MetricReportingManager::CreateForTesting(
+      std::move(fake_delegate), nullptr);
+
+  metric_reporting_manager->OnLogin(nullptr);
+  EXPECT_CALL(*event_queue_ptr, AddRecord).Times(1);
+  base::RunLoop run_loop;
+  EmitSignalStrengthEvent();
+  base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                   run_loop.QuitClosure());
+  run_loop.Run();
 }
 
-TEST(MetricReportingManagerTest, NetworkCollectors_PolicyDisabled) {
-  NetworkCollectorsTestHelper(
-      /*is_affiliated=*/true,
-      /*enabled_features=*/
-      {MetricReportingManager::kEnableNetworkTelemetryReporting},
-      /*disabled_features=*/{},
-      /*telemetry_policy_enabled=*/false,
-      /*telemetry_collection_rate_ms=*/60000,
-      /*time_forward=*/base::Milliseconds(120000),
-      /*expected_telemetry_reports_count=*/0ul);
-}
+INSTANTIATE_TEST_SUITE_P(
+    NetworkHealthReportingTests,
+    NetworkHealthReportingTest,
+    ::testing::ValuesIn<NetworkHealthReportingTestCase>(
+        {{"FeatureDisabled", /*is_feature_enabled=*/false,
+          /*is_deprovisioned=*/false,
+          /*is_affiliated=*/true, /*info_policy_enabled=*/true,
+          /*telemetry_policy_enabled=*/true,
+          /*latency_verdict=*/RoutineVerdict::PROBLEM,
+          /*expected_info_count=*/0,
+          /*expected_telemetry_count=*/0, /*expected_event_count=*/0,
+          /*expected_flush_per_period=*/1},
+         {"Deprovisioned", /*is_feature_enabled=*/true,
+          /*is_deprovisioned=*/true,
+          /*is_affiliated=*/true, /*info_policy_enabled=*/true,
+          /*telemetry_policy_enabled=*/true,
+          /*latency_verdict=*/RoutineVerdict::PROBLEM,
+          /*expected_info_count=*/0,
+          /*expected_telemetry_count=*/0, /*expected_event_count=*/0,
+          /*expected_flush_per_period=*/0},
+         {"NotAffiliated", /*is_feature_enabled=*/true,
+          /*is_deprovisioned=*/false,
+          /*is_affiliated=*/false, /*info_policy_enabled=*/true,
+          /*telemetry_policy_enabled=*/true,
+          /*latency_verdict=*/RoutineVerdict::PROBLEM,
+          /*expected_info_count=*/1,
+          /*expected_telemetry_count=*/0, /*expected_event_count=*/0,
+          /*expected_flush_per_period=*/1},
+         {"InfoPolicyDisabled", /*is_feature_enabled=*/true,
+          /*is_deprovisioned=*/false,
+          /*is_affiliated=*/true, /*info_policy_enabled=*/false,
+          /*telemetry_policy_enabled=*/true,
+          /*latency_verdict=*/RoutineVerdict::PROBLEM,
+          /*expected_info_count=*/0,
+          /*expected_telemetry_count=*/1, /*expected_event_count=*/1,
+          /*expected_flush_per_period=*/1},
+         {"TelemetryPolicyDisabled", /*is_feature_enabled=*/true,
+          /*is_deprovisioned=*/false,
+          /*is_affiliated=*/true, /*info_policy_enabled=*/true,
+          /*telemetry_policy_enabled=*/false,
+          /*latency_verdict=*/RoutineVerdict::PROBLEM,
+          /*expected_info_count=*/1,
+          /*expected_telemetry_count=*/0, /*expected_event_count=*/0,
+          /*expected_flush_per_period=*/1},
+         {"LatencyVerdictNoProblem", /*is_feature_enabled=*/true,
+          /*is_deprovisioned=*/false,
+          /*is_affiliated=*/true, /*info_policy_enabled=*/true,
+          /*telemetry_policy_enabled=*/true,
+          /*latency_verdict=*/RoutineVerdict::NO_PROBLEM,
+          /*expected_info_count=*/1,
+          /*expected_telemetry_count=*/1, /*expected_event_count=*/0,
+          /*expected_flush_per_period=*/1},
+         {"Default", /*is_feature_enabled=*/true, /*is_deprovisioned=*/false,
+          /*is_affiliated=*/true, /*info_policy_enabled=*/true,
+          /*telemetry_policy_enabled=*/true,
+          /*latency_verdict=*/RoutineVerdict::PROBLEM,
+          /*expected_info_count=*/1,
+          /*expected_telemetry_count=*/1, /*expected_event_count=*/1,
+          /*expected_flush_per_period=*/1}}),
+    [](const testing::TestParamInfo<NetworkHealthReportingTest::ParamType>&
+           info) { return info.param.test_name; });
 
-TEST(MetricReportingManagerTest, NetworkCollectors_NotAffiliated) {
-  NetworkCollectorsTestHelper(
-      /*is_affiliated=*/false,
-      /*enabled_features=*/
-      {MetricReportingManager::kEnableNetworkTelemetryReporting},
-      /*disabled_features=*/{},
-      /*telemetry_policy_enabled=*/true,
-      /*telemetry_collection_rate_ms=*/60000,
-      /*time_forward=*/base::Milliseconds(120000),
-      /*expected_telemetry_reports_count=*/0ul);
-}
-
-TEST(MetricReportingManagerTest, NetworkCollectors_Default) {
-  NetworkCollectorsTestHelper(
-      /*is_affiliated=*/true,
-      /*enabled_features=*/
-      {MetricReportingManager::kEnableNetworkTelemetryReporting},
-      /*disabled_features=*/{},
-      /*telemetry_policy_enabled=*/true,
-      /*telemetry_collection_rate_ms=*/60000,
-      /*time_forward=*/base::Milliseconds(120000),
-      /*expected_telemetry_reports_count=*/2ul);
-}
+}  // namespace
 }  // namespace reporting
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler.cc
new file mode 100644
index 0000000..984f22d
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler.cc
@@ -0,0 +1,91 @@
+// 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 "chrome/browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler.h"
+
+#include <utility>
+
+#include "ash/constants/ash_features.h"
+#include "chromeos/dbus/hermes/hermes_euicc_client.h"
+#include "chromeos/dbus/hermes/hermes_manager_client.h"
+#include "chromeos/network/device_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/network_type_pattern.h"
+#include "components/reporting/proto/synced/metric_data.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace reporting {
+namespace {
+
+absl::optional<NetworkDeviceType> GetNetworkDeviceType(
+    const ::ash::NetworkTypePattern& type) {
+  if (type.Equals(::ash::NetworkTypePattern::Cellular())) {
+    return NetworkDeviceType::CELLULAR_DEVICE;
+  }
+  if (type.MatchesPattern(::ash::NetworkTypePattern::EthernetOrEthernetEAP())) {
+    return NetworkDeviceType::ETHERNET_DEVICE;
+  }
+  if (type.Equals(::ash::NetworkTypePattern::WiFi())) {
+    return NetworkDeviceType::WIFI_DEVICE;
+  }
+  return absl::nullopt;
+}
+
+}  // namespace
+
+void NetworkInfoSampler::Collect(MetricCallback callback) {
+  ::ash::NetworkStateHandler::DeviceStateList device_list;
+  ::ash::NetworkStateHandler* network_state_handler =
+      ::ash::NetworkHandler::Get()->network_state_handler();
+  network_state_handler->GetDeviceList(&device_list);
+
+  MetricData metric_data;
+  auto* const networks_info =
+      metric_data.mutable_info_data()->mutable_networks_info();
+  for (const auto* device : device_list) {
+    auto type = ::ash::NetworkTypePattern::Primitive(device->type());
+    absl::optional<NetworkDeviceType> device_type = GetNetworkDeviceType(type);
+    if (!device_type.has_value()) {
+      continue;
+    }
+
+    auto* const interface = networks_info->add_network_interfaces();
+    interface->set_type(device_type.value());
+    if (!device->mac_address().empty()) {
+      interface->set_mac_address(device->mac_address());
+    }
+    if (!device->meid().empty()) {
+      interface->set_meid(device->meid());
+    }
+    if (!device->imei().empty()) {
+      interface->set_imei(device->imei());
+    }
+    if (!device->mdn().empty()) {
+      interface->set_mdn(device->mdn());
+    }
+    if (!device->iccid().empty()) {
+      interface->set_iccid(device->iccid());
+    }
+    if (!device->path().empty()) {
+      interface->set_device_path(device->path());
+    }
+
+    // Report EIDs for cellular connections.
+    if (type.Equals(::ash::NetworkTypePattern::Cellular()) &&
+        ::ash::features::IsESimPolicyEnabled()) {
+      for (const auto& euicc_path :
+           ::chromeos::HermesManagerClient::Get()->GetAvailableEuiccs()) {
+        ::chromeos::HermesEuiccClient::Properties* properties =
+            ::chromeos::HermesEuiccClient::Get()->GetProperties(euicc_path);
+        interface->add_eids(properties->eid().value());
+      }
+    }
+  }
+
+  if (!networks_info->network_interfaces().empty()) {
+    std::move(callback).Run(std::move(metric_data));
+  }
+}
+
+}  // namespace reporting
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler.h b/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler.h
new file mode 100644
index 0000000..42e46bc3
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler.h
@@ -0,0 +1,27 @@
+// 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 CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_NETWORK_NETWORK_INFO_SAMPLER_H_
+#define CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_NETWORK_NETWORK_INFO_SAMPLER_H_
+
+#include "components/reporting/metrics/sampler.h"
+
+namespace reporting {
+
+// Sampler to collect networks info.
+class NetworkInfoSampler : public Sampler {
+ public:
+  NetworkInfoSampler() = default;
+
+  NetworkInfoSampler(const NetworkInfoSampler&) = delete;
+  NetworkInfoSampler& operator=(const NetworkInfoSampler&) = delete;
+
+  ~NetworkInfoSampler() override = default;
+
+  void Collect(MetricCallback callback) override;
+};
+
+}  // namespace reporting
+
+#endif  // CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_NETWORK_NETWORK_INFO_SAMPLER_H_
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler_unittest.cc
new file mode 100644
index 0000000..e21ab535
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler_unittest.cc
@@ -0,0 +1,212 @@
+// 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 "chrome/browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler.h"
+
+#include <string>
+#include <utility>
+
+#include "ash/constants/ash_features.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "base/values.h"
+#include "chromeos/dbus/hermes/hermes_manager_client.h"
+#include "chromeos/dbus/shill/shill_device_client.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_handler_test_helper.h"
+#include "chromeos/network/network_state_handler.h"
+#include "dbus/object_path.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
+
+namespace reporting {
+namespace {
+
+constexpr char kEid0[] = "1234";
+constexpr char kEid1[] = "5678";
+constexpr char kEthernetPath[] = "ethernet/path";
+constexpr char kEthernetMac[] = "ethernet_mac";
+constexpr char kWifiPath[] = "wifi/path";
+constexpr char kWifiMac[] = "wifi_mac";
+constexpr char kCellularPath[] = "cellular/path";
+constexpr char kMeid[] = "12343";
+constexpr char kImei[] = "5689";
+constexpr char kIccid[] = "9876563";
+constexpr char kMdn[] = "134345";
+
+class NetworkInfoSamplerTest : public ::testing::Test {
+ protected:
+  NetworkInfoSamplerTest() = default;
+
+  NetworkInfoSamplerTest(const NetworkInfoSamplerTest&) = delete;
+  NetworkInfoSamplerTest& operator=(const NetworkInfoSamplerTest&) = delete;
+
+  ~NetworkInfoSamplerTest() override = default;
+
+  void SetUp() override {
+    device_client_ = network_handler_test_helper_.device_test();
+    device_client_->ClearDevices();
+  }
+
+  ::ash::ShillDeviceClient::TestInterface* device_client_;
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+
+  ::ash::NetworkHandlerTestHelper network_handler_test_helper_;
+};
+
+TEST_F(NetworkInfoSamplerTest, AllTypes) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(ash::features::kESimPolicy);
+  ::chromeos::HermesManagerClient::Get()->GetTestInterface()->AddEuicc(
+      dbus::ObjectPath("path0"), kEid0, true, 1);
+  ::chromeos::HermesManagerClient::Get()->GetTestInterface()->AddEuicc(
+      dbus::ObjectPath("path1"), kEid1, true, 2);
+
+  device_client_->AddDevice(kEthernetPath, shill::kTypeEthernet, "ethernet");
+  device_client_->SetDeviceProperty(kEthernetPath, shill::kAddressProperty,
+                                    base::Value(kEthernetMac),
+                                    /*notify_changed=*/true);
+
+  device_client_->AddDevice(kWifiPath, shill::kTypeWifi, "wifi");
+  device_client_->SetDeviceProperty(kWifiPath, shill::kAddressProperty,
+                                    base::Value(kWifiMac),
+                                    /*notify_changed=*/true);
+
+  device_client_->AddDevice(kCellularPath, shill::kTypeCellular, "cellular");
+  device_client_->SetDeviceProperty(kCellularPath, shill::kMeidProperty,
+                                    base::Value(kMeid),
+                                    /*notify_changed=*/true);
+  device_client_->SetDeviceProperty(kCellularPath, shill::kImeiProperty,
+                                    base::Value(kImei),
+                                    /*notify_changed=*/true);
+  device_client_->SetDeviceProperty(kCellularPath, shill::kIccidProperty,
+                                    base::Value(kIccid),
+                                    /*notify_changed=*/true);
+  device_client_->SetDeviceProperty(kCellularPath, shill::kMdnProperty,
+                                    base::Value(kMdn),
+                                    /*notify_changed=*/true);
+
+  base::RunLoop().RunUntilIdle();
+
+  MetricData result;
+  NetworkInfoSampler sampler;
+  sampler.Collect(base::BindLambdaForTesting(
+      [&](MetricData metric_data) { result = std::move(metric_data); }));
+
+  ASSERT_TRUE(result.has_info_data());
+  ASSERT_TRUE(result.info_data().has_networks_info());
+  ASSERT_EQ(result.info_data().networks_info().network_interfaces_size(), 3);
+  // Ethernet.
+  EXPECT_EQ(result.info_data().networks_info().network_interfaces(0).type(),
+            NetworkDeviceType::ETHERNET_DEVICE);
+  EXPECT_EQ(
+      result.info_data().networks_info().network_interfaces(0).mac_address(),
+      kEthernetMac);
+  EXPECT_EQ(
+      result.info_data().networks_info().network_interfaces(0).device_path(),
+      kEthernetPath);
+  EXPECT_FALSE(
+      result.info_data().networks_info().network_interfaces(0).has_meid());
+  EXPECT_FALSE(
+      result.info_data().networks_info().network_interfaces(0).has_imei());
+  EXPECT_FALSE(
+      result.info_data().networks_info().network_interfaces(0).has_iccid());
+  EXPECT_FALSE(
+      result.info_data().networks_info().network_interfaces(0).has_mdn());
+  EXPECT_TRUE(
+      result.info_data().networks_info().network_interfaces(0).eids().empty());
+  // Wifi.
+  EXPECT_EQ(result.info_data().networks_info().network_interfaces(1).type(),
+            NetworkDeviceType::WIFI_DEVICE);
+  EXPECT_EQ(
+      result.info_data().networks_info().network_interfaces(1).mac_address(),
+      kWifiMac);
+  EXPECT_EQ(
+      result.info_data().networks_info().network_interfaces(1).device_path(),
+      kWifiPath);
+  EXPECT_FALSE(
+      result.info_data().networks_info().network_interfaces(1).has_meid());
+  EXPECT_FALSE(
+      result.info_data().networks_info().network_interfaces(1).has_imei());
+  EXPECT_FALSE(
+      result.info_data().networks_info().network_interfaces(1).has_iccid());
+  EXPECT_FALSE(
+      result.info_data().networks_info().network_interfaces(1).has_mdn());
+  EXPECT_TRUE(
+      result.info_data().networks_info().network_interfaces(1).eids().empty());
+  // Cellular.
+  EXPECT_EQ(result.info_data().networks_info().network_interfaces(2).type(),
+            NetworkDeviceType::CELLULAR_DEVICE);
+  EXPECT_EQ(result.info_data().networks_info().network_interfaces(2).meid(),
+            kMeid);
+  EXPECT_EQ(result.info_data().networks_info().network_interfaces(2).imei(),
+            kImei);
+  EXPECT_EQ(result.info_data().networks_info().network_interfaces(2).iccid(),
+            kIccid);
+  EXPECT_EQ(result.info_data().networks_info().network_interfaces(2).mdn(),
+            kMdn);
+  EXPECT_EQ(
+      result.info_data().networks_info().network_interfaces(2).device_path(),
+      kCellularPath);
+  EXPECT_FALSE(result.info_data()
+                   .networks_info()
+                   .network_interfaces(2)
+                   .has_mac_address());
+  ASSERT_EQ(
+      result.info_data().networks_info().network_interfaces(2).eids_size(), 2);
+  EXPECT_EQ(result.info_data().networks_info().network_interfaces(2).eids(0),
+            kEid0);
+  EXPECT_EQ(result.info_data().networks_info().network_interfaces(2).eids(1),
+            kEid1);
+}
+
+TEST_F(NetworkInfoSamplerTest, Cellular_ESimPolicyDisabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(ash::features::kESimPolicy);
+  ::chromeos::HermesManagerClient::Get()->GetTestInterface()->AddEuicc(
+      dbus::ObjectPath("path1"), kEid0, true, 1);
+
+  device_client_->AddDevice(kCellularPath, shill::kTypeCellular, "cellular");
+  device_client_->SetDeviceProperty(kCellularPath, shill::kMeidProperty,
+                                    base::Value(kMeid),
+                                    /*notify_changed=*/true);
+
+  base::RunLoop().RunUntilIdle();
+
+  MetricData result;
+  NetworkInfoSampler sampler;
+  sampler.Collect(base::BindLambdaForTesting(
+      [&](MetricData metric_data) { result = std::move(metric_data); }));
+
+  ASSERT_TRUE(result.has_info_data());
+  ASSERT_TRUE(result.info_data().has_networks_info());
+  ASSERT_EQ(result.info_data().networks_info().network_interfaces_size(), 1);
+  EXPECT_EQ(result.info_data().networks_info().network_interfaces(0).type(),
+            NetworkDeviceType::CELLULAR_DEVICE);
+  EXPECT_EQ(result.info_data().networks_info().network_interfaces(0).meid(),
+            kMeid);
+  EXPECT_EQ(
+      result.info_data().networks_info().network_interfaces(0).device_path(),
+      kCellularPath);
+  EXPECT_FALSE(result.info_data()
+                   .networks_info()
+                   .network_interfaces(0)
+                   .has_mac_address());
+  EXPECT_FALSE(
+      result.info_data().networks_info().network_interfaces(0).has_imei());
+  EXPECT_FALSE(
+      result.info_data().networks_info().network_interfaces(0).has_iccid());
+  EXPECT_FALSE(
+      result.info_data().networks_info().network_interfaces(0).has_mdn());
+  // No eid reported, feature is disabled.
+  EXPECT_TRUE(
+      result.info_data().networks_info().network_interfaces(0).eids().empty());
+}
+
+}  // namespace
+}  // namespace reporting
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler.cc
index 9c90213..0adaba621 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler.cc
@@ -7,9 +7,11 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_type_pattern.h"
+#include "chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom.h"
 #include "components/reporting/proto/synced/metric_data.pb.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 
@@ -98,7 +100,11 @@
     }
   }
 
-  std::move(callback).Run(metric_data);
+  CrosHealthdMetricSampler sampler(
+      chromeos::cros_healthd::mojom::ProbeCategoryEnum::kNetworkInterface,
+      ::reporting::CrosHealthdMetricSampler::MetricType::kTelemetry);
+  sampler.SetMetricData(std::move(metric_data));
+  sampler.Collect(std::move(callback));
 }
 }  // namespace
 
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler_unittest.cc
index 4fe3281..ee9eafae 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler_unittest.cc
@@ -13,20 +13,35 @@
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "base/values.h"
+#include "chromeos/dbus/cros_healthd/cros_healthd_client.h"
+#include "chromeos/dbus/cros_healthd/fake_cros_healthd_client.h"
 #include "chromeos/dbus/shill/shill_ipconfig_client.h"
 #include "chromeos/dbus/shill/shill_service_client.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_handler_test_helper.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/tether_constants.h"
+#include "chromeos/services/cros_healthd/public/cpp/service_connection.h"
 #include "components/reporting/metrics/fake_sampler.h"
 #include "components/reporting/proto/synced/metric_data.pb.h"
+#include "components/reporting/util/test_support_callbacks.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 
 namespace reporting {
 namespace {
 
+// Wifi constants.
+constexpr char kInterfaceName[] = "interface_name";
+constexpr char kAccessPointAddress[] = "access_point";
+constexpr bool kPowerManagementEnabled = true;
+constexpr bool kEncryptionOn = true;
+constexpr int64_t kTxBitRateMbps = 8;
+constexpr int64_t kRxBitRateMbps = 4;
+constexpr int64_t kTxPowerDbm = 2;
+constexpr int64_t kLinkQuality = 1;
+constexpr int kSignalLevelDbm = 10;
+
 struct FakeNetworkData {
   std::string guid;
   std::string connection_state;
@@ -42,8 +57,6 @@
 
 TelemetryData NetworkTelemetrySamplerTestHelper(
     const std::vector<FakeNetworkData>& networks_data) {
-  base::test::SingleThreadTaskEnvironment task_environment;
-
   MetricData metric_data;
   auto* latency_data = metric_data.mutable_telemetry_data()
                            ->mutable_networks_telemetry()
@@ -105,10 +118,9 @@
   TelemetryData result;
   NetworkTelemetrySampler network_telemetry_sampler(
       https_latency_sampler.get());
-  network_telemetry_sampler.Collect(
-      base::BindLambdaForTesting([&result](MetricData metric_data) {
-        result = std::move(metric_data.telemetry_data());
-      }));
+  test::TestEvent<MetricData> metric_collect_event;
+  network_telemetry_sampler.Collect(metric_collect_event.cb());
+  result = metric_collect_event.result().telemetry_data();
 
   EXPECT_EQ(result.networks_telemetry().https_latency_data().verdict(),
             latency_data->verdict());
@@ -119,7 +131,56 @@
   return result;
 }
 
-TEST(NetworkTelemetrySamplerTest, CellularConnecting) {
+chromeos::cros_healthd::mojom::TelemetryInfoPtr CreateWifiResult(
+    const std::string& interface_name,
+    bool power_management_enabled,
+    const std::string& access_point_address,
+    int64_t tx_bit_rate_mbps,
+    int64_t rx_bit_rate_mbps,
+    int64_t tx_power_dbm,
+    bool encryption_on,
+    int64_t link_quality,
+    int signal_level_dbm) {
+  auto telemetry_info = chromeos::cros_healthd::mojom::TelemetryInfo::New();
+  std::vector<chromeos::cros_healthd::mojom::NetworkInterfaceInfoPtr>
+      network_interfaces;
+
+  auto wireless_link_info =
+      chromeos::cros_healthd::mojom::WirelessLinkInfo::New(
+          access_point_address, tx_bit_rate_mbps, rx_bit_rate_mbps,
+          tx_power_dbm, encryption_on, link_quality, signal_level_dbm);
+  auto wireless_interface_info =
+      chromeos::cros_healthd::mojom::WirelessInterfaceInfo::New(
+          interface_name, power_management_enabled,
+          std::move(wireless_link_info));
+  network_interfaces.push_back(
+      chromeos::cros_healthd::mojom::NetworkInterfaceInfo::
+          NewWirelessInterfaceInfo(std::move(wireless_interface_info)));
+  auto network_interface_result =
+      chromeos::cros_healthd::mojom::NetworkInterfaceResult::
+          NewNetworkInterfaceInfo(std::move(network_interfaces));
+
+  telemetry_info->network_interface_result =
+      std::move(network_interface_result);
+  return telemetry_info;
+}
+
+class NetworkTelemetrySamplerTest : public testing::Test {
+ public:
+  NetworkTelemetrySamplerTest() {
+    chromeos::CrosHealthdClient::InitializeFake();
+  }
+
+  ~NetworkTelemetrySamplerTest() override {
+    chromeos::CrosHealthdClient::Shutdown();
+    chromeos::cros_healthd::ServiceConnection::GetInstance()->FlushForTesting();
+  }
+
+ protected:
+  base::test::SingleThreadTaskEnvironment task_environment_;
+};
+
+TEST_F(NetworkTelemetrySamplerTest, CellularConnecting) {
   const std::vector<FakeNetworkData> networks_data = {
       {"guid1", shill::kStateConfiguration, shill::kTypeCellular,
        0 /* signal_strength */, "device/path", "192.168.86.25" /* ip_address */,
@@ -146,7 +207,7 @@
             NetworkType::CELLULAR);
 }
 
-TEST(NetworkTelemetrySamplerTest, VpnInvisibleNotConnected) {
+TEST_F(NetworkTelemetrySamplerTest, VpnInvisibleNotConnected) {
   const std::vector<FakeNetworkData> networks_data = {
       {"guid1", shill::kStateOffline, shill::kTypeVPN, 0 /* signal_strength */,
        "device/path", "192.168.86.25" /* ip_address */,
@@ -173,7 +234,7 @@
             NetworkType::VPN);
 }
 
-TEST(NetworkTelemetrySamplerTest, EthernetPortal) {
+TEST_F(NetworkTelemetrySamplerTest, EthernetPortal) {
   const std::vector<FakeNetworkData> networks_data = {
       {"guid1", shill::kStateRedirectFound, shill::kTypeEthernet,
        0 /* signal_strength */, "device/path", "192.168.86.25" /* ip_address */,
@@ -200,7 +261,7 @@
             NetworkType::ETHERNET);
 }
 
-TEST(NetworkTelemetrySamplerTest, MixTypesAndConfigurations) {
+TEST_F(NetworkTelemetrySamplerTest, MixTypesAndConfigurations) {
   const std::vector<FakeNetworkData> networks_data = {
       {"guid1", shill::kStateReady, shill::kTypeWifi, 10 /* signal_strength */,
        "device/path1", "192.168.86.25" /* ip_address */,
@@ -215,6 +276,12 @@
        "192.168.86.27" /* ip_address */, "192.168.86.3" /* gateway */,
        false /* is_portal */, true /* is_visible */, true /* is_configured */}};
 
+  auto telemetry_info = CreateWifiResult(
+      kInterfaceName, kPowerManagementEnabled, kAccessPointAddress,
+      kTxBitRateMbps, kRxBitRateMbps, kTxPowerDbm, kEncryptionOn, kLinkQuality,
+      kSignalLevelDbm);
+  chromeos::cros_healthd::FakeCrosHealthdClient::Get()
+      ->SetProbeTelemetryInfoResponseForTesting(telemetry_info);
   TelemetryData result = NetworkTelemetrySamplerTestHelper(networks_data);
 
   // Not configured network is not included
@@ -237,6 +304,24 @@
   EXPECT_EQ(result.networks_telemetry().network_telemetry(0).type(),
             NetworkType::WIFI);
 
+  ASSERT_TRUE(result.networks_telemetry()
+                  .network_telemetry(0)
+                  .network_interface_telemetry(0)
+                  .has_wireless_interface());
+  const auto& wireless_interface = result.networks_telemetry()
+                                       .network_telemetry(0)
+                                       .network_interface_telemetry(0)
+                                       .wireless_interface();
+  EXPECT_EQ(wireless_interface.power_management_enabled(),
+            kPowerManagementEnabled);
+  EXPECT_EQ(wireless_interface.access_point_address(), kAccessPointAddress);
+  EXPECT_EQ(wireless_interface.tx_bit_rate_mbps(), kTxBitRateMbps);
+  EXPECT_EQ(wireless_interface.rx_bit_rate_mbps(), kRxBitRateMbps);
+  EXPECT_EQ(wireless_interface.tx_power_dbm(), kTxPowerDbm);
+  EXPECT_EQ(wireless_interface.encryption_on(), kEncryptionOn);
+  EXPECT_EQ(wireless_interface.link_quality(), kLinkQuality);
+  EXPECT_EQ(wireless_interface.signal_level_dbm(), kSignalLevelDbm);
+
   // TETHER
   EXPECT_EQ(result.networks_telemetry().network_telemetry(1).guid(),
             networks_data[2].guid);
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/usb/usb_events_observer.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/usb/usb_events_observer.cc
new file mode 100644
index 0000000..e4035d9
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/usb/usb_events_observer.cc
@@ -0,0 +1,50 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/usb/usb_events_observer.h"
+#include "chromeos/services/cros_healthd/public/cpp/service_connection.h"
+#include "components/reporting/proto/synced/metric_data.pb.h"
+
+using UsbEventInfoPtr = chromeos::cros_healthd::mojom::UsbEventInfoPtr;
+
+namespace reporting {
+
+UsbEventsObserver::UsbEventsObserver()
+    : CrosHealthdEventsObserverBase<
+          chromeos::cros_healthd::mojom::CrosHealthdUsbObserver>(this) {}
+
+UsbEventsObserver::~UsbEventsObserver() = default;
+void UsbEventsObserver::OnAdd(UsbEventInfoPtr info) {
+  MetricData metric_data;
+  metric_data.mutable_event_data()->set_type(MetricEventType::USB_ADDED);
+  FillUsbEventData(metric_data.mutable_event_data()->mutable_usb_event_data(),
+                   std::move(info));
+  OnEventObserved(std::move(metric_data));
+}
+
+void UsbEventsObserver::OnRemove(UsbEventInfoPtr info) {
+  MetricData metric_data;
+  metric_data.mutable_event_data()->set_type(MetricEventType::USB_REMOVED);
+  FillUsbEventData(metric_data.mutable_event_data()->mutable_usb_event_data(),
+                   std::move(info));
+  OnEventObserved(std::move(metric_data));
+}
+
+void UsbEventsObserver::AddObserver() {
+  chromeos::cros_healthd::ServiceConnection::GetInstance()->AddUsbObserver(
+      BindNewPipeAndPassRemote());
+}
+
+void UsbEventsObserver::FillUsbEventData(UsbEventData* data,
+                                         UsbEventInfoPtr info) {
+  data->set_vendor(info->vendor);
+  data->set_name(info->name);
+  data->set_pid(info->pid);
+  data->set_vid(info->vid);
+
+  for (auto& category : info->categories) {
+    data->add_categories(category);
+  }
+}
+}  // namespace reporting
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/usb/usb_events_observer.h b/chrome/browser/ash/policy/reporting/metrics_reporting/usb/usb_events_observer.h
new file mode 100644
index 0000000..02a5667
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/usb/usb_events_observer.h
@@ -0,0 +1,41 @@
+// 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 CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_USB_USB_EVENTS_OBSERVER_H_
+#define CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_USB_USB_EVENTS_OBSERVER_H_
+
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_events_observer_base.h"
+#include "chromeos/services/cros_healthd/public/mojom/cros_healthd_events.mojom.h"
+
+using UsbEventInfoPtr = chromeos::cros_healthd::mojom::UsbEventInfoPtr;
+
+namespace reporting {
+
+class UsbEventsObserver
+    : public reporting::CrosHealthdEventsObserverBase<
+          chromeos::cros_healthd::mojom::CrosHealthdUsbObserver>,
+      public chromeos::cros_healthd::mojom::CrosHealthdUsbObserver {
+ public:
+  UsbEventsObserver();
+
+  UsbEventsObserver(const UsbEventsObserver& other) = delete;
+  UsbEventsObserver& operator=(const UsbEventsObserver& other) = delete;
+
+  ~UsbEventsObserver() override;
+
+  // chromeos::cros_healthd::mojom::CrosHealthdUsbObserver:
+  void OnAdd(UsbEventInfoPtr info) override;
+
+  void OnRemove(UsbEventInfoPtr info) override;
+
+ protected:
+  // CrosHealthdEventsObserverBase
+  void AddObserver() override;
+
+ private:
+  void FillUsbEventData(UsbEventData* data, UsbEventInfoPtr info);
+};
+}  // namespace reporting
+
+#endif  // CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_USB_USB_EVENTS_OBSERVER_H_
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/usb/usb_events_observer_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/usb/usb_events_observer_unittest.cc
new file mode 100644
index 0000000..84c8519
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/usb/usb_events_observer_unittest.cc
@@ -0,0 +1,158 @@
+// 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 "chrome/browser/ash/policy/reporting/metrics_reporting/usb/usb_events_observer.h"
+#include <sys/types.h>
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "chromeos/dbus/cros_healthd/cros_healthd_client.h"
+#include "chromeos/dbus/cros_healthd/fake_cros_healthd_client.h"
+#include "chromeos/services/cros_healthd/public/cpp/service_connection.h"
+#include "chromeos/services/cros_healthd/public/mojom/cros_healthd.mojom.h"
+#include "components/reporting/util/test_support_callbacks.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::Eq;
+using testing::IsEmpty;
+using testing::Not;
+using ::testing::Pointwise;
+using ::testing::StrEq;
+using UsbEventInfoPtr = chromeos::cros_healthd::mojom::UsbEventInfoPtr;
+using UsbEventInfo = chromeos::cros_healthd::mojom::UsbEventInfo;
+
+namespace reporting {
+namespace {
+
+static constexpr int32_t kTestVid = 0xffee;
+static constexpr int32_t kTestPid = 0x0;
+static constexpr char kTestName[] = "TestName";
+static constexpr char kTestVendor[] = "TestVendor";
+static constexpr char kTestCategory1[] = "TestCategory1";
+static constexpr char kTestCategory2[] = "TestCategory2";
+const std::vector<std::string> kTestCategories = {kTestCategory1,
+                                                  kTestCategory2};
+
+class UsbEventsObserverTest : public ::testing::Test {
+ public:
+  UsbEventsObserverTest() = default;
+
+  UsbEventsObserverTest(const UsbEventsObserverTest&) = delete;
+  UsbEventsObserverTest& operator=(const UsbEventsObserverTest&) = delete;
+
+  ~UsbEventsObserverTest() override = default;
+
+  void SetUp() override { ::chromeos::CrosHealthdClient::InitializeFake(); }
+
+  void TearDown() override {
+    ::chromeos::CrosHealthdClient::Shutdown();
+
+    // Wait for ServiceConnection to observe the destruction of the client.
+    ::chromeos::cros_healthd::ServiceConnection::GetInstance()
+        ->FlushForTesting();
+  }
+
+  UsbEventInfoPtr test_usb_event_info = UsbEventInfo::New(kTestVendor,
+                                                          kTestName,
+                                                          kTestVid,
+                                                          kTestPid,
+                                                          kTestCategories);
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+};
+
+TEST_F(UsbEventsObserverTest, UsbOnAdd) {
+  MetricData metric_data;
+  UsbEventsObserver usb_observer;
+  auto cb = base::BindLambdaForTesting(
+      [&](MetricData md) { metric_data = std::move(md); });
+
+  usb_observer.SetOnEventObservedCallback(std::move(cb));
+  usb_observer.SetReportingEnabled(true);
+  usb_observer.OnRemove(std::move(test_usb_event_info));
+
+  ASSERT_TRUE(metric_data.has_event_data());
+  ASSERT_TRUE(metric_data.event_data().has_usb_event_data());
+  EXPECT_TRUE(metric_data.event_data().usb_event_data().has_name());
+  EXPECT_TRUE(metric_data.event_data().usb_event_data().has_pid());
+  EXPECT_TRUE(metric_data.event_data().usb_event_data().has_vendor());
+  EXPECT_TRUE(metric_data.event_data().usb_event_data().has_vid());
+  EXPECT_THAT(metric_data.event_data().usb_event_data().categories(),
+              Not(IsEmpty()));
+  EXPECT_THAT(metric_data.event_data().usb_event_data().name(),
+              StrEq(kTestName));
+  EXPECT_THAT(metric_data.event_data().usb_event_data().pid(), Eq(kTestPid));
+  EXPECT_THAT(metric_data.event_data().usb_event_data().vendor(),
+              StrEq(kTestVendor));
+  EXPECT_THAT(metric_data.event_data().usb_event_data().vid(), Eq(kTestVid));
+  EXPECT_THAT(metric_data.event_data().usb_event_data().categories().size(),
+              Eq(kTestCategories.size()));
+
+  for (int i = 0; i < kTestCategories.size(); i++) {
+    EXPECT_THAT(metric_data.event_data().usb_event_data().categories()[i],
+                StrEq(kTestCategories[i]));
+  }
+
+  EXPECT_EQ(metric_data.event_data().type(), MetricEventType::USB_REMOVED);
+}
+
+TEST_F(UsbEventsObserverTest, UsbOnRemoved) {
+  MetricData metric_data;
+  UsbEventsObserver usb_observer;
+  auto cb = base::BindLambdaForTesting(
+      [&](MetricData md) { metric_data = std::move(md); });
+
+  usb_observer.SetOnEventObservedCallback(std::move(cb));
+  usb_observer.SetReportingEnabled(true);
+  usb_observer.OnAdd(std::move(test_usb_event_info));
+
+  ASSERT_TRUE(metric_data.has_event_data());
+  ASSERT_TRUE(metric_data.event_data().has_usb_event_data());
+  EXPECT_TRUE(metric_data.event_data().usb_event_data().has_name());
+  EXPECT_TRUE(metric_data.event_data().usb_event_data().has_pid());
+  EXPECT_TRUE(metric_data.event_data().usb_event_data().has_vendor());
+  EXPECT_TRUE(metric_data.event_data().usb_event_data().has_vid());
+  EXPECT_THAT(metric_data.event_data().usb_event_data().categories(),
+              Not(IsEmpty()));
+  EXPECT_THAT(metric_data.event_data().usb_event_data().name(),
+              StrEq(kTestName));
+  EXPECT_THAT(metric_data.event_data().usb_event_data().pid(), Eq(kTestPid));
+  EXPECT_THAT(metric_data.event_data().usb_event_data().vendor(),
+              StrEq(kTestVendor));
+  EXPECT_THAT(metric_data.event_data().usb_event_data().vid(), Eq(kTestVid));
+  EXPECT_THAT(metric_data.event_data().usb_event_data().categories().size(),
+              Eq(kTestCategories.size()));
+
+  for (int i = 0; i < kTestCategories.size(); i++) {
+    EXPECT_THAT(metric_data.event_data().usb_event_data().categories()[i],
+                StrEq(kTestCategories[i]));
+  }
+
+  EXPECT_EQ(metric_data.event_data().type(), MetricEventType::USB_ADDED);
+}
+
+TEST_F(UsbEventsObserverTest, UsbOnAddUsingFakeCrosHealthdClient) {
+  test::TestEvent<MetricData> result_metric_data;
+  UsbEventsObserver usb_observer;
+
+  usb_observer.SetOnEventObservedCallback(result_metric_data.cb());
+  usb_observer.SetReportingEnabled(true);
+
+  ::chromeos::cros_healthd::FakeCrosHealthdClient::Get()
+      ->EmitUsbAddEventForTesting();
+
+  const auto metric_data = result_metric_data.result();
+  ASSERT_TRUE(metric_data.has_event_data());
+  ASSERT_TRUE(metric_data.event_data().has_usb_event_data());
+  EXPECT_TRUE(metric_data.event_data().usb_event_data().has_name());
+  EXPECT_TRUE(metric_data.event_data().usb_event_data().has_pid());
+  EXPECT_TRUE(metric_data.event_data().usb_event_data().has_vendor());
+  EXPECT_TRUE(metric_data.event_data().usb_event_data().has_vid());
+  EXPECT_THAT(metric_data.event_data().usb_event_data().categories(),
+              IsEmpty());
+  EXPECT_EQ(metric_data.event_data().type(), MetricEventType::USB_ADDED);
+}
+}  // namespace
+}  // namespace reporting
diff --git a/chrome/browser/ash/policy/server_backed_state/server_backed_device_state.cc b/chrome/browser/ash/policy/server_backed_state/server_backed_device_state.cc
index 6ba0cf1..b85a2a60 100644
--- a/chrome/browser/ash/policy/server_backed_state/server_backed_device_state.cc
+++ b/chrome/browser/ash/policy/server_backed_state/server_backed_device_state.cc
@@ -41,9 +41,10 @@
 
 DeviceStateMode GetDeviceStateMode() {
   std::string device_state_mode;
-  g_browser_process->local_state()
-      ->GetDictionary(prefs::kServerBackedDeviceState)
-      ->GetString(kDeviceStateMode, &device_state_mode);
+  base::Value::AsDictionaryValue(
+      *g_browser_process->local_state()->GetDictionary(
+          prefs::kServerBackedDeviceState))
+      .GetString(kDeviceStateMode, &device_state_mode);
   if (device_state_mode.empty())
     return RESTORE_MODE_NONE;
   if (device_state_mode == kDeviceStateRestoreModeReEnrollmentRequested)
diff --git a/chrome/browser/ash/policy/status_collector/activity_storage.cc b/chrome/browser/ash/policy/status_collector/activity_storage.cc
index 260a8a9..8b4e972 100644
--- a/chrome/browser/ash/policy/status_collector/activity_storage.cc
+++ b/chrome/browser/ash/policy/status_collector/activity_storage.cc
@@ -251,7 +251,7 @@
 void ActivityStorage::ForEachActivityPeriodFromPref(
     const base::RepeatingCallback<
         void(const int64_t, const int64_t, const std::string&)>& f) const {
-  const base::DictionaryValue* stored_activity_periods =
+  const base::Value* stored_activity_periods =
       pref_service_->GetDictionary(pref_name_);
   for (const auto item : stored_activity_periods->DictItems()) {
     int64_t timestamp;
diff --git a/chrome/browser/ash/policy/status_collector/device_status_collector.cc b/chrome/browser/ash/policy/status_collector/device_status_collector.cc
index d00bc86..ed4bb32 100644
--- a/chrome/browser/ash/policy/status_collector/device_status_collector.cc
+++ b/chrome/browser/ash/policy/status_collector/device_status_collector.cc
@@ -2334,6 +2334,7 @@
 
 bool DeviceStatusCollector::GetNetworkConfiguration(
     em::DeviceStatusReportRequest* status) {
+  // Note: keep in sync with `::reporting::NetworkInfoSampler`
   static const struct {
     const char* type_string;
     em::NetworkInterface::NetworkDeviceType type_constant;
diff --git a/chrome/browser/ash/policy/status_collector/managed_session_service.cc b/chrome/browser/ash/policy/status_collector/managed_session_service.cc
index 6d7d66d..fcedbaa 100644
--- a/chrome/browser/ash/policy/status_collector/managed_session_service.cc
+++ b/chrome/browser/ash/policy/status_collector/managed_session_service.cc
@@ -136,7 +136,7 @@
   }
 }
 
-void ManagedSessionService::OnAuthFailure(const chromeos::AuthFailure& error) {
+void ManagedSessionService::OnAuthFailure(const ash::AuthFailure& error) {
   for (auto& observer : observers_) {
     observer.OnLoginFailure(error);
   }
diff --git a/chrome/browser/ash/policy/status_collector/managed_session_service.h b/chrome/browser/ash/policy/status_collector/managed_session_service.h
index a71d3d1..da434d17 100644
--- a/chrome/browser/ash/policy/status_collector/managed_session_service.h
+++ b/chrome/browser/ash/policy/status_collector/managed_session_service.h
@@ -27,7 +27,7 @@
 class ManagedSessionService : public session_manager::SessionManagerObserver,
                               public ProfileObserver,
                               public chromeos::PowerManagerClient::Observer,
-                              public chromeos::AuthStatusConsumer,
+                              public ash::AuthStatusConsumer,
                               public ash::UserAuthenticatorObserver,
                               public ash::SessionTerminationManager::Observer,
                               public user_manager::UserManager::Observer {
@@ -35,7 +35,7 @@
   class Observer : public base::CheckedObserver {
    public:
     // Occurs when a user's login attempt fails.
-    virtual void OnLoginFailure(const chromeos::AuthFailure& error) {}
+    virtual void OnLoginFailure(const ash::AuthFailure& error) {}
 
     // Occurs when a user has logged in.
     virtual void OnLogin(Profile* profile) {}
@@ -94,7 +94,7 @@
 
   void OnAuthSuccess(const ash::UserContext& user_context) override {}
 
-  void OnAuthFailure(const chromeos::AuthFailure& error) override;
+  void OnAuthFailure(const ash::AuthFailure& error) override;
 
   void OnAuthAttemptStarted() override;
 
diff --git a/chrome/browser/ash/policy/status_collector/managed_session_service_unittest.cc b/chrome/browser/ash/policy/status_collector/managed_session_service_unittest.cc
index b699b23..d7a06c7 100644
--- a/chrome/browser/ash/policy/status_collector/managed_session_service_unittest.cc
+++ b/chrome/browser/ash/policy/status_collector/managed_session_service_unittest.cc
@@ -68,7 +68,7 @@
 
   base::SimpleTestClock* test_clock() { return &test_clock_; }
 
-  void OnLoginFailure(const chromeos::AuthFailure& error) override {
+  void OnLoginFailure(const ash::AuthFailure& error) override {
     auth_failure_ = error;
   }
   void OnLogin(Profile* profile) override { logged_in_ = profile; }
@@ -79,8 +79,7 @@
     suspend_time_ = std::make_unique<base::Time>(time);
   }
 
-  chromeos::AuthFailure auth_failure_ =
-      chromeos::AuthFailure::AuthFailureNone();
+  ash::AuthFailure auth_failure_ = ash::AuthFailure::AuthFailureNone();
   Profile* logged_in_ = nullptr;
   Profile* logged_out_ = nullptr;
   bool locked_ = false;
@@ -244,11 +243,11 @@
 TEST_F(ManagedSessionServiceTest, LoginFailure) {
   managed_session_service()->AddObserver(this);
 
-  managed_session_service()->OnAuthFailure(chromeos::AuthFailure(
-      chromeos::AuthFailure::FailureReason::OWNER_REQUIRED));
+  managed_session_service()->OnAuthFailure(
+      ash::AuthFailure(ash::AuthFailure::FailureReason::OWNER_REQUIRED));
 
   EXPECT_EQ(auth_failure_.reason(),
-            chromeos::AuthFailure::FailureReason::OWNER_REQUIRED);
+            ash::AuthFailure::FailureReason::OWNER_REQUIRED);
 }
 
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/uploading/system_log_uploader.cc b/chrome/browser/ash/policy/uploading/system_log_uploader.cc
index 0c4fbb6d..52ce1855 100644
--- a/chrome/browser/ash/policy/uploading/system_log_uploader.cc
+++ b/chrome/browser/ash/policy/uploading/system_log_uploader.cc
@@ -524,7 +524,7 @@
   const base::Time now = base::Time::NowFromSystemTime();
   PrefService* local_state = g_browser_process->local_state();
 
-  const base::ListValue* prev_log_uploads =
+  const base::Value* prev_log_uploads =
       local_state->GetList(policy::prefs::kStoreLogStatesAcrossReboots);
 
   std::vector<base::Time> updated_log_uploads;
diff --git a/chrome/browser/ash/printing/calculators_policies_binder.cc b/chrome/browser/ash/printing/calculators_policies_binder.cc
index 4ba7bff..486b6158 100644
--- a/chrome/browser/ash/printing/calculators_policies_binder.cc
+++ b/chrome/browser/ash/printing/calculators_policies_binder.cc
@@ -72,7 +72,7 @@
   }
 
   std::vector<std::string> GetStringList(const char* name) const override {
-    return ConvertToVector(prefs_->GetList(name));
+    return ConvertToVector(&base::Value::AsListValue(*prefs_->GetList(name)));
   }
 
  private:
diff --git a/chrome/browser/ash/printing/enterprise_printers_provider.cc b/chrome/browser/ash/printing/enterprise_printers_provider.cc
index 57afa70..50f86fc 100644
--- a/chrome/browser/ash/printing/enterprise_printers_provider.cc
+++ b/chrome/browser/ash/printing/enterprise_printers_provider.cc
@@ -29,9 +29,9 @@
 
 namespace {
 
-std::vector<std::string> ConvertToVector(const base::ListValue* list) {
+std::vector<std::string> ConvertToVector(const base::Value* list) {
   std::vector<std::string> string_list;
-  if (list) {
+  if (list && list->is_list()) {
     for (const base::Value& value : list->GetList()) {
       if (value.is_string()) {
         string_list.push_back(value.GetString());
diff --git a/chrome/browser/ash/system/device_disabling_manager.cc b/chrome/browser/ash/system/device_disabling_manager.cc
index 17718f5f..b795bd9 100644
--- a/chrome/browser/ash/system/device_disabling_manager.cc
+++ b/chrome/browser/ash/system/device_disabling_manager.cc
@@ -126,10 +126,10 @@
 
   // Update the enrollment domain.
   enrollment_domain_.clear();
-  g_browser_process->local_state()->GetDictionary(
-      prefs::kServerBackedDeviceState)->GetString(
-          policy::kDeviceStateManagementDomain,
-          &enrollment_domain_);
+  base::Value::AsDictionaryValue(
+      *g_browser_process->local_state()->GetDictionary(
+          prefs::kServerBackedDeviceState))
+      .GetString(policy::kDeviceStateManagementDomain, &enrollment_domain_);
 
   // Update the serial number.
   serial_number_ = chromeos::system::StatisticsProvider::GetInstance()
@@ -137,10 +137,10 @@
 
   // Update the disabled message.
   std::string disabled_message;
-  g_browser_process->local_state()->GetDictionary(
-      prefs::kServerBackedDeviceState)->GetString(
-          policy::kDeviceStateDisabledMessage,
-          &disabled_message);
+  base::Value::AsDictionaryValue(
+      *g_browser_process->local_state()->GetDictionary(
+          prefs::kServerBackedDeviceState))
+      .GetString(policy::kDeviceStateDisabledMessage, &disabled_message);
   CacheDisabledMessageAndNotify(disabled_message);
 
   // Indicate that the device is disabled.
diff --git a/chrome/browser/ash/wallpaper_handlers/mock_wallpaper_handlers.cc b/chrome/browser/ash/wallpaper_handlers/mock_wallpaper_handlers.cc
new file mode 100644
index 0000000..75b96bf
--- /dev/null
+++ b/chrome/browser/ash/wallpaper_handlers/mock_wallpaper_handlers.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 "chrome/browser/ash/wallpaper_handlers/mock_wallpaper_handlers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace wallpaper_handlers {
+
+MockGooglePhotosCountFetcher::MockGooglePhotosCountFetcher(Profile* profile)
+    : GooglePhotosCountFetcher(profile) {
+  ON_CALL(*this, AddCallbackAndStartIfNecessary)
+      .WillByDefault([](OnGooglePhotosCountFetched callback) {
+        base::SequencedTaskRunnerHandle::Get()->PostTask(
+            FROM_HERE, base::BindOnce(std::move(callback), /*count=*/0));
+      });
+}
+
+MockGooglePhotosCountFetcher::~MockGooglePhotosCountFetcher() = default;
+
+}  // namespace wallpaper_handlers
diff --git a/chrome/browser/ash/wallpaper_handlers/mock_wallpaper_handlers.h b/chrome/browser/ash/wallpaper_handlers/mock_wallpaper_handlers.h
new file mode 100644
index 0000000..8f20cbbc
--- /dev/null
+++ b/chrome/browser/ash/wallpaper_handlers/mock_wallpaper_handlers.h
@@ -0,0 +1,37 @@
+// 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 CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_MOCK_WALLPAPER_HANDLERS_H_
+#define CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_MOCK_WALLPAPER_HANDLERS_H_
+
+#include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
+#include "chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace wallpaper_handlers {
+
+// Fetcher that returns a dummy value for the number of photos in a user's
+// Google Photos library. Used to avoid network requests in unit tests.
+class MockGooglePhotosCountFetcher : public GooglePhotosCountFetcher {
+ public:
+  explicit MockGooglePhotosCountFetcher(Profile* profile);
+
+  MockGooglePhotosCountFetcher(const MockGooglePhotosCountFetcher&) = delete;
+  MockGooglePhotosCountFetcher& operator=(const MockGooglePhotosCountFetcher&) =
+      delete;
+
+  ~MockGooglePhotosCountFetcher() override;
+
+  // GooglePhotosCountFetcher:
+  using OnGooglePhotosCountFetched = ash::personalization_app::mojom::
+      WallpaperProvider::FetchGooglePhotosCountCallback;
+  MOCK_METHOD(void,
+              AddCallbackAndStartIfNecessary,
+              (OnGooglePhotosCountFetched callback),
+              (override));
+};
+
+}  // namespace wallpaper_handlers
+
+#endif  // CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_MOCK_WALLPAPER_HANDLERS_H_
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
index 8cd967c..5f704efd 100644
--- a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
+++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
@@ -4,19 +4,34 @@
 
 #include "chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h"
 
+#include <tuple>
+#include <utility>
+
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
 #include "ash/constants/devicetype.h"
 #include "ash/webui/personalization_app/proto/backdrop_wallpaper.pb.h"
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/string_util.h"
+#include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/net/system_network_context_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/common/extensions/api/wallpaper_private.h"
+#include "components/signin/public/base/consent_level.h"
+#include "components/signin/public/identity_manager/access_token_info.h"
+#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
+#include "components/signin/public/identity_manager/scope_set.h"
 #include "content/public/browser/browser_thread.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "google_apis/gaia/google_service_auth_error.h"
 #include "net/base/load_flags.h"
 #include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "url/gurl.h"
 
@@ -46,6 +61,30 @@
 // The label used to return exclusive content for Google branded chromebooks.
 constexpr char kGoogleDeviceFilteringLabel[] = "google_branded_chromebook";
 
+// The URL to download the number of photos in a user's Google Photos library.
+constexpr char kGooglePhotosCountUrl[] =
+    "https://photosfirstparty-pa.googleapis.com/v1/chromeos/user:read";
+
+constexpr net::NetworkTrafficAnnotationTag kGooglePhotosCountTrafficAnnotation =
+    net::DefineNetworkTrafficAnnotation("wallpaper_google_photos_count",
+                                        R"(
+      semantics {
+        sender: "ChromeOS Wallpaper Picker"
+        description:
+          "The ChromeOS Wallpaper Picker displays a tile to view and pick from "
+          "a user's Google Photos library. The tile shows the library's number "
+          "of photos, which this query fetches."
+        trigger: "When the user opens the ChromeOS Wallpaper Picker app."
+        data: "None."
+        destination: GOOGLE_OWNED_SERVICE
+      }
+      policy {
+        cookies_allowed: NO
+        setting: "N/A"
+        policy_exception_justification:
+          "Not implemented, considered not necessary."
+      })");
+
 // Returns the corresponding test url if |kTestWallpaperServer| is present,
 // otherwise returns |url| as is. See https://crbug.com/914144.
 std::string MaybeConvertToTestUrl(std::string url) {
@@ -170,21 +209,18 @@
         semantics {
           sender: "ChromeOS Wallpaper Picker"
           description:
-            "The ChromeOS Wallpaper Picker extension displays a rich set of "
+            "The ChromeOS Wallpaper Picker app displays a rich set of "
             "wallpapers for users to choose from. Each wallpaper belongs to a "
             "collection (e.g. Arts, Landscape etc.). The list of all available "
             "collections is downloaded from the Backdrop wallpaper service."
-          trigger:
-            "When ChromeOS Wallpaper Picker extension is open, and "
-            "BUILDFLAG(GOOGLE_CHROME_BRANDING) is defined."
+          trigger: "When the user opens the ChromeOS Wallpaper Picker app."
           data:
             "The Backdrop protocol buffer messages. No user data is included."
           destination: GOOGLE_OWNED_SERVICE
         }
         policy {
           cookies_allowed: NO
-          setting:
-            "NA"
+          setting: "N/A"
           policy_exception_justification:
             "Not implemented, considered not necessary."
         })");
@@ -249,17 +285,15 @@
             "and descriptive texts for each image. Such information is "
             "downloaded from the Backdrop wallpaper service."
           trigger:
-            "When ChromeOS Wallpaper Picker extension is open, "
-            "BUILDFLAG(GOOGLE_CHROME_BRANDING) is defined and user clicks on a "
-            "particular collection."
+            "When the ChromeOS Wallpaper Picker app is open and the user "
+            "clicks on a particular collection."
           data:
             "The Backdrop protocol buffer messages. No user data is included."
           destination: GOOGLE_OWNED_SERVICE
         }
         policy {
           cookies_allowed: NO
-          setting:
-            "NA"
+          setting: "N/A"
           policy_exception_justification:
             "Not implemented, considered not necessary."
         })");
@@ -328,17 +362,15 @@
             "periodically changed to a random wallpaper selected by the "
             "Backdrop wallpaper service."
           trigger:
-            "When ChromeOS Wallpaper Picker extension is open, "
-            "BUILDFLAG(GOOGLE_CHROME_BRANDING) is defined and user turns on "
-            "the surprise me feature."
+            "When the ChromeOS Wallpaper Picker app is open and the user turns "
+            "on the surprise me feature."
           data:
             "The Backdrop protocol buffer messages. No user data is included."
           destination: GOOGLE_OWNED_SERVICE
         }
         policy {
           cookies_allowed: NO
-          setting:
-            "NA"
+          setting: "N/A"
           policy_exception_justification:
             "Not implemented, considered not necessary."
         })");
@@ -370,21 +402,131 @@
                            surprise_me_image_response.resume_token());
 }
 
-GooglePhotosCountFetcher::GooglePhotosCountFetcher() {
+template <typename... Args>
+GooglePhotosFetcher<Args...>::GooglePhotosFetcher(
+    Profile* profile,
+    const char* service_url,
+    const net::NetworkTrafficAnnotationTag& traffic_annotation)
+    : profile_(profile),
+      identity_manager_(IdentityManagerFactory::GetForProfile(profile)),
+      service_url_(service_url),
+      traffic_annotation_(traffic_annotation) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(profile_);
+  DCHECK(identity_manager_);
+  DCHECK(service_url_);
+  identity_manager_observation_.Observe(identity_manager_);
+}
+
+template <typename... Args>
+GooglePhotosFetcher<Args...>::~GooglePhotosFetcher() = default;
+
+template <typename... Args>
+void GooglePhotosFetcher<Args...>::AddCallbackAndStartIfNecessary(
+    ClientCallback callback) {
+  pending_client_callbacks_.push_back(std::move(callback));
+  if (pending_client_callbacks_.size() > 1)
+    return;
+
+  signin::ScopeSet scopes;
+  scopes.insert(GaiaConstants::kPhotosModuleOAuth2Scope);
+
+  DCHECK(!token_fetcher_);
+  token_fetcher_ = std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
+      "wallpaper_google_photos_fetcher", identity_manager_, scopes,
+      base::BindOnce(&GooglePhotosFetcher::OnTokenReceived,
+                     base::Unretained(this) /*`this` owns `token_fetcher_`.*/),
+      signin::PrimaryAccountAccessTokenFetcher::Mode::kImmediate,
+      signin::ConsentLevel::kSignin);
+}
+
+template <typename... Args>
+void GooglePhotosFetcher<Args...>::OnTokenReceived(
+    GoogleServiceAuthError error,
+    signin::AccessTokenInfo token_info) {
+  token_fetcher_.reset();
+  if (error.state() != GoogleServiceAuthError::NONE) {
+    OnResponseReady(absl::nullopt);
+    return;
+  }
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->method = "GET";
+  resource_request->url = GURL(service_url_);
+  // Cookies should not be allowed.
+  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
+  resource_request->headers.SetHeader(net::HttpRequestHeaders::kContentType,
+                                      "application/json");
+  resource_request->headers.SetHeader(net::HttpRequestHeaders::kAuthorization,
+                                      "Bearer " + token_info.token);
+
+  DCHECK(!url_loader_);
+  url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
+                                                 traffic_annotation_);
+  url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      profile_->GetURLLoaderFactory().get(),
+      base::BindOnce(&GooglePhotosFetcher::OnJsonReceived,
+                     base::Unretained(this) /*`this` owns `url_loader_`.*/));
+}
+
+template <typename... Args>
+void GooglePhotosFetcher<Args...>::OnJsonReceived(
+    std::unique_ptr<std::string> response_body) {
+  const int net_error = url_loader_->NetError();
+  url_loader_.reset();
+
+  if (net_error != net::OK || !response_body) {
+    OnResponseReady(absl::nullopt);
+    return;
+  }
+
+  data_decoder::DataDecoder::ParseJsonIsolated(
+      *response_body,
+      base::BindOnce([](data_decoder::DataDecoder::ValueOrError result) {
+        return std::move(result.value);
+      })
+          .Then(base::BindOnce(&GooglePhotosFetcher::OnResponseReady,
+                               weak_factory_.GetWeakPtr())));
+}
+
+template <typename... Args>
+void GooglePhotosFetcher<Args...>::OnResponseReady(
+    absl::optional<base::Value> response) {
+  std::tuple<Args...> args = ParseResponse(std::move(response));
+  std::apply(
+      [&](Args... args) {
+        for (auto& callback : pending_client_callbacks_)
+          std::move(callback).Run(std::forward<Args>(args)...);
+        pending_client_callbacks_.clear();
+      },
+      args);
+}
+
+GooglePhotosCountFetcher::GooglePhotosCountFetcher(Profile* profile)
+    : GooglePhotosFetcher(profile,
+                          kGooglePhotosCountUrl,
+                          kGooglePhotosCountTrafficAnnotation) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 }
 
 GooglePhotosCountFetcher::~GooglePhotosCountFetcher() = default;
 
-void GooglePhotosCountFetcher::Start(OnGooglePhotosCountFetched callback) {
-  DCHECK(callback_.is_null());
-  callback_ = std::move(callback);
+std::tuple<int> GooglePhotosCountFetcher::ParseResponse(
+    absl::optional<base::Value> response) {
+  if (!response.has_value())
+    return std::make_tuple(-1);
 
-  OnResponseFetched(std::string());
-}
+  const base::Value* user = response->FindDictPath("user");
+  if (!user)
+    return std::make_tuple(-1);
 
-void GooglePhotosCountFetcher::OnResponseFetched(const std::string& response) {
-  std::move(callback_).Run(/*count=*/0);
+  const std::string* count_string = user->FindStringKey("numPhotos");
+  int64_t count;
+  if (!count_string || !base::StringToInt64(*count_string, &count))
+    return std::make_tuple(-1);
+
+  return std::make_tuple(base::saturated_cast<int>(count));
 }
 
 }  // namespace wallpaper_handlers
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h
index 971da312..8e6d7298 100644
--- a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h
+++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h
@@ -6,15 +6,38 @@
 #define CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_WALLPAPER_HANDLERS_H_
 
 #include <memory>
+#include <tuple>
 
 #include "base/callback.h"
-#include "services/network/public/cpp/simple_url_loader.h"
+#include "base/scoped_observation.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "services/data_decoder/public/cpp/data_decoder.h"
+
+class GoogleServiceAuthError;
+class Profile;
 
 namespace backdrop {
 class Collection;
 class Image;
 }  // namespace backdrop
 
+namespace base {
+class Value;
+}  // namespace base
+
+namespace net {
+struct NetworkTrafficAnnotationTag;
+}  // namespace net
+
+namespace network {
+class SimpleURLLoader;
+}  // namespace network
+
+namespace signin {
+class PrimaryAccountAccessTokenFetcher;
+struct AccessTokenInfo;
+}  // namespace signin
+
 namespace wallpaper_handlers {
 
 class BackdropFetcher;
@@ -121,27 +144,81 @@
   OnSurpriseMeImageFetched callback_;
 };
 
-// Downloads the number of photos a user has stored in Google Photos.
-class GooglePhotosCountFetcher {
+// Base class for common logic among fetchers that query the Google Photos API.
+template <typename... Args>
+class GooglePhotosFetcher : public signin::IdentityManager::Observer {
  public:
-  using OnGooglePhotosCountFetched = base::OnceCallback<void(int64_t count)>;
+  GooglePhotosFetcher(
+      Profile* profile,
+      const char* service_url,
+      const net::NetworkTrafficAnnotationTag& traffic_annotation);
 
-  GooglePhotosCountFetcher();
+  GooglePhotosFetcher(const GooglePhotosFetcher&) = delete;
+  GooglePhotosFetcher& operator=(const GooglePhotosFetcher&) = delete;
+
+  ~GooglePhotosFetcher() override;
+
+  // Issues an API request if and only if one is not in progress.
+  using ClientCallback = base::OnceCallback<void(Args...)>;
+  virtual void AddCallbackAndStartIfNecessary(ClientCallback callback);
+
+ protected:
+  // Called when the API request finishes. `response` will be absent if there
+  // was an error in sending the request, receiving the response, or parsing the
+  // response; otherwise, it will hold a response in the API's specified
+  // structure.
+  virtual std::tuple<Args...> ParseResponse(
+      absl::optional<base::Value> response) = 0;
+
+ private:
+  void OnTokenReceived(GoogleServiceAuthError error,
+                       signin::AccessTokenInfo token_info);
+  void OnJsonReceived(std::unique_ptr<std::string> response_body);
+  void OnResponseReady(absl::optional<base::Value> response);
+
+  // Profile associated with the Google Photos account that will be queried.
+  Profile* const profile_;
+
+  // Supplies `token_fetcher_` with `profile_`'s GAIA account information.
+  signin::IdentityManager* const identity_manager_;
+  base::ScopedObservation<signin::IdentityManager,
+                          signin::IdentityManager::Observer>
+      identity_manager_observation_{this};
+
+  // API endpoint for the request this fetcher makes. Expected to outlive this
+  // class and therefore not need cleanup.
+  const char* const service_url_;
+
+  // States metadata about the network request that this fetcher sends.
+  const net::NetworkTrafficAnnotationTag traffic_annotation_;
+
+  // Called when the download finishes, successfully or in error.
+  std::vector<ClientCallback> pending_client_callbacks_;
+
+  // Used for fetching OAuth2 access tokens. Only non-null when a token
+  // is being fetched.
+  std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher> token_fetcher_;
+
+  // Used to download the client's desired information from the Google Photos
+  // service. Only non-null when a download is in progress.
+  std::unique_ptr<network::SimpleURLLoader> url_loader_;
+
+  base::WeakPtrFactory<GooglePhotosFetcher> weak_factory_{this};
+};
+
+// Downloads the number of photos in a user's Google Photos library.
+class GooglePhotosCountFetcher : public GooglePhotosFetcher<int> {
+ public:
+  explicit GooglePhotosCountFetcher(Profile* profile);
 
   GooglePhotosCountFetcher(const GooglePhotosCountFetcher&) = delete;
   GooglePhotosCountFetcher& operator=(const GooglePhotosCountFetcher&) = delete;
 
-  ~GooglePhotosCountFetcher();
-
-  // Starts the fetcher.
-  void Start(OnGooglePhotosCountFetched callback);
+  ~GooglePhotosCountFetcher() override;
 
  private:
-  // Called when the photo count download completes.
-  void OnResponseFetched(const std::string& response);
-
-  // Runs upon photo count download completion.
-  OnGooglePhotosCountFetched callback_;
+  // GooglePhotosFetcher:
+  std::tuple<int> ParseResponse(absl::optional<base::Value> response) override;
 };
 
 }  // namespace wallpaper_handlers
diff --git a/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_ui_delegate.cc b/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_ui_delegate.cc
index 821757f..e3a3f0fa 100644
--- a/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_ui_delegate.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_ui_delegate.cc
@@ -185,20 +185,13 @@
     return;
   }
 
-  pending_google_photos_count_callbacks_.push_back(std::move(callback));
-  if (google_photos_count_fetcher_) {
-    // Photo count fetching already started. No need to start again.
-    return;
+  if (!google_photos_count_fetcher_) {
+    google_photos_count_fetcher_ =
+        std::make_unique<wallpaper_handlers::GooglePhotosCountFetcher>(
+            profile_);
   }
-
-  google_photos_count_fetcher_ =
-      std::make_unique<wallpaper_handlers::GooglePhotosCountFetcher>();
-
-  // base::Unretained is safe to use because |this| outlives
-  // |google_photos_count_fetcher_|.
-  google_photos_count_fetcher_->Start(base::BindOnce(
-      &ChromePersonalizationAppUiDelegate::OnFetchGooglePhotosCount,
-      base::Unretained(this)));
+  google_photos_count_fetcher_->AddCallbackAndStartIfNecessary(
+      std::move(callback));
 }
 
 void ChromePersonalizationAppUiDelegate::GetLocalImages(
@@ -432,6 +425,13 @@
   WallpaperController::Get()->CancelPreviewWallpaper();
 }
 
+wallpaper_handlers::GooglePhotosCountFetcher*
+ChromePersonalizationAppUiDelegate::SetGooglePhotosCountFetcherForTest(
+    std::unique_ptr<wallpaper_handlers::GooglePhotosCountFetcher> fetcher) {
+  google_photos_count_fetcher_ = std::move(fetcher);
+  return google_photos_count_fetcher_.get();
+}
+
 void ChromePersonalizationAppUiDelegate::OnFetchCollections(
     bool success,
     const std::vector<backdrop::Collection>& collections) {
@@ -476,18 +476,6 @@
   std::move(callback).Run(std::move(result));
 }
 
-void ChromePersonalizationAppUiDelegate::OnFetchGooglePhotosCount(
-    int64_t count) {
-  DCHECK(google_photos_count_fetcher_);
-  DCHECK(!pending_google_photos_count_callbacks_.empty());
-
-  for (auto& callback : pending_google_photos_count_callbacks_) {
-    std::move(callback).Run(count);
-  }
-  pending_google_photos_count_callbacks_.clear();
-  google_photos_count_fetcher_.reset();
-}
-
 void ChromePersonalizationAppUiDelegate::OnGetLocalImages(
     GetLocalImagesCallback callback,
     const std::vector<base::FilePath>& images) {
diff --git a/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_ui_delegate.h b/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_ui_delegate.h
index 6bf53ab..862a3023e 100644
--- a/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_ui_delegate.h
+++ b/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_ui_delegate.h
@@ -40,16 +40,16 @@
 class Image;
 }  // namespace backdrop
 
+namespace content {
+class WebUI;
+}  // namespace content
+
 namespace wallpaper_handlers {
 class BackdropCollectionInfoFetcher;
 class BackdropImageInfoFetcher;
 class GooglePhotosCountFetcher;
 }  // namespace wallpaper_handlers
 
-namespace content {
-class WebUI;
-}  // namespace content
-
 class Profile;
 
 // Implemented in //chrome because this will rely on chrome
@@ -131,6 +131,10 @@
 
   void CancelPreviewWallpaper() override;
 
+  wallpaper_handlers::GooglePhotosCountFetcher*
+  SetGooglePhotosCountFetcherForTest(
+      std::unique_ptr<wallpaper_handlers::GooglePhotosCountFetcher> fetcher);
+
  private:
   friend class ChromePersonalizationAppUiDelegateTest;
 
@@ -144,8 +148,6 @@
       const std::string& collection_id,
       const std::vector<backdrop::Image>& images);
 
-  void OnFetchGooglePhotosCount(int64_t count);
-
   void OnGetLocalImages(GetLocalImagesCallback callback,
                         const std::vector<base::FilePath>& images);
 
@@ -195,10 +197,12 @@
   std::unique_ptr<wallpaper_handlers::BackdropImageInfoFetcher>
       wallpaper_attribution_info_fetcher_;
 
+  // Fetches the number of photos in the user's Google Photos library.
+  // Constructed lazily at the time of the first request and then persists for
+  // the rest of the delegate's lifetime, unless preemptively or subsequently
+  // replaced by a mock in a test.
   std::unique_ptr<wallpaper_handlers::GooglePhotosCountFetcher>
       google_photos_count_fetcher_;
-  std::vector<FetchGooglePhotosCountCallback>
-      pending_google_photos_count_callbacks_;
 
   SelectWallpaperCallback pending_select_wallpaper_callback_;
 
diff --git a/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_ui_delegate_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_ui_delegate_unittest.cc
index 56d5652..6e723e6 100644
--- a/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_ui_delegate_unittest.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_ui_delegate_unittest.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/ash/settings/device_settings_cache.h"
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
+#include "chrome/browser/ash/wallpaper_handlers/mock_wallpaper_handlers.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/ui/ash/test_wallpaper_controller.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client_impl.h"
@@ -39,6 +40,7 @@
 #include "content/public/test/test_web_ui.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -155,6 +157,8 @@
     return &test_wallpaper_controller_;
   }
 
+  TestingProfile* profile() { return profile_; }
+
   mojo::Remote<ash::personalization_app::mojom::WallpaperProvider>*
   wallpaper_provider_remote() {
     return &wallpaper_provider_remote_;
@@ -345,12 +349,27 @@
                          /*google_photos_enabled=*/::testing::Bool());
 
 TEST_P(ChromePersonalizationAppUiDelegateGooglePhotosTest, FetchCount) {
+  // Mock a fetcher for the photos count query.
+  auto* const google_photos_count_fetcher = static_cast<
+      wallpaper_handlers::MockGooglePhotosCountFetcher*>(
+      delegate()->SetGooglePhotosCountFetcherForTest(
+          std::make_unique<wallpaper_handlers::MockGooglePhotosCountFetcher>(
+              profile())));
+
+  // Simulate the client making multiple requests for the same information to
+  // test that all callbacks for that query are called.
+  const size_t num_fetches = 2;
+  EXPECT_CALL(*google_photos_count_fetcher, AddCallbackAndStartIfNecessary)
+      .Times(GooglePhotosEnabled() ? num_fetches : 0);
+
   base::RunLoop loop;
-  wallpaper_provider_remote()->get()->FetchGooglePhotosCount(
-      base::BindLambdaForTesting([&, this](int count) {
-        EXPECT_EQ(count, GooglePhotosEnabled() ? 0 : -1);
-        loop.QuitClosure().Run();
-      }));
+  for (size_t i = 0; i < num_fetches; ++i) {
+    wallpaper_provider_remote()->get()->FetchGooglePhotosCount(
+        base::BindLambdaForTesting([&](int count) {
+          EXPECT_EQ(count, GooglePhotosEnabled() ? 0 : -1);
+          loop.QuitClosure().Run();
+        }));
+  }
   wallpaper_provider_remote()->FlushForTesting();
   loop.Run();
 }
diff --git a/chrome/browser/background/background_contents_service.cc b/chrome/browser/background/background_contents_service.cc
index 68e7cb0..3dc73f0 100644
--- a/chrome/browser/background/background_contents_service.cc
+++ b/chrome/browser/background/background_contents_service.cc
@@ -472,8 +472,8 @@
 void BackgroundContentsService::LoadBackgroundContentsFromPrefs() {
   if (!prefs_)
     return;
-  const base::DictionaryValue* contents =
-      prefs_->GetDictionary(prefs::kRegisteredBackgroundContents);
+  const base::DictionaryValue* contents = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(prefs::kRegisteredBackgroundContents));
   if (!contents)
     return;
   extensions::ExtensionRegistry* extension_registry =
@@ -537,8 +537,8 @@
   // Now look in the prefs.
   if (!prefs_)
     return;
-  const base::DictionaryValue* contents =
-      prefs_->GetDictionary(prefs::kRegisteredBackgroundContents);
+  const base::DictionaryValue* contents = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(prefs::kRegisteredBackgroundContents));
   if (!contents)
     return;
   LoadBackgroundContentsFromDictionary(extension_id, contents);
@@ -653,9 +653,9 @@
     const std::string& app_id) {
   if (!prefs_)
     return false;
-  const base::DictionaryValue* contents =
+  const base::Value* contents =
       prefs_->GetDictionary(prefs::kRegisteredBackgroundContents);
-  return contents->HasKey(app_id);
+  return contents->FindKey(app_id);
 }
 
 void BackgroundContentsService::UnregisterBackgroundContents(
diff --git a/chrome/browser/background/background_contents_service_unittest.cc b/chrome/browser/background/background_contents_service_unittest.cc
index 69391b0..5a32633 100644
--- a/chrome/browser/background/background_contents_service_unittest.cc
+++ b/chrome/browser/background/background_contents_service_unittest.cc
@@ -88,8 +88,8 @@
   }
 
   const base::DictionaryValue* GetPrefs(Profile* profile) {
-    return profile->GetPrefs()->GetDictionary(
-        prefs::kRegisteredBackgroundContents);
+    return &base::Value::AsDictionaryValue(*profile->GetPrefs()->GetDictionary(
+        prefs::kRegisteredBackgroundContents));
   }
 
   // Returns the stored pref URL for the passed app id.
diff --git a/chrome/browser/browser_features.cc b/chrome/browser/browser_features.cc
index 17ced6e..f0b5b20 100644
--- a/chrome/browser/browser_features.cc
+++ b/chrome/browser/browser_features.cc
@@ -80,6 +80,14 @@
     "MuteNotificationSnoozeAction", base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
+#if defined(OS_WIN)
+// Results in remembering fonts used at the time of fcp, and prewarming those
+// fonts on subsequent loading of search results pages for the default search
+// engine.
+const base::Feature kPrewarmSearchResultsPageFonts{
+    "PrewarmSearchResultsPageFonts", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 // Shows a confirmation dialog when updates to PWAs identity (name and icon)
 // have been detected.
 const base::Feature kPwaUpdateDialogForNameAndIcon{
diff --git a/chrome/browser/browser_features.h b/chrome/browser/browser_features.h
index 61ffd30..f13d030 100644
--- a/chrome/browser/browser_features.h
+++ b/chrome/browser/browser_features.h
@@ -44,6 +44,10 @@
 extern const base::Feature kMuteNotificationSnoozeAction;
 #endif
 
+#if defined(OS_WIN)
+extern const base::Feature kPrewarmSearchResultsPageFonts;
+#endif
+
 extern const base::Feature kPwaUpdateDialogForNameAndIcon;
 
 extern const base::Feature kSandboxExternalProtocolBlocked;
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index ab3d5ff..e73edc3 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -99,7 +99,7 @@
         <include name="IDR_GAIA_AUTH_WEBVIEW_SAML_INJECTED_JS" file="resources\gaia_auth_host\webview_saml_injected.js" flattenhtml="true" type="BINDATA" />
       </if>
 
-      <if expr="chromeos or lacros">
+      <if expr="chromeos_ash or chromeos_lacros">
         <include name="IDR_CHROME_URLS_DISABLED_PAGE_HTML" file="resources\chromeos\chrome_urls_disabled_page\app.html" type="BINDATA" />
       </if>
 
diff --git a/chrome/browser/browser_switcher/browser_switcher_prefs.cc b/chrome/browser/browser_switcher/browser_switcher_prefs.cc
index b796d4b..e11133f3 100644
--- a/chrome/browser/browser_switcher/browser_switcher_prefs.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_prefs.cc
@@ -297,7 +297,7 @@
   alt_browser_params_.clear();
   if (!prefs_->IsManagedPreference(prefs::kAlternativeBrowserParameters))
     return;
-  const base::ListValue* params =
+  const base::Value* params =
       prefs_->GetList(prefs::kAlternativeBrowserParameters);
   for (const auto& param : params->GetList()) {
     std::string param_string = param.GetString();
@@ -389,7 +389,7 @@
   chrome_params_.clear();
   if (!prefs_->IsManagedPreference(prefs::kChromeParameters))
     return;
-  const base::ListValue* params = prefs_->GetList(prefs::kChromeParameters);
+  const base::Value* params = prefs_->GetList(prefs::kChromeParameters);
   for (const auto& param : params->GetList()) {
     std::string param_string = param.GetString();
     chrome_params_.push_back(param_string);
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 9c6b114..042a9c1 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -615,9 +615,8 @@
   }
   if (!command_line->HasSwitch(
           embedder_support::kOriginTrialDisabledFeatures)) {
-    const base::ListValue* override_disabled_feature_list =
-        local_state->GetList(
-            embedder_support::prefs::kOriginTrialDisabledFeatures);
+    const base::Value* override_disabled_feature_list = local_state->GetList(
+        embedder_support::prefs::kOriginTrialDisabledFeatures);
     if (override_disabled_feature_list) {
       std::vector<base::StringPiece> disabled_features;
       for (const auto& item : override_disabled_feature_list->GetList()) {
@@ -635,7 +634,7 @@
     }
   }
   if (!command_line->HasSwitch(embedder_support::kOriginTrialDisabledTokens)) {
-    const base::ListValue* disabled_token_list = local_state->GetList(
+    const base::Value* disabled_token_list = local_state->GetList(
         embedder_support::prefs::kOriginTrialDisabledTokens);
     if (disabled_token_list) {
       std::vector<base::StringPiece> disabled_tokens;
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 6125095..3f93962 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -809,7 +809,7 @@
     return false;
 
   // Check if the current URL matches a URL pattern on the allowlist.
-  const base::ListValue* autoplay_allowlist =
+  const base::Value* autoplay_allowlist =
       prefs->GetList(prefs::kAutoplayWhitelist);
   return autoplay_allowlist &&
          prefs->IsManagedPreference(prefs::kAutoplayWhitelist) &&
@@ -3898,6 +3898,7 @@
     case sandbox::mojom::Sandbox::kSpeechRecognition:
     case sandbox::mojom::Sandbox::kPdfConversion:
     case sandbox::mojom::Sandbox::kService:
+    case sandbox::mojom::Sandbox::kServiceWithJit:
     case sandbox::mojom::Sandbox::kIconReader:
     case sandbox::mojom::Sandbox::kMediaFoundationCdm:
     case sandbox::mojom::Sandbox::kWindowsSystemProxyResolver:
@@ -3950,6 +3951,9 @@
       enforce_code_integrity =
           base::FeatureList::IsEnabled(kNetworkServiceCodeIntegrity);
       break;
+    case sandbox::mojom::Sandbox::kServiceWithJit:
+      enforce_code_integrity = true;
+      break;
     case sandbox::mojom::Sandbox::kUtility:
     case sandbox::mojom::Sandbox::kGpu:
     case sandbox::mojom::Sandbox::kPpapi:
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 4bcceea..1a25770 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1645,6 +1645,8 @@
     "../ash/input_method/candidate_window_controller_impl.h",
     "../ash/input_method/component_extension_ime_manager_delegate_impl.cc",
     "../ash/input_method/component_extension_ime_manager_delegate_impl.h",
+    "../ash/input_method/diacritics_checker.cc",
+    "../ash/input_method/diacritics_checker.h",
     "../ash/input_method/diacritics_insensitive_string_comparator.cc",
     "../ash/input_method/diacritics_insensitive_string_comparator.h",
     "../ash/input_method/emoji_suggester.cc",
@@ -2668,8 +2670,12 @@
     "../ash/policy/reporting/metrics_reporting/network/https_latency_sampler.h",
     "../ash/policy/reporting/metrics_reporting/network/network_events_observer.cc",
     "../ash/policy/reporting/metrics_reporting/network/network_events_observer.h",
+    "../ash/policy/reporting/metrics_reporting/network/network_info_sampler.cc",
+    "../ash/policy/reporting/metrics_reporting/network/network_info_sampler.h",
     "../ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler.cc",
     "../ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler.h",
+    "../ash/policy/reporting/metrics_reporting/usb/usb_events_observer.cc",
+    "../ash/policy/reporting/metrics_reporting/usb/usb_events_observer.h",
     "../ash/policy/reporting/single_arc_app_install_event_log.cc",
     "../ash/policy/reporting/single_arc_app_install_event_log.h",
     "../ash/policy/reporting/single_extension_install_event_log.cc",
@@ -3807,6 +3813,8 @@
     "../ash/secure_channel/fake_nearby_endpoint_finder.h",
     "../ash/settings/scoped_testing_cros_settings.cc",
     "../ash/settings/scoped_testing_cros_settings.h",
+    "../ash/wallpaper_handlers/mock_wallpaper_handlers.cc",
+    "../ash/wallpaper_handlers/mock_wallpaper_handlers.h",
     "extensions/test_external_cache.cc",
     "extensions/test_external_cache.h",
     "printing/printing_stubs.cc",
@@ -3828,6 +3836,7 @@
     "//ash/components/settings",
     "//ash/constants",
     "//ash/services/ime:test_support",
+    "//ash/webui/personalization_app/mojom",
     "//chrome/browser:browser_process",
     "//chrome/browser/web_applications",
     "//chrome/common:constants",
@@ -4182,6 +4191,7 @@
     "../ash/input_method/assistive_suggester_unittest.cc",
     "../ash/input_method/assistive_window_controller_unittest.cc",
     "../ash/input_method/autocorrect_manager_unittest.cc",
+    "../ash/input_method/diacritics_checker_unittest.cc",
     "../ash/input_method/diacritics_insensitive_string_comparator_unittest.cc",
     "../ash/input_method/emoji_suggester_unittest.cc",
     "../ash/input_method/fake_suggestion_handler.cc",
@@ -4422,7 +4432,9 @@
     "../ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc",
     "../ash/policy/reporting/metrics_reporting/network/https_latency_sampler_unittest.cc",
     "../ash/policy/reporting/metrics_reporting/network/network_events_observer_unittest.cc",
+    "../ash/policy/reporting/metrics_reporting/network/network_info_sampler_unittest.cc",
     "../ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler_unittest.cc",
+    "../ash/policy/reporting/metrics_reporting/usb/usb_events_observer_unittest.cc",
     "../ash/policy/reporting/single_arc_app_install_event_log_unittest.cc",
     "../ash/policy/reporting/user_added_removed/user_added_removed_reporter_unittest.cc",
     "../ash/policy/reporting/user_event_reporter_helper_unittest.cc",
diff --git a/chrome/browser/chromeos/extensions/echo_private_api.cc b/chrome/browser/chromeos/extensions/echo_private_api.cc
index ea560db..6abb8074 100644
--- a/chrome/browser/chromeos/extensions/echo_private_api.cc
+++ b/chrome/browser/chromeos/extensions/echo_private_api.cc
@@ -117,8 +117,8 @@
 
   const std::string& service_id = params->id;
   PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* offer_infos = local_state->
-      GetDictionary(prefs::kEchoCheckedOffers);
+  const base::DictionaryValue* offer_infos = &base::Value::AsDictionaryValue(
+      *local_state->GetDictionary(prefs::kEchoCheckedOffers));
 
   const base::DictionaryValue* offer_info = NULL;
   if (!offer_infos->GetDictionaryWithoutPathExpansion(
diff --git a/chrome/browser/chromeos/extensions/input_method_api.cc b/chrome/browser/chromeos/extensions/input_method_api.cc
index e08e13e..89ba5ce3 100644
--- a/chrome/browser/chromeos/extensions/input_method_api.cc
+++ b/chrome/browser/chromeos/extensions/input_method_api.cc
@@ -378,7 +378,7 @@
   const auto params = GetSettings::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
-  const base::DictionaryValue* input_methods =
+  const base::Value* input_methods =
       Profile::FromBrowserContext(browser_context())
           ->GetPrefs()
           ->GetDictionary(prefs::kLanguageInputMethodSpecificSettings);
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler.cc
index fe71471..0662d534 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler.cc
@@ -106,7 +106,7 @@
 std::unordered_set<std::string>
 ExtensionCleanupHandler::GetCleanupExemptExtensions() {
   std::unordered_set<std::string> exempt_extensions;
-  const base::ListValue* exempt_list = profile_->GetPrefs()->GetList(
+  const base::Value* exempt_list = profile_->GetPrefs()->GetList(
       prefs::kRestrictedManagedGuestSessionExtensionCleanupExemptList);
 
   for (const base::Value& value : exempt_list->GetList()) {
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc b/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc
index d8030e1..26214cf 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc
@@ -109,10 +109,10 @@
   for (const user_manager::User* user : user_manager->GetUsers()) {
     if (!user || user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT)
       continue;
-    chromeos::UserContext context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
-                                  user->GetAccountId());
+    ash::UserContext context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
+                             user->GetAccountId());
     if (parameters->password) {
-      context.SetKey(chromeos::Key(*parameters->password));
+      context.SetKey(ash::Key(*parameters->password));
       context.SetManagedGuestSessionLaunchExtensionId(extension_id());
     }
 
@@ -216,9 +216,9 @@
     return RespondNow(Error(login_api_errors::kAnotherUnlockAttemptInProgress));
   }
 
-  chromeos::UserContext context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
-                                active_user->GetAccountId());
-  context.SetKey(chromeos::Key(parameters->password));
+  ash::UserContext context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
+                           active_user->GetAccountId());
+  context.SetKey(ash::Key(parameters->password));
   handler->Authenticate(
       context,
       base::BindOnce(
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/login_api_lock_handler.cc b/chrome/browser/chromeos/extensions/login_screen/login/login_api_lock_handler.cc
index ed3a1328..bba82bc 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/login_api_lock_handler.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/login_api_lock_handler.cc
@@ -42,7 +42,7 @@
 }
 
 void LoginApiLockHandler::Authenticate(
-    const UserContext& user_context,
+    const ash::UserContext& user_context,
     base::OnceCallback<void(bool auth_success)> callback) {
   unlock_in_progress_ = true;
   callback_ = std::move(callback);
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/login_api_lock_handler.h b/chrome/browser/chromeos/extensions/login_screen/login/login_api_lock_handler.h
index 8bf700f..d063723 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/login_api_lock_handler.h
+++ b/chrome/browser/chromeos/extensions/login_screen/login/login_api_lock_handler.h
@@ -8,9 +8,11 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 
-namespace chromeos {
-
+namespace ash {
 class UserContext;
+}  // namespace ash
+
+namespace chromeos {
 
 // A thin wrapper around |SessionControllerClientImpl| and
 // |ScreenLocker| to allow easier mocking for tests. Also manages the
@@ -33,7 +35,7 @@
 
   virtual void RequestLockScreen();
 
-  virtual void Authenticate(const UserContext& user_context,
+  virtual void Authenticate(const ash::UserContext& user_context,
                             base::OnceCallback<void(bool auth_success)>);
 
   virtual bool IsUnlockInProgress() const;
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/login_api_unittest.cc b/chrome/browser/chromeos/extensions/login_screen/login/login_api_unittest.cc
index 964f216..e7b81f2 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/login_api_unittest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/login_api_unittest.cc
@@ -71,7 +71,7 @@
   ~MockExistingUserController() override = default;
 
   MOCK_METHOD2(Login,
-               void(const chromeos::UserContext&, const ash::SigninSpecifics&));
+               void(const ash::UserContext&, const ash::SigninSpecifics&));
   MOCK_CONST_METHOD0(IsSigninInProgress, bool());
 };
 
@@ -91,7 +91,7 @@
 
   MOCK_METHOD0(RequestLockScreen, void());
   MOCK_METHOD2(Authenticate,
-               void(const chromeos::UserContext& user_context,
+               void(const ash::UserContext& user_context,
                     base::OnceCallback<void(bool auth_success)> callback));
   MOCK_CONST_METHOD0(IsUnlockInProgress, bool());
 };
@@ -118,9 +118,9 @@
   TestingProfileManager* const profile_manager_;
 };
 
-chromeos::UserContext GetPublicUserContext(const std::string& email) {
-  return chromeos::UserContext(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
-                               AccountId::FromUserEmail(email));
+ash::UserContext GetPublicUserContext(const std::string& email) {
+  return ash::UserContext(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
+                          AccountId::FromUserEmail(email));
 }
 
 void SetLoginExtensionApiLaunchExtensionIdPref(
@@ -239,8 +239,8 @@
 // `ExistingUserController`.
 TEST_F(LoginApiUnittest, LaunchManagedGuestSessionWithPassword) {
   std::unique_ptr<ScopedTestingProfile> profile = AddPublicAccountUser(kEmail);
-  chromeos::UserContext user_context = GetPublicUserContext(kEmail);
-  user_context.SetKey(chromeos::Key("password"));
+  ash::UserContext user_context = GetPublicUserContext(kEmail);
+  user_context.SetKey(ash::Key("password"));
   EXPECT_CALL(*mock_existing_user_controller_,
               Login(user_context, MatchSigninSpecifics(ash::SigninSpecifics())))
       .Times(1);
@@ -412,7 +412,7 @@
       .WillOnce(Return(false));
   EXPECT_CALL(*mock_lock_handler_,
               Authenticate(MatchUserContextSecret("password"), _))
-      .WillOnce([](chromeos::UserContext user_context,
+      .WillOnce([](ash::UserContext user_context,
                    base::OnceCallback<void(bool auth_success)> callback) {
         std::move(callback).Run(/*auth_success=*/true);
       });
@@ -502,7 +502,7 @@
       .WillOnce(Return(false));
   EXPECT_CALL(*mock_lock_handler_,
               Authenticate(MatchUserContextSecret("password"), _))
-      .WillOnce([](chromeos::UserContext user_context,
+      .WillOnce([](ash::UserContext user_context,
                    base::OnceCallback<void(bool auth_success)> callback) {
         std::move(callback).Run(/*auth_success=*/false);
       });
@@ -609,7 +609,7 @@
     EXPECT_CALL(*mock_lock_handler_,
                 Authenticate(MatchUserContextSecret(session_secret), _))
         .WillOnce([auth_success](
-                      chromeos::UserContext user_context,
+                      ash::UserContext user_context,
                       base::OnceCallback<void(bool auth_success)> callback) {
           std::move(callback).Run(/*auth_success=*/auth_success);
         });
@@ -626,7 +626,7 @@
   ui::UserActivityDetector::Get()->set_now_for_test(now_);
   SetExtensionWithId(kExtensionId);
   std::unique_ptr<ScopedTestingProfile> profile = AddPublicAccountUser(kEmail);
-  chromeos::UserContext user_context;
+  ash::UserContext user_context;
   EXPECT_CALL(*mock_existing_user_controller_,
               Login(_, MatchSigninSpecifics(chromeos::SigninSpecifics())))
       .WillOnce(SaveArg<0>(&user_context));
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.cc b/chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.cc
index 5fa4a40..c47f36e 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.cc
@@ -96,9 +96,9 @@
 
   session_secret_ = GenerateRandomString(kSessionSecretLength);
 
-  chromeos::UserContext context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
-                                user->GetAccountId());
-  context.SetKey(chromeos::Key(session_secret_));
+  ash::UserContext context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
+                           user->GetAccountId());
+  context.SetKey(ash::Key(session_secret_));
   context.SetManagedGuestSessionLaunchExtensionId(extension_id);
   existing_user_controller->Login(context, chromeos::SigninSpecifics());
 
@@ -287,9 +287,9 @@
   const user_manager::User* active_user =
       user_manager::UserManager::Get()->GetActiveUser();
 
-  UserContext user_context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
-                           active_user->GetAccountId());
-  user_context.SetKey(chromeos::Key(session_secret_));
+  ash::UserContext user_context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
+                                active_user->GetAccountId());
+  user_context.SetKey(ash::Key(session_secret_));
   LoginApiLockHandler::Get()->Authenticate(user_context, std::move(callback));
 }
 
diff --git a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller.cc b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller.cc
index bd7395d8..5edfdc8 100644
--- a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller.cc
+++ b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller.cc
@@ -197,13 +197,11 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     reporting_manager->ReportWarningProceededEvent(
         src_pattern, GetComponent(data_dst->type()),
-        DlpRulesManager::Restriction::kClipboard,
-        DlpRulesManager::Level::kNotSet);
+        DlpRulesManager::Restriction::kClipboard);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   } else {
     reporting_manager->ReportWarningProceededEvent(
-        src_pattern, dst_pattern, DlpRulesManager::Restriction::kClipboard,
-        DlpRulesManager::Level::kNotSet);
+        src_pattern, dst_pattern, DlpRulesManager::Restriction::kClipboard);
   }
 }
 
diff --git a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc
index d86ac97..63223a6 100644
--- a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc
+++ b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc
@@ -672,8 +672,7 @@
   EXPECT_EQ(events.size(), 2u);
   EXPECT_THAT(events[1],
               IsDlpPolicyEvent(CreateDlpPolicyWarningProceededEvent(
-                  kMailUrl, "*", DlpRulesManager::Restriction::kClipboard,
-                  DlpRulesManager::Level::kNotSet)));
+                  kMailUrl, "*", DlpRulesManager::Restriction::kClipboard)));
 
   testing::Mock::VerifyAndClearExpectations(&dlp_controller_);
 }
@@ -845,20 +844,15 @@
   EXPECT_EQ(events.size(), 1u);
   EXPECT_THAT(events[0],
               IsDlpPolicyEvent(CreateDlpPolicyWarningProceededEvent(
-                  kMailUrl, "*", DlpRulesManager::Restriction::kClipboard,
-                  DlpRulesManager::Level::kNotSet)));
+                  kMailUrl, "*", DlpRulesManager::Restriction::kClipboard)));
 
   testing::Mock::VerifyAndClearExpectations(&dlp_controller_);
 }
 
 // Test case for crbug.com/1213143
 // Flaky on MSan bots: crbug.com/1230617
-#if defined(MEMORY_SANITIZER)
-#define MAYBE_Reporting DISABLED_Reporting
-#else
-#define MAYBE_Reporting Reporting
-#endif
-IN_PROC_BROWSER_TEST_F(DataTransferDlpBlinkBrowserTest, MAYBE_Reporting) {
+// Also flaky on linux-chromeos-dbg: crbug.com/1281659
+IN_PROC_BROWSER_TEST_F(DataTransferDlpBlinkBrowserTest, DISABLED_Reporting) {
   base::HistogramTester histogram_tester;
 
   ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc
index abcbe9c..587c2fe 100644
--- a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc
+++ b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc
@@ -250,9 +250,9 @@
   dlp_controller_.PasteIfAllowed(&data_src, &data_dst, absl::nullopt,
                                  web_contents->GetMainFrame(), callback.Get());
   EXPECT_EQ(events_.size(), 1u);
-  EXPECT_THAT(events_[0], IsDlpPolicyEvent(CreateDlpPolicyWarningProceededEvent(
-                              "", "", DlpRulesManager::Restriction::kClipboard,
-                              DlpRulesManager::Level::kWarn)));
+  EXPECT_THAT(events_[0],
+              IsDlpPolicyEvent(CreateDlpPolicyWarningProceededEvent(
+                  "", "", DlpRulesManager::Restriction::kClipboard)));
 }
 
 TEST_F(DataTransferDlpControllerTest, PasteIfAllowed_CancelDst) {
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
index f395aa3..e9961c7 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
@@ -46,34 +46,6 @@
   reporting_manager->ReportEvent(src_url, restriction, level);
 }
 
-// Reports events of type DlpPolicyEvent::Mode::WARN_PROCEED to
-// `reporting_manager`.
-void ReportWarningProceededEvent(const GURL& url,
-                                 DlpRulesManager::Restriction restriction,
-                                 DlpReportingManager* reporting_manager) {
-  if (!reporting_manager)
-    return;
-
-  DlpRulesManager* rules_manager =
-      DlpRulesManagerFactory::GetForPrimaryProfile();
-  if (rules_manager) {
-    const std::string src_url = rules_manager->GetSourceUrlPattern(
-        url, restriction, DlpRulesManager::Level::kWarn);
-    reporting_manager->ReportWarningProceededEvent(src_url, restriction);
-  }
-}
-
-// Helper method to create a callback with ReportWarningProceededEvent function.
-bool MaybeReportWarningProceededEvent(GURL url,
-                                      DlpRulesManager::Restriction restriction,
-                                      DlpReportingManager* reporting_manager,
-                                      bool should_proceed) {
-  if (should_proceed) {
-    ReportWarningProceededEvent(url, restriction, reporting_manager);
-  }
-  return should_proceed;
-}
-
 // Helper method to check whether the restriction level is kBlock.
 bool IsBlocked(RestrictionLevelAndUrl restriction_info) {
   return restriction_info.level == DlpRulesManager::Level::kBlock;
@@ -130,7 +102,7 @@
     }
 
     // Immediately report a warning event.
-    ReportWarningEvent(restriction_info,
+    ReportWarningEvent(restriction_info.url,
                        DlpRulesManager::Restriction::kPrinting);
 
     // Report a warning proceeded event only after a user proceeds with printing
@@ -153,6 +125,33 @@
 DlpContentManager::DlpContentManager() = default;
 DlpContentManager::~DlpContentManager() = default;
 
+void DlpContentManager::ReportWarningProceededEvent(
+    const GURL& url,
+    DlpRulesManager::Restriction restriction,
+    DlpReportingManager* reporting_manager) {
+  if (!reporting_manager)
+    return;
+
+  DlpRulesManager* rules_manager =
+      DlpRulesManagerFactory::GetForPrimaryProfile();
+  if (rules_manager) {
+    const std::string src_url = rules_manager->GetSourceUrlPattern(
+        url, restriction, DlpRulesManager::Level::kWarn);
+    reporting_manager->ReportWarningProceededEvent(src_url, restriction);
+  }
+}
+
+bool DlpContentManager::MaybeReportWarningProceededEvent(
+    GURL url,
+    DlpRulesManager::Restriction restriction,
+    DlpReportingManager* reporting_manager,
+    bool should_proceed) {
+  if (should_proceed) {
+    ReportWarningProceededEvent(url, restriction, reporting_manager);
+  }
+  return should_proceed;
+}
+
 void DlpContentManager::Init() {
   DlpRulesManager* rules_manager =
       DlpRulesManagerFactory::GetForPrimaryProfile();
@@ -276,12 +275,11 @@
 }
 
 void DlpContentManager::ReportWarningEvent(
-    const RestrictionLevelAndUrl& restriction_info,
+    const GURL& url,
     DlpRulesManager::Restriction restriction) {
-  DCHECK(IsWarn(restriction_info));
   if (reporting_manager_) {
-    ReportEvent(restriction_info.url, restriction,
-                DlpRulesManager::Level::kWarn, reporting_manager_);
+    ReportEvent(url, restriction, DlpRulesManager::Level::kWarn,
+                reporting_manager_);
   }
 }
 
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
index 5d6f237..55797b95e 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
@@ -74,6 +74,21 @@
   DlpContentManager();
   ~DlpContentManager() override;
 
+  // Reports events of type DlpPolicyEvent::Mode::WARN_PROCEED to
+  // `reporting_manager`.
+  static void ReportWarningProceededEvent(
+      const GURL& url,
+      DlpRulesManager::Restriction restriction,
+      DlpReportingManager* reporting_manager);
+
+  // Helper method to create a callback with ReportWarningProceededEvent
+  // function.
+  static bool MaybeReportWarningProceededEvent(
+      GURL url,
+      DlpRulesManager::Restriction restriction,
+      DlpReportingManager* reporting_manager,
+      bool should_proceed);
+
   // Initializing to be called separately to make testing possible.
   virtual void Init();
 
@@ -119,10 +134,10 @@
                         DlpRulesManager::Restriction restriction);
 
   // Reports warning events if `reporting_manager` is configured.
-  void ReportWarningEvent(const RestrictionLevelAndUrl& restriction_info,
+  void ReportWarningEvent(const GURL& url,
                           DlpRulesManager::Restriction restriction);
 
-  // Removes all elements of |contents| that the user has recently already
+  // Removes all elemxents of |contents| that the user has recently already
   // acknowledged the warning for.
   void RemoveAllowedContents(DlpConfidentialContents& contents,
                              DlpRulesManager::Restriction restriction);
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.cc b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.cc
index 8871405..e95da47 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.cc
@@ -130,7 +130,8 @@
 }
 
 DlpPolicyEvent CreateDlpPolicyEvent(const std::string& src_pattern,
-                                    DlpRulesManager::Restriction restriction) {
+                                    DlpRulesManager::Restriction restriction,
+                                    DlpRulesManager::Level level) {
   DlpPolicyEvent event;
 
   DlpPolicyEventSource* event_source = new DlpPolicyEventSource;
@@ -141,15 +142,8 @@
       DlpRulesManagerRestriction2DlpEventRestriction(restriction));
   event.set_timestamp_micro(base::Time::Now().ToTimeT());
   event.set_user_type(GetCurrentUserType());
-
-  return event;
-}
-
-DlpPolicyEvent CreateDlpPolicyEvent(const std::string& src_pattern,
-                                    DlpRulesManager::Restriction restriction,
-                                    DlpRulesManager::Level level) {
-  auto event = CreateDlpPolicyEvent(src_pattern, restriction);
   event.set_mode(DlpRulesManagerLevel2DlpEventMode(level));
+
   return event;
 }
 
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.h b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.h
index bd617ae..7b0b8ec 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.h
@@ -19,8 +19,6 @@
 // helper function to create DlpPolicyEvents to be enqueued or used to test
 // against.
 DlpPolicyEvent CreateDlpPolicyEvent(const std::string& src_pattern,
-                                    DlpRulesManager::Restriction restriction);
-DlpPolicyEvent CreateDlpPolicyEvent(const std::string& src_pattern,
                                     DlpRulesManager::Restriction restriction,
                                     DlpRulesManager::Level level);
 DlpPolicyEvent CreateDlpPolicyEvent(const std::string& src_pattern,
@@ -31,14 +29,10 @@
                                     DlpRulesManager::Component dst_component,
                                     DlpRulesManager::Restriction restriction,
                                     DlpRulesManager::Level level);
-// TODO(jkopanski): Using template parameter pack enforces including
-//  "dlp_policy_event.pb.h" in the header file. Check after implementing
-//  reporting for clipboard if this template patter is needed or move all
-//  functions to a separate header.
 template <typename... Args>
 DlpPolicyEvent CreateDlpPolicyWarningProceededEvent(Args... args) {
-  // TODO(jkopanski): Add level as the last argument.
-  auto event = CreateDlpPolicyEvent(args...);
+  auto event = CreateDlpPolicyEvent(args..., DlpRulesManager::Level::kNotSet);
+  // Override DlpRulesManager::Level::kNotSet set above.
   event.set_mode(DlpPolicyEvent_Mode_WARN_PROCEED);
   return event;
 }
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
index 1dbe6d4..ec7abc6 100644
--- a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
+++ b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
@@ -215,7 +215,7 @@
   }
 
   int InPrefHandlerCount() {
-    const base::ListValue* in_pref_handlers = profile()->GetPrefs()->GetList(
+    const base::Value* in_pref_handlers = profile()->GetPrefs()->GetList(
         custom_handlers::prefs::kRegisteredProtocolHandlers);
     return static_cast<int>(in_pref_handlers->GetList().size());
   }
@@ -229,7 +229,7 @@
   }
 
   int InPrefIgnoredHandlerCount() {
-    const base::ListValue* in_pref_ignored_handlers =
+    const base::Value* in_pref_ignored_handlers =
         profile()->GetPrefs()->GetList(
             custom_handlers::prefs::kIgnoredProtocolHandlers);
     return static_cast<int>(in_pref_ignored_handlers->GetList().size());
diff --git a/chrome/browser/device_api/device_service_impl.cc b/chrome/browser/device_api/device_service_impl.cc
index d7d34c3..9eabce18 100644
--- a/chrome/browser/device_api/device_service_impl.cc
+++ b/chrome/browser/device_api/device_service_impl.cc
@@ -37,7 +37,7 @@
 // attributes.
 bool CanAccessDeviceAttributes(const PrefService* prefs,
                                const url::Origin& origin) {
-  const base::ListValue* prefs_list =
+  const base::Value* prefs_list =
       prefs->GetList(prefs::kDeviceAttributesAllowedForOrigins);
   if (!prefs_list)
     return false;
@@ -68,7 +68,7 @@
 // policy.
 bool IsForceInstalledOrigin(const PrefService* prefs,
                             const url::Origin& origin) {
-  const base::ListValue* prefs_list =
+  const base::Value* prefs_list =
       prefs->GetList(prefs::kWebAppInstallForceList);
   if (!prefs_list)
     return false;
diff --git a/chrome/browser/devtools/device/devtools_android_bridge_browsertest.cc b/chrome/browser/devtools/device/devtools_android_bridge_browsertest.cc
index ec59705f..27412174 100644
--- a/chrome/browser/devtools/device/devtools_android_bridge_browsertest.cc
+++ b/chrome/browser/devtools/device/devtools_android_bridge_browsertest.cc
@@ -102,8 +102,8 @@
   service->ClearPref(prefs::kDevToolsDiscoverTCPTargetsEnabled);
   service->ClearPref(prefs::kDevToolsTCPDiscoveryConfig);
 
-  const base::ListValue* targets =
-    service->GetList(prefs::kDevToolsTCPDiscoveryConfig);
+  const base::Value* targets =
+      service->GetList(prefs::kDevToolsTCPDiscoveryConfig);
   EXPECT_NE(nullptr, targets);
   EXPECT_EQ(2ul, targets->GetList().size());
 
diff --git a/chrome/browser/devtools/devtools_file_helper.cc b/chrome/browser/devtools/devtools_file_helper.cc
index d723f8f..1ec973e9 100644
--- a/chrome/browser/devtools/devtools_file_helper.cc
+++ b/chrome/browser/devtools/devtools_file_helper.cc
@@ -195,7 +195,8 @@
 using PathToType = std::map<std::string, std::string>;
 PathToType GetAddedFileSystemPaths(Profile* profile) {
   const base::DictionaryValue* file_systems_paths_value =
-      profile->GetPrefs()->GetDictionary(prefs::kDevToolsFileSystemPaths);
+      &base::Value::AsDictionaryValue(
+          *profile->GetPrefs()->GetDictionary(prefs::kDevToolsFileSystemPaths));
   PathToType result;
   for (base::DictionaryValue::Iterator it(*file_systems_paths_value);
        !it.IsAtEnd(); it.Advance()) {
@@ -247,8 +248,8 @@
     return;
   }
 
-  const base::DictionaryValue* file_map =
-      profile_->GetPrefs()->GetDictionary(prefs::kDevToolsEditedFiles);
+  const base::DictionaryValue* file_map = &base::Value::AsDictionaryValue(
+      *profile_->GetPrefs()->GetDictionary(prefs::kDevToolsEditedFiles));
   base::FilePath initial_path;
 
   const base::Value* path_value;
@@ -433,9 +434,9 @@
     const std::string& file_system_path) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  const base::DictionaryValue* file_systems_paths_value =
+  const base::Value* file_systems_paths_value =
       profile_->GetPrefs()->GetDictionary(prefs::kDevToolsFileSystemPaths);
-  return file_systems_paths_value->HasKey(file_system_path);
+  return file_systems_paths_value->FindKey(file_system_path);
 }
 
 void DevToolsFileHelper::OnOpenItemComplete(
diff --git a/chrome/browser/devtools/devtools_settings.cc b/chrome/browser/devtools/devtools_settings.cc
index 7cbe3c300..a725c992 100644
--- a/chrome/browser/devtools/devtools_settings.cc
+++ b/chrome/browser/devtools/devtools_settings.cc
@@ -89,7 +89,7 @@
     return base::Value(result ? "true" : "false");
   }
   const char* dict_name = GetDictionaryNameForSettingsName(name);
-  const base::DictionaryValue* dict = prefs->GetDictionary(dict_name);
+  const base::Value* dict = prefs->GetDictionary(dict_name);
   const base::Value* value = dict->FindKey(name);
   return value ? absl::optional<base::Value>(value->Clone()) : absl::nullopt;
 }
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index bc9b7dd9..909bac7 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -1653,7 +1653,8 @@
 
 void DevToolsWindow::CreateDevToolsBrowser() {
   PrefService* prefs = profile_->GetPrefs();
-  if (!prefs->GetDictionary(prefs::kAppWindowPlacement)->HasKey(kDevToolsApp)) {
+  if (!prefs->GetDictionary(prefs::kAppWindowPlacement)
+           ->FindKey(kDevToolsApp)) {
     // Ensure there is always a default size so that
     // BrowserFrame::InitBrowserFrame can retrieve it later.
     DictionaryPrefUpdate update(prefs, prefs::kAppWindowPlacement);
diff --git a/chrome/browser/devtools/devtools_window.h b/chrome/browser/devtools/devtools_window.h
index 4be7910..234299de 100644
--- a/chrome/browser/devtools/devtools_window.h
+++ b/chrome/browser/devtools/devtools_window.h
@@ -198,7 +198,7 @@
   //    to BeforeUnloadFired is proxied through HandleBeforeUnload() rather
   //    than getting called directly.
   // 3a. If |DevToolsWindow::BeforeUnloadFired| is called with |proceed|=false
-  //     it calls throught to the content's BeforeUnloadFired(), which from the
+  //     it calls through to the content's BeforeUnloadFired(), which from the
   //     WebContents perspective looks the same as the |content|'s own
   //     beforeunload dialog having had it's 'stay on this page' button clicked.
   // 3b. If |proceed| = true, then it fires beforeunload event on |contents|
@@ -228,7 +228,7 @@
 
   // Returns true if this contents beforeunload event was intercepted by
   // devtools and false otherwise. If the event was intercepted, caller should
-  // not fire beforeunlaod event on |contents| itself as devtools window will
+  // not fire beforeunload event on |contents| itself as devtools window will
   // take care of it, otherwise caller should continue handling the event as
   // usual.
   static bool InterceptPageBeforeUnload(content::WebContents* contents);
@@ -318,7 +318,7 @@
                                 const std::string& settings,
                                 const std::string& panel,
                                 bool has_other_clients,
-                                bool browser_connnection);
+                                bool browser_connection);
   static GURL GetDevToolsURL(Profile* profile,
                              FrontendType frontend_type,
                              const std::string& frontend_url,
diff --git a/chrome/browser/download/bubble/download_display.cc b/chrome/browser/download/bubble/download_display.cc
new file mode 100644
index 0000000..e3397ba
--- /dev/null
+++ b/chrome/browser/download/bubble/download_display.cc
@@ -0,0 +1,7 @@
+// 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 "chrome/browser/download/bubble/download_display.h"
+
+DownloadDisplay::~DownloadDisplay() = default;
diff --git a/chrome/browser/download/bubble/download_display.h b/chrome/browser/download/bubble/download_display.h
new file mode 100644
index 0000000..558562b
--- /dev/null
+++ b/chrome/browser/download/bubble/download_display.h
@@ -0,0 +1,29 @@
+// 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 CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_DISPLAY_H_
+#define CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_DISPLAY_H_
+
+#include "chrome/browser/download/bubble/download_icon_state.h"
+
+class DownloadDisplay {
+ public:
+  // Shows the download display.
+  virtual void Show() = 0;
+  // Hides the download display.
+  virtual void Hide() = 0;
+  // Returns whether or not the download display is visible.
+  virtual bool IsShowing() = 0;
+  // Enables potential actions resulting from clicking the download display.
+  virtual void Enable() = 0;
+  // Disables potential actions resulting from clicking the download display.
+  virtual void Disable() = 0;
+  // Updates the download icon according to |state|.
+  virtual void UpdateDownloadIcon(download::DownloadIconState state) = 0;
+
+ protected:
+  virtual ~DownloadDisplay();
+};
+
+#endif  // CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_DISPLAY_H_
diff --git a/chrome/browser/download/bubble/download_display_controller.cc b/chrome/browser/download/bubble/download_display_controller.cc
new file mode 100644
index 0000000..881f50ef
--- /dev/null
+++ b/chrome/browser/download/bubble/download_display_controller.cc
@@ -0,0 +1,104 @@
+// 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 "chrome/browser/download/bubble/download_display_controller.h"
+
+#include "base/numerics/safe_conversions.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/download/bubble/download_display.h"
+#include "chrome/browser/download/bubble/download_icon_state.h"
+#include "chrome/browser/download/download_item_model.h"
+
+namespace {
+constexpr int kToolbarIconVisbilityHours = 24;
+}
+
+DownloadDisplayController::DownloadDisplayController(
+    DownloadDisplay* display,
+    content::DownloadManager* download_manager)
+    : display_(display),
+      download_manager_(download_manager),
+      download_notifier_(download_manager, this) {}
+
+DownloadDisplayController::~DownloadDisplayController() = default;
+
+void DownloadDisplayController::OnDownloadCreated(
+    content::DownloadManager* manager,
+    download::DownloadItem* item) {
+  UpdateToolbarButtonState();
+}
+
+void DownloadDisplayController::OnDownloadUpdated(
+    content::DownloadManager* manager,
+    download::DownloadItem* item) {
+  DownloadItemModel item_model(item);
+
+  if (item_model.IsDone()) {
+    ScheduleToolbarDisappearance(base::Hours(kToolbarIconVisbilityHours));
+  }
+  UpdateToolbarButtonState();
+}
+
+void DownloadDisplayController::ShowToolbarButton() {
+  if (!display_->IsShowing()) {
+    display_->Enable();
+    display_->Show();
+  }
+}
+
+void DownloadDisplayController::HideToolbarButton() {
+  if (display_->IsShowing()) {
+    display_->Hide();
+  }
+}
+
+void DownloadDisplayController::UpdateToolbarButtonState() {
+  download::DownloadIconState icon_state;
+
+  if (download_manager_->InProgressCount() > 0) {
+    ShowToolbarButton();
+    icon_state = download::DownloadIconState::kProgress;
+  } else {
+    icon_state = download::DownloadIconState::kComplete;
+  }
+
+  display_->UpdateDownloadIcon(icon_state);
+}
+
+void DownloadDisplayController::ScheduleToolbarDisappearance(
+    base::TimeDelta interval) {
+  icon_disappearance_timer_.Stop();
+  icon_disappearance_timer_.Start(
+      FROM_HERE, interval, this, &DownloadDisplayController::HideToolbarButton);
+}
+
+DownloadDisplayController::ProgressInfo
+DownloadDisplayController::GetProgress() {
+  DownloadDisplayController::ProgressInfo progress_info;
+
+  int64_t received_bytes = 0;
+  int64_t total_bytes = 0;
+
+  content::DownloadManager::DownloadVector items;
+  download_manager_->GetAllDownloads(&items);
+  for (auto* item : items) {
+    if (item->GetState() == download::DownloadItem::IN_PROGRESS) {
+      ++progress_info.download_count;
+      if (item->GetTotalBytes() <= 0) {
+        // There may or may not be more data coming down this pipe.
+        progress_info.progress_certain = false;
+      } else {
+        received_bytes += item->GetReceivedBytes();
+        total_bytes += item->GetTotalBytes();
+      }
+    }
+  }
+
+  if (total_bytes > 0) {
+    progress_info.progress_percentage =
+        base::ClampFloor(received_bytes * 100.0 / total_bytes);
+  }
+
+  return progress_info;
+}
diff --git a/chrome/browser/download/bubble/download_display_controller.h b/chrome/browser/download/bubble/download_display_controller.h
new file mode 100644
index 0000000..02bc6ac
--- /dev/null
+++ b/chrome/browser/download/bubble/download_display_controller.h
@@ -0,0 +1,68 @@
+// 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 CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_DISPLAY_CONTROLLER_H_
+#define CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_DISPLAY_CONTROLLER_H_
+
+#include "components/download/content/public/all_download_item_notifier.h"
+
+namespace content {
+class DownloadManager;
+}  // namespace content
+
+namespace base {
+class TimeDelta;
+class OneShotTimer;
+}  // namespace base
+
+class DownloadDisplay;
+
+class DownloadDisplayController
+    : public download::AllDownloadItemNotifier::Observer {
+ public:
+  DownloadDisplayController(DownloadDisplay* display,
+                            content::DownloadManager* download_manager);
+  DownloadDisplayController(const DownloadDisplayController&) = delete;
+  DownloadDisplayController& operator=(const DownloadDisplayController&) =
+      delete;
+  ~DownloadDisplayController() override;
+
+  struct ProgressInfo {
+    bool progress_certain = true;
+    int progress_percentage = 0;
+    int download_count = 0;
+  };
+
+  // Returns a ProgressInfo where |download_count| is the number of currently
+  // active downloads. If we know the final size of all downloads,
+  // |progress_certain| is true. |progress_percentage| is the percentage
+  // complete of all in-progress  downloads.
+  //
+  // This implementation will match the one in download_status_updater.cc
+  ProgressInfo GetProgress();
+
+  void ShowToolbarButton();
+  void HideToolbarButton();
+
+ private:
+  void ScheduleToolbarDisappearance(base::TimeDelta interval);
+
+  void UpdateToolbarButtonState();
+
+  void UpdateDownloadIconDisplayState();
+
+  // AllDownloadItemNotifier::Observer
+  void OnDownloadCreated(content::DownloadManager* manager,
+                         download::DownloadItem* item) override;
+  void OnDownloadUpdated(content::DownloadManager* manager,
+                         download::DownloadItem* item) override;
+
+  // The pointer is created in ToolbarView and owned by ToolbarView.
+  DownloadDisplay* const display_;
+  content::DownloadManager* const download_manager_;
+  download::AllDownloadItemNotifier download_notifier_;
+  base::OneShotTimer icon_disappearance_timer_;
+};
+
+#endif  // CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_DISPLAY_CONTROLLER_H_
diff --git a/chrome/browser/download/bubble/download_display_controller_unittest.cc b/chrome/browser/download/bubble/download_display_controller_unittest.cc
new file mode 100644
index 0000000..ce75e65
--- /dev/null
+++ b/chrome/browser/download/bubble/download_display_controller_unittest.cc
@@ -0,0 +1,109 @@
+// 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 "chrome/browser/download/bubble/download_display_controller.h"
+#include "chrome/browser/download/bubble/download_display.h"
+#include "chrome/browser/download/bubble/download_icon_state.h"
+#include "components/download/public/common/mock_download_item.h"
+#include "content/public/test/mock_download_manager.h"
+#include "content/public/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+using testing::SetArgPointee;
+
+namespace {
+using StrictMockDownloadItem = testing::StrictMock<download::MockDownloadItem>;
+
+class FakeDownloadDisplay : public DownloadDisplay {
+ public:
+  FakeDownloadDisplay() = default;
+  FakeDownloadDisplay(const FakeDownloadDisplay&) = delete;
+  FakeDownloadDisplay& operator=(const FakeDownloadDisplay&) = delete;
+
+  void Show() override { shown_ = true; }
+
+  void Hide() override { shown_ = false; }
+
+  bool IsShowing() override { return shown_; }
+
+  void Enable() override { enabled_ = true; }
+
+  void Disable() override { enabled_ = false; }
+
+  void UpdateDownloadIcon(download::DownloadIconState state) override {}
+
+ private:
+  bool shown_ = false;
+  bool enabled_ = false;
+};
+
+class DownloadDisplayControllerTest : public testing::Test {
+ public:
+  DownloadDisplayControllerTest()
+      : manager_(std::make_unique<NiceMock<content::MockDownloadManager>>()) {}
+  DownloadDisplayControllerTest(const DownloadDisplayControllerTest&) = delete;
+  DownloadDisplayControllerTest& operator=(
+      const DownloadDisplayControllerTest&) = delete;
+
+ protected:
+  NiceMock<content::MockDownloadManager>& manager() { return *manager_.get(); }
+  download::MockDownloadItem& item(size_t index) { return *items_[index]; }
+
+  void InitDownloadItem(const base::FilePath::CharType* path,
+                        download::DownloadItem::DownloadState state) {
+    size_t index = items_.size();
+    items_.push_back(std::make_unique<StrictMockDownloadItem>());
+    EXPECT_CALL(item(index), GetId())
+        .WillRepeatedly(Return(static_cast<uint32_t>(items_.size() + 1)));
+    EXPECT_CALL(item(index), GetState()).WillRepeatedly(Return(state));
+    int received_bytes =
+        state == download::DownloadItem::IN_PROGRESS ? 50 : 100;
+    EXPECT_CALL(item(index), GetReceivedBytes())
+        .WillRepeatedly(Return(received_bytes));
+    EXPECT_CALL(item(index), GetTotalBytes()).WillRepeatedly(Return(100));
+
+    std::vector<download::DownloadItem*> items;
+    for (size_t i = 0; i < items_.size(); ++i) {
+      items.push_back(&item(i));
+    }
+    EXPECT_CALL(*manager_.get(), GetAllDownloads(_))
+        .WillRepeatedly(SetArgPointee<0>(items));
+  }
+
+ private:
+  std::vector<std::unique_ptr<StrictMockDownloadItem>> items_;
+  std::unique_ptr<NiceMock<content::MockDownloadManager>> manager_;
+};
+
+TEST_F(DownloadDisplayControllerTest, GetProgressItemsInProgress) {
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
+                   download::DownloadItem::IN_PROGRESS);
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"),
+                   download::DownloadItem::COMPLETE);
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar4.pdf"),
+                   download::DownloadItem::IN_PROGRESS);
+  FakeDownloadDisplay display;
+  DownloadDisplayController controller(&display, &manager());
+  DownloadDisplayController::ProgressInfo progress = controller.GetProgress();
+
+  EXPECT_EQ(progress.download_count, 2);
+  EXPECT_EQ(progress.progress_percentage, 50);
+}
+
+TEST_F(DownloadDisplayControllerTest, GetProgressItemsAllComplete) {
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
+                   download::DownloadItem::COMPLETE);
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"),
+                   download::DownloadItem::COMPLETE);
+  FakeDownloadDisplay display;
+  DownloadDisplayController controller(&display, &manager());
+  DownloadDisplayController::ProgressInfo progress = controller.GetProgress();
+
+  EXPECT_EQ(progress.download_count, 0);
+  EXPECT_EQ(progress.progress_percentage, 0);
+}
+}  // namespace
diff --git a/chrome/browser/download/bubble/download_icon_state.h b/chrome/browser/download/bubble/download_icon_state.h
new file mode 100644
index 0000000..5b3a6f1
--- /dev/null
+++ b/chrome/browser/download/bubble/download_icon_state.h
@@ -0,0 +1,12 @@
+// 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 CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_ICON_STATE_H_
+#define CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_ICON_STATE_H_
+
+namespace download {
+enum class DownloadIconState { kFailed, kProgress, kComplete };
+}  // namespace download
+
+#endif  // CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_ICON_STATE_H_
diff --git a/chrome/browser/download/download_prefs.cc b/chrome/browser/download/download_prefs.cc
index a45799d..d352852 100644
--- a/chrome/browser/download/download_prefs.cc
+++ b/chrome/browser/download/download_prefs.cc
@@ -663,7 +663,7 @@
   // We only need to configure |allowed_urls| if something is set by policy,
   // otherwise the default object does what we want.
   if (list->GetList().size() != 0) {
-    allowed_urls->Allow(list);
+    allowed_urls->Allow(&base::Value::AsListValue(*list));
 
     // Since we only want to auto-open for the specified urls, block everything
     // else.
diff --git a/chrome/browser/enterprise/connectors/connectors_manager.cc b/chrome/browser/enterprise/connectors/connectors_manager.cc
index 6d14508..b38a27d4 100644
--- a/chrome/browser/enterprise/connectors/connectors_manager.cc
+++ b/chrome/browser/enterprise/connectors/connectors_manager.cc
@@ -150,7 +150,7 @@
   const char* pref = ConnectorPref(connector);
   DCHECK(pref);
 
-  const base::ListValue* policy_value =
+  const base::Value* policy_value =
       pref_change_registrar_.prefs()->GetList(pref);
   if (policy_value && policy_value->is_list()) {
     for (const base::Value& service_settings : policy_value->GetList())
@@ -167,7 +167,7 @@
   const char* pref = ConnectorPref(connector);
   DCHECK(pref);
 
-  const base::ListValue* policy_value =
+  const base::Value* policy_value =
       pref_change_registrar_.prefs()->GetList(pref);
   if (policy_value && policy_value->is_list()) {
     for (const base::Value& service_settings : policy_value->GetList())
@@ -184,7 +184,7 @@
   const char* pref = ConnectorPref(connector);
   DCHECK(pref);
 
-  const base::ListValue* policy_value =
+  const base::Value* policy_value =
       pref_change_registrar_.prefs()->GetList(pref);
   if (policy_value && policy_value->is_list()) {
     for (const base::Value& service_settings : policy_value->GetList())
diff --git a/chrome/browser/enterprise/connectors/device_trust/device_trust_connector_service.cc b/chrome/browser/enterprise/connectors/device_trust/device_trust_connector_service.cc
index 614df56..56651cb8 100644
--- a/chrome/browser/enterprise/connectors/device_trust/device_trust_connector_service.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/device_trust_connector_service.cc
@@ -17,7 +17,8 @@
 namespace {
 
 const base::ListValue* GetPolicyUrlPatterns(PrefService* prefs) {
-  return prefs->GetList(kContextAwareAccessSignalsAllowlistPref);
+  return &base::Value::AsListValue(
+      *prefs->GetList(kContextAwareAccessSignalsAllowlistPref));
 }
 
 bool ConnectorPolicyHasValues(PrefService* profile_prefs) {
diff --git a/chrome/browser/enterprise/connectors/device_trust/device_trust_service_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/device_trust_service_unittest.cc
index eabc4533..ed444c3a 100644
--- a/chrome/browser/enterprise/connectors/device_trust/device_trust_service_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/device_trust_service_unittest.cc
@@ -69,7 +69,7 @@
                        std::make_unique<base::ListValue>());
   }
 
-  const base::ListValue* GetPolicyUrls() {
+  const base::Value* GetPolicyUrls() {
     return prefs_.GetList(kContextAwareAccessSignalsAllowlistPref);
   }
 
diff --git a/chrome/browser/enterprise/connectors/file_system/account_info_utils.cc b/chrome/browser/enterprise/connectors/file_system/account_info_utils.cc
index f075c93..846d6a6 100644
--- a/chrome/browser/enterprise/connectors/file_system/account_info_utils.cc
+++ b/chrome/browser/enterprise/connectors/file_system/account_info_utils.cc
@@ -241,7 +241,7 @@
                                      const std::string& service_provider) {
   const auto path = GetPrefPath(kAccountInfoPrefPathTemplate, service_provider);
 
-  const base::DictionaryValue* val = prefs->GetDictionary(path);
+  const base::Value* val = prefs->GetDictionary(path);
   DCHECK(val);
   return val->Clone();
 }
diff --git a/chrome/browser/enterprise/reporting/extension_request/extension_request_observer.cc b/chrome/browser/enterprise/reporting/extension_request/extension_request_observer.cc
index 1e8f4cf..480a9db 100644
--- a/chrome/browser/enterprise/reporting/extension_request/extension_request_observer.cc
+++ b/chrome/browser/enterprise/reporting/extension_request/extension_request_observer.cc
@@ -96,7 +96,7 @@
 
 void ExtensionRequestObserver::ShowNotification(
     ExtensionRequestNotification::NotifyType type) {
-  const base::DictionaryValue* pending_requests =
+  const base::Value* pending_requests =
       profile_->GetPrefs()->GetDictionary(prefs::kCloudExtensionRequestIds);
 
   if (!pending_requests)
diff --git a/chrome/browser/enterprise/reporting/extension_request/extension_request_observer_unittest.cc b/chrome/browser/enterprise/reporting/extension_request/extension_request_observer_unittest.cc
index d2faa35..1df510fcc 100644
--- a/chrome/browser/enterprise/reporting/extension_request/extension_request_observer_unittest.cc
+++ b/chrome/browser/enterprise/reporting/extension_request/extension_request_observer_unittest.cc
@@ -146,7 +146,7 @@
     close_run_loop.Run();
 
     // Verify that only |expected_removed_requests| are removed from the pref.
-    const base::DictionaryValue* actual_pending_requests =
+    const base::Value* actual_pending_requests =
         profile()->GetPrefs()->GetDictionary(prefs::kCloudExtensionRequestIds);
     EXPECT_EQ(number_of_existing_requests - expected_removed_requests.size(),
               actual_pending_requests->DictSize());
diff --git a/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc b/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc
index 586a3218..b6170a5 100644
--- a/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc
+++ b/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc
@@ -28,7 +28,7 @@
 namespace {
 
 bool IsRequestInDict(const std::string& extension_id,
-                     const base::DictionaryValue* requests) {
+                     const base::Value* requests) {
   return requests->FindKey(extension_id) != nullptr;
 }
 
@@ -102,9 +102,9 @@
   std::string webstore_update_url =
       extension_urls::GetDefaultWebstoreUpdateUrl().spec();
 
-  const base::DictionaryValue* pending_requests =
+  const base::Value* pending_requests =
       profile->GetPrefs()->GetDictionary(prefs::kCloudExtensionRequestIds);
-  const base::DictionaryValue* uploaded_requests =
+  const base::Value* uploaded_requests =
       profile->GetPrefs()->GetDictionary(kCloudExtensionRequestUploadedIds);
 
   for (auto it : pending_requests->DictItems()) {
diff --git a/chrome/browser/enterprise/reporting/profile_report_generator_desktop.cc b/chrome/browser/enterprise/reporting/profile_report_generator_desktop.cc
index 75657f2..0b0d872 100644
--- a/chrome/browser/enterprise/reporting/profile_report_generator_desktop.cc
+++ b/chrome/browser/enterprise/reporting/profile_report_generator_desktop.cc
@@ -45,7 +45,7 @@
     enterprise_management::ChromeUserProfileInfo* report) {
   if (!profile_->GetPrefs()->GetBoolean(prefs::kCloudExtensionRequestEnabled))
     return;
-  const base::DictionaryValue* pending_requests =
+  const base::Value* pending_requests =
       profile_->GetPrefs()->GetDictionary(prefs::kCloudExtensionRequestIds);
 
   // In case a corrupted profile prefs causing |pending_requests| to be null.
diff --git a/chrome/browser/extensions/api/commands/command_service.cc b/chrome/browser/extensions/api/commands/command_service.cc
index ae7c0cd..52c171e 100644
--- a/chrome/browser/extensions/api/commands/command_service.cc
+++ b/chrome/browser/extensions/api/commands/command_service.cc
@@ -295,8 +295,8 @@
 
 Command CommandService::FindCommandByName(const std::string& extension_id,
                                           const std::string& command) const {
-  const base::DictionaryValue* bindings =
-      profile_->GetPrefs()->GetDictionary(prefs::kExtensionCommands);
+  const base::DictionaryValue* bindings = &base::Value::AsDictionaryValue(
+      *profile_->GetPrefs()->GetDictionary(prefs::kExtensionCommands));
   for (base::DictionaryValue::Iterator it(*bindings); !it.IsAtEnd();
        it.Advance()) {
     const base::DictionaryValue* item = NULL;
diff --git a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc
index 02bdea1..84e128f 100644
--- a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc
+++ b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc
@@ -57,7 +57,7 @@
 
 // ContainsAppIdByHash returns true iff the SHA-256 hash of one of the
 // elements of |list| equals |hash|.
-bool ContainsAppIdByHash(const base::ListValue& list,
+bool ContainsAppIdByHash(const base::Value& list,
                          const std::vector<uint8_t>& hash) {
   if (hash.size() != crypto::kSHA256Length) {
     return false;
@@ -170,7 +170,7 @@
 
   Profile* const profile = Profile::FromBrowserContext(browser_context());
   const PrefService* const prefs = profile->GetPrefs();
-  const base::ListValue* const permit_attestation =
+  const base::Value* const permit_attestation =
       prefs->GetList(prefs::kSecurityKeyPermitAttestation);
 
   return RespondNow(ArgumentList(
@@ -200,7 +200,7 @@
   // prompt is shown.
   Profile* const profile = Profile::FromBrowserContext(browser_context());
   const PrefService* const prefs = profile->GetPrefs();
-  const base::ListValue* const permit_attestation =
+  const base::Value* const permit_attestation =
       prefs->GetList(prefs::kSecurityKeyPermitAttestation);
 
   for (const auto& entry : permit_attestation->GetList()) {
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index c11ef1b..197e419 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -184,7 +184,8 @@
     feature_list_.InitWithFeatures(
         /*enabled_features=*/
         {blink::features::kInterestGroupStorage,
-         blink::features::kAdInterestGroupAPI, blink::features::kFledge},
+         blink::features::kAdInterestGroupAPI, blink::features::kFledge,
+         blink::features::kFencedFrames},
         /*disabled_features=*/
         {});
     net::test_server::RegisterDefaultHandlers(embedded_test_server());
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc
index 03e982d4..0adefd0 100644
--- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc
+++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc
@@ -132,7 +132,7 @@
     // allowed in chrome/common/extensions/api/_permission_features.json
     return true;
   }
-  const base::ListValue* list =
+  const base::Value* list =
       profile->GetPrefs()->GetList(prefs::kAttestationExtensionAllowlist);
   DCHECK_NE(list, nullptr);
   base::Value value(extension->id());
diff --git a/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.cc b/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.cc
index fd3d6536..517d807 100644
--- a/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.cc
+++ b/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.cc
@@ -73,7 +73,7 @@
 void ForceInstalledAffiliatedExtensionApiTest::SetUpOnMainThread() {
   // Log in user that was created with
   // policy::AffiliationTestHelper::PreLoginUser() in the PRE_ test.
-  const base::ListValue* users =
+  const base::Value* users =
       g_browser_process->local_state()->GetList("LoggedInUsers");
   if (!users->GetList().empty()) {
     policy::AffiliationTestHelper::LoginUser(affiliation_mixin_.account_id());
diff --git a/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc b/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc
index 35f73420..2c583cd 100644
--- a/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc
+++ b/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc
@@ -51,7 +51,7 @@
   // All native messaging hosts are allowed if there is no blocklist.
   if (!pref_service->IsManagedPreference(pref_names::kNativeMessagingBlocklist))
     return allow_result;
-  const base::ListValue* blocklist =
+  const base::Value* blocklist =
       pref_service->GetList(pref_names::kNativeMessagingBlocklist);
   if (!blocklist)
     return allow_result;
@@ -67,7 +67,7 @@
   // The native messaging host is blocklisted. Check the allowlist.
   if (pref_service->IsManagedPreference(
           pref_names::kNativeMessagingAllowlist)) {
-    const base::ListValue* allowlist =
+    const base::Value* allowlist =
         pref_service->GetList(pref_names::kNativeMessagingAllowlist);
     if (allowlist && base::Contains(allowlist->GetList(), name_value))
       return allow_result;
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.cc
index 1c906765..8b55f70 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.cc
@@ -41,7 +41,7 @@
 bool IsOsReauthAllowedAsh(Profile* profile,
                           base::TimeDelta auth_token_lifetime) {
   const bool user_cannot_manually_enter_password =
-      !chromeos::password_visibility::AccountHasUserFacingPassword(
+      !ash::password_visibility::AccountHasUserFacingPassword(
           chromeos::ProfileHelper::Get()
               ->GetUserByProfile(profile)
               ->GetAccountId());
diff --git a/chrome/browser/extensions/api/printing/print_job_submitter.cc b/chrome/browser/extensions/api/printing/print_job_submitter.cc
index 63ad34c..522dd2b7 100644
--- a/chrome/browser/extensions/api/printing/print_job_submitter.cc
+++ b/chrome/browser/extensions/api/printing/print_job_submitter.cc
@@ -75,7 +75,7 @@
                                 const std::string& extension_id) {
   if (g_skip_confirmation_dialog_for_testing)
     return false;
-  const base::ListValue* list =
+  const base::Value* list =
       Profile::FromBrowserContext(browser_context)
           ->GetPrefs()
           ->GetList(prefs::kPrintingAPIExtensionsAllowlist);
diff --git a/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.cc b/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.cc
index bc013a8c..3956dfc 100644
--- a/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.cc
+++ b/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.cc
@@ -240,8 +240,7 @@
   if (authenticator_allocator_) {
     extended_authenticator_ = authenticator_allocator_.Run(helper.get());
   } else {
-    extended_authenticator_ =
-        chromeos::ExtendedAuthenticator::Create(helper.get());
+    extended_authenticator_ = ash::ExtendedAuthenticator::Create(helper.get());
   }
 
   // The extension function needs to stay alive while the authenticator runs the
@@ -630,7 +629,7 @@
   const user_manager::User* const user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(
           GetActiveProfile(browser_context()));
-  const chromeos::UserContext user_context(*user);
+  const ash::UserContext user_context(*user);
 
   Respond(ArgumentList(SetModes::Results::Create()));
 }
diff --git a/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.h b/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.h
index ab6ee4b..5922bb5a 100644
--- a/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.h
+++ b/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.h
@@ -14,18 +14,18 @@
 #include "chrome/common/extensions/api/quick_unlock_private.h"
 #include "extensions/browser/extension_function.h"
 
-namespace chromeos {
+namespace ash {
 class AuthStatusConsumer;
 class ExtendedAuthenticator;
-}  // namespace chromeos
+}  // namespace ash
 
 namespace extensions {
 
 class QuickUnlockPrivateGetAuthTokenFunction : public ExtensionFunction {
  public:
   using AuthenticatorAllocator =
-      base::RepeatingCallback<chromeos::ExtendedAuthenticator*(
-          chromeos::AuthStatusConsumer* auth_status_consumer)>;
+      base::RepeatingCallback<ash::ExtendedAuthenticator*(
+          ash::AuthStatusConsumer* auth_status_consumer)>;
 
   QuickUnlockPrivateGetAuthTokenFunction();
   QuickUnlockPrivateGetAuthTokenFunction(
@@ -56,7 +56,7 @@
 
  private:
   ChromeExtensionFunctionDetails chrome_details_;
-  scoped_refptr<chromeos::ExtendedAuthenticator> extended_authenticator_;
+  scoped_refptr<ash::ExtendedAuthenticator> extended_authenticator_;
   AuthenticatorAllocator authenticator_allocator_;
 };
 
diff --git a/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc b/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc
index 236fbb4f..758699f 100644
--- a/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc
@@ -657,7 +657,7 @@
   QuickUnlockPrivateSetModesFunction::ModesChangedEventHandler
       modes_changed_handler_;
   bool expect_modes_changed_ = false;
-  chromeos::UserContext auth_token_user_context_;
+  ash::UserContext auth_token_user_context_;
   std::string token_;
 };
 
diff --git a/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc b/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc
index 2c1674d..15dde6b6 100644
--- a/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc
+++ b/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc
@@ -42,34 +42,34 @@
     default;
 
 void QuickUnlockPrivateGetAuthTokenHelper::Run(
-    chromeos::ExtendedAuthenticator* extended_authenticator,
+    ash::ExtendedAuthenticator* extended_authenticator,
     const std::string& password,
     QuickUnlockPrivateGetAuthTokenHelper::ResultCallback callback) {
   callback_ = std::move(callback);
 
   const user_manager::User* const user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
-  chromeos::UserContext user_context(*user);
-  user_context.SetKey(chromeos::Key(password));
+  ash::UserContext user_context(*user);
+  user_context.SetKey(ash::Key(password));
 
   AddRef();
 
   content::GetUIThreadTaskRunner({})->PostTask(
       FROM_HERE,
-      base::BindOnce(&chromeos::ExtendedAuthenticator::AuthenticateToCheck,
+      base::BindOnce(&ash::ExtendedAuthenticator::AuthenticateToCheck,
                      extended_authenticator, user_context,
                      base::OnceClosure()));
 }
 
 void QuickUnlockPrivateGetAuthTokenHelper::OnAuthFailure(
-    const chromeos::AuthFailure& error) {
+    const ash::AuthFailure& error) {
   std::move(callback_).Run(false, nullptr, kPasswordIncorrect);
 
   Release();  // Balanced in Run().
 }
 
 void QuickUnlockPrivateGetAuthTokenHelper::OnAuthSuccess(
-    const chromeos::UserContext& user_context) {
+    const ash::UserContext& user_context) {
   auto token_info = std::make_unique<TokenInfo>();
 
   QuickUnlockStorage* quick_unlock_storage =
diff --git a/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.h b/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.h
index 253e5d1..a43dbfa7 100644
--- a/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.h
+++ b/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.h
@@ -14,10 +14,10 @@
 
 class Profile;
 
-namespace chromeos {
+namespace ash {
 class ExtendedAuthenticator;
 class UserContext;
-}  // namespace chromeos
+}  // namespace ash
 
 namespace extensions {
 
@@ -28,21 +28,21 @@
 }  // namespace api
 
 // A single-use adaptor to make calls to
-//   chromeos::ExtendedAuthenticator::AuthenticateToCheck()
+//   ash::ExtendedAuthenticator::AuthenticateToCheck()
 // and pass result back to a single callback. Re. object lifetime, caller just
 // have to call:
 //
 //   scoped_refptr<QuickUnlockPrivateGetAuthTokenHelper> helper =
 //      base::MakeRefCounted<QuickUnlockPrivateGetAuthTokenHelper>(...);
 //   ...
-//   // Attach |helper| to a chromeos::ExtendedAuthenticator.
+//   // Attach |helper| to a ash::ExtendedAuthenticator.
 //   ...
 //   // Bind callback and pass as argument.
 //   helper->Run(...);
 //
 // Hereafter, the caller need not worry about |helper|'s lifetime.
 class QuickUnlockPrivateGetAuthTokenHelper
-    : public chromeos::AuthStatusConsumer,
+    : public ash::AuthStatusConsumer,
       public base::RefCountedThreadSafe<
           QuickUnlockPrivateGetAuthTokenHelper,
           content::BrowserThread::DeleteOnUIThread> {
@@ -62,7 +62,7 @@
   QuickUnlockPrivateGetAuthTokenHelper& operator=(
       const QuickUnlockPrivateGetAuthTokenHelper&) = delete;
 
-  void Run(chromeos::ExtendedAuthenticator* extended_authenticator,
+  void Run(ash::ExtendedAuthenticator* extended_authenticator,
            const std::string& password,
            ResultCallback callback);
 
@@ -76,8 +76,8 @@
       content::BrowserThread::UI>;
 
   // AuthStatusConsumer overrides.
-  void OnAuthFailure(const chromeos::AuthFailure& error) override;
-  void OnAuthSuccess(const chromeos::UserContext& user_context) override;
+  void OnAuthFailure(const ash::AuthFailure& error) override;
+  void OnAuthSuccess(const ash::UserContext& user_context) override;
 
   Profile* profile_;
   ResultCallback callback_;
diff --git a/chrome/browser/extensions/api/terminal/terminal_private_api.cc b/chrome/browser/extensions/api/terminal/terminal_private_api.cc
index c41de4c..c552b40 100644
--- a/chrome/browser/extensions/api/terminal/terminal_private_api.cc
+++ b/chrome/browser/extensions/api/terminal/terminal_private_api.cc
@@ -654,7 +654,7 @@
       Profile::FromBrowserContext(browser_context()));
   PrefService* service =
       Profile::FromBrowserContext(browser_context())->GetPrefs();
-  const base::DictionaryValue* value =
+  const base::Value* value =
       service->GetDictionary(crostini::prefs::kCrostiniTerminalSettings);
   return RespondNow(OneArgument(value->Clone()));
 }
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_unittest.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_unittest.cc
index 4612836..e15f97f 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_unittest.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_unittest.cc
@@ -109,7 +109,7 @@
 void VerifyPendingList(const std::map<ExtensionId, ExtensionRequestData>&
                            expected_pending_requests,
                        Profile* profile) {
-  const base::DictionaryValue* actual_pending_requests =
+  const base::Value* actual_pending_requests =
       profile->GetPrefs()->GetDictionary(prefs::kCloudExtensionRequestIds);
   ASSERT_EQ(expected_pending_requests.size(),
             actual_pending_requests->DictSize());
diff --git a/chrome/browser/extensions/extension_garbage_collector_chromeos_unittest.cc b/chrome/browser/extensions/extension_garbage_collector_chromeos_unittest.cc
index 4e21480..62ea7f0 100644
--- a/chrome/browser/extensions/extension_garbage_collector_chromeos_unittest.cc
+++ b/chrome/browser/extensions/extension_garbage_collector_chromeos_unittest.cc
@@ -182,12 +182,13 @@
 
   EXPECT_TRUE(base::PathExists(path_id2_1));
 
-  const base::DictionaryValue* shared_extensions = testing_local_state_.Get()->
-      GetDictionary(ExtensionAssetsManagerChromeOS::kSharedExtensions);
+  const base::Value* shared_extensions =
+      testing_local_state_.Get()->GetDictionary(
+          ExtensionAssetsManagerChromeOS::kSharedExtensions);
   ASSERT_TRUE(shared_extensions);
 
-  EXPECT_FALSE(shared_extensions->HasKey(kExtensionId1));
-  EXPECT_TRUE(shared_extensions->HasKey(kExtensionId2));
+  EXPECT_FALSE(shared_extensions->FindKey(kExtensionId1));
+  EXPECT_TRUE(shared_extensions->FindKey(kExtensionId2));
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_override_apitest.cc b/chrome/browser/extensions/extension_override_apitest.cc
index ccf206b..9a2739a5 100644
--- a/chrome/browser/extensions/extension_override_apitest.cc
+++ b/chrome/browser/extensions/extension_override_apitest.cc
@@ -47,9 +47,9 @@
 
   bool CheckHistoryOverridesContainsNoDupes() {
     // There should be no duplicate entries in the preferences.
-    const base::DictionaryValue* overrides =
-        browser()->profile()->GetPrefs()->GetDictionary(
-            ExtensionWebUI::kExtensionURLOverrides);
+    const base::DictionaryValue* overrides = &base::Value::AsDictionaryValue(
+        *browser()->profile()->GetPrefs()->GetDictionary(
+            ExtensionWebUI::kExtensionURLOverrides));
 
     const base::ListValue* values = nullptr;
     if (!overrides->GetList("history", &values))
diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc
index 3874afb..778fada 100644
--- a/chrome/browser/extensions/extension_prefs_unittest.cc
+++ b/chrome/browser/extensions/extension_prefs_unittest.cc
@@ -1326,7 +1326,7 @@
   TestExtensionPrefs prefs(base::ThreadTaskRunnerHandle::Get());
 
   auto has_extension_pref_entry = [&prefs](const std::string& id) {
-    const base::DictionaryValue* extensions_dictionary =
+    const base::Value* extensions_dictionary =
         prefs.pref_service()->GetDictionary(pref_names::kExtensions);
     if (!extensions_dictionary) {
       ADD_FAILURE() << "Extensions dictionary is missing!";
diff --git a/chrome/browser/extensions/extension_service_test_base.cc b/chrome/browser/extensions/extension_service_test_base.cc
index 4144343..76ad82b 100644
--- a/chrome/browser/extensions/extension_service_test_base.cc
+++ b/chrome/browser/extensions/extension_service_test_base.cc
@@ -250,7 +250,7 @@
 }
 
 size_t ExtensionServiceTestBase::GetPrefKeyCount() {
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       profile()->GetPrefs()->GetDictionary(pref_names::kExtensions);
   if (!dict) {
     ADD_FAILURE();
@@ -272,8 +272,8 @@
                                        expected_val ? "true" : "false");
 
   PrefService* prefs = profile()->GetPrefs();
-  const base::DictionaryValue* dict =
-      prefs->GetDictionary(pref_names::kExtensions);
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *prefs->GetDictionary(pref_names::kExtensions));
   if (!dict) {
     return testing::AssertionFailure()
         << "extension.settings does not exist " << msg;
@@ -306,8 +306,8 @@
       base::NumberToString(expected_val).c_str());
 
   PrefService* prefs = profile()->GetPrefs();
-  const base::DictionaryValue* dict =
-      prefs->GetDictionary(pref_names::kExtensions);
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *prefs->GetDictionary(pref_names::kExtensions));
   ASSERT_TRUE(dict != NULL) << msg;
   const base::DictionaryValue* pref = NULL;
   ASSERT_TRUE(dict->GetDictionary(extension_id, &pref)) << msg;
@@ -323,8 +323,8 @@
                                        extension_id.c_str(), pref_path.c_str(),
                                        expected_val.c_str());
 
-  const base::DictionaryValue* dict =
-      profile()->GetPrefs()->GetDictionary(pref_names::kExtensions);
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *profile()->GetPrefs()->GetDictionary(pref_names::kExtensions));
   ASSERT_TRUE(dict != NULL) << msg;
   const base::DictionaryValue* pref = NULL;
   std::string manifest_path = extension_id + ".manifest";
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 27de263..afdf837 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -693,8 +693,8 @@
 
   const base::DictionaryValue* GetExtensionPref(const std::string& extension_id,
                                                 const std::string& pref_path) {
-    const base::DictionaryValue* dict =
-        profile()->GetPrefs()->GetDictionary(pref_names::kExtensions);
+    const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+        *profile()->GetPrefs()->GetDictionary(pref_names::kExtensions));
     if (!dict) {
       return nullptr;
     }
diff --git a/chrome/browser/extensions/extension_web_ui.cc b/chrome/browser/extensions/extension_web_ui.cc
index 75095fb..b8b7e28b 100644
--- a/chrome/browser/extensions/extension_web_ui.cc
+++ b/chrome/browser/extensions/extension_web_ui.cc
@@ -356,8 +356,9 @@
   DCHECK(url.SchemeIs(content::kChromeUIScheme));
 
   Profile* profile = Profile::FromBrowserContext(browser_context);
-  const base::DictionaryValue* overrides = profile->GetPrefs()->GetDictionary(
-      ExtensionWebUI::kExtensionURLOverrides);
+  const base::DictionaryValue* overrides =
+      &base::Value::AsDictionaryValue(*profile->GetPrefs()->GetDictionary(
+          ExtensionWebUI::kExtensionURLOverrides));
 
   const base::ListValue* url_list = nullptr;
   if (!overrides || !overrides->GetList(url.host_piece(), &url_list))
@@ -450,8 +451,8 @@
 bool ExtensionWebUI::HandleChromeURLOverrideReverse(
     GURL* url, content::BrowserContext* browser_context) {
   Profile* profile = Profile::FromBrowserContext(browser_context);
-  const base::DictionaryValue* overrides =
-      profile->GetPrefs()->GetDictionary(kExtensionURLOverrides);
+  const base::DictionaryValue* overrides = &base::Value::AsDictionaryValue(
+      *profile->GetPrefs()->GetDictionary(kExtensionURLOverrides));
   if (!overrides)
     return false;
 
diff --git a/chrome/browser/extensions/extension_web_ui_unittest.cc b/chrome/browser/extensions/extension_web_ui_unittest.cc
index 0963950..22fcbaa 100644
--- a/chrome/browser/extensions/extension_web_ui_unittest.cc
+++ b/chrome/browser/extensions/extension_web_ui_unittest.cc
@@ -212,7 +212,7 @@
 
   // Duplicates should be removed (in response to ExtensionSystem::ready()).
   // Only a single entry should remain.
-  const base::DictionaryValue* overrides =
+  const base::Value* overrides =
       prefs->GetDictionary(ExtensionWebUI::kExtensionURLOverrides);
   ASSERT_TRUE(overrides);
   const base::Value* newtab_overrides =
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_tracker.cc b/chrome/browser/extensions/forced_extensions/force_installed_tracker.cc
index 11faa955..468981e 100644
--- a/chrome/browser/extensions/forced_extensions/force_installed_tracker.cc
+++ b/chrome/browser/extensions/forced_extensions/force_installed_tracker.cc
@@ -118,7 +118,7 @@
   DCHECK(status_ == kWaitingForPolicyService ||
          status_ == kWaitingForInstallForcelistPref);
 
-  const base::DictionaryValue* value =
+  const base::Value* value =
       pref_service_->GetDictionary(pref_names::kInstallForceList);
   if (!forced_extensions_pref_ready_ && value && !value->DictEmpty()) {
     forced_extensions_pref_ready_ = true;
@@ -142,7 +142,7 @@
   registry_observation_.Observe(registry_.get());
   collector_observation_.Observe(InstallStageTracker::Get(profile_));
 
-  const base::DictionaryValue* value =
+  const base::Value* value =
       pref_service_->GetDictionary(pref_names::kInstallForceList);
   if (value) {
     // Add each extension to |extensions_|.
diff --git a/chrome/browser/external_protocol/external_protocol_handler.cc b/chrome/browser/external_protocol/external_protocol_handler.cc
index 34eaaae..16e07067 100644
--- a/chrome/browser/external_protocol/external_protocol_handler.cc
+++ b/chrome/browser/external_protocol/external_protocol_handler.cc
@@ -241,7 +241,7 @@
   if (!initiating_origin)
     return false;
 
-  const base::ListValue* exempted_protocols =
+  const base::Value* exempted_protocols =
       prefs->GetList(prefs::kAutoLaunchProtocolsFromOrigins);
   if (!exempted_protocols)
     return false;
@@ -326,7 +326,7 @@
 
     if (MayRememberAllowDecisionsForThisOrigin(initiating_origin)) {
       // Check if there is a matching {Origin+Protocol} pair exemption:
-      const base::DictionaryValue* allowed_origin_protocol_pairs =
+      const base::Value* allowed_origin_protocol_pairs =
           profile_prefs->GetDictionary(
               prefs::kProtocolHandlerPerOriginAllowedProtocols);
       const base::Value* allowed_protocols_for_origin =
diff --git a/chrome/browser/external_protocol/external_protocol_handler_unittest.cc b/chrome/browser/external_protocol/external_protocol_handler_unittest.cc
index d356d73..15a57690 100644
--- a/chrome/browser/external_protocol/external_protocol_handler_unittest.cc
+++ b/chrome/browser/external_protocol/external_protocol_handler_unittest.cc
@@ -426,7 +426,7 @@
       kScheme_2, &example_origin_2, profile_.get());
   EXPECT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);
 
-  const base::DictionaryValue* protocol_origin_pairs =
+  const base::Value* protocol_origin_pairs =
       profile_->GetPrefs()->GetDictionary(
           prefs::kProtocolHandlerPerOriginAllowedProtocols);
   base::Value expected_allowed_protocols_for_example_origin_1(
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 8c58a15..c40b642db 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -580,17 +580,17 @@
   {
     "name": "bookmark-bottom-sheet",
     "owners": [ "wylieb" ],
-    "expiry_milestone": 98
+    "expiry_milestone": 102
   },
   {
     "name": "bookmarks-improved-save-flow",
     "owners": ["wylieb", "fgorski", "mdjones"],
-    "expiry_milestone": 100
+    "expiry_milestone": 102
   },
   {
     "name": "bookmarks-refresh",
     "owners": ["wylieb", "fgorski", "mdjones"],
-    "expiry_milestone": 100
+    "expiry_milestone": 102
   },
   {
     "name": "borealis-big-gl",
@@ -1067,7 +1067,7 @@
   },
   {
     "name": "default-browser-fullscreen-promo-cta-experiment",
-    "owners": [ "javierrobles", "bling-flags@google.com" ],
+    "owners": [ "rkgibson@google.com", "bling-flags@google.com" ],
     "expiry_milestone": 93
   },
   {
@@ -1077,12 +1077,12 @@
   },
   {
     "name": "default-browser-promo-non-modal",
-    "owners": [ "javierrobles", "rkgibson@google.com", "bling-flags@google.com" ],
+    "owners": [ "rkgibson@google.com", "bling-flags@google.com" ],
     "expiry_milestone": 97
   },
   {
     "name": "default-browser-promo-tailored",
-    "owners": [ "javierrobles", "bling-flags@google.com" ],
+    "owners": [ "rkgibson@google.com", "bling-flags@google.com" ],
     "expiry_milestone": 97
   },
   {
@@ -1814,6 +1814,11 @@
     "expiry_milestone": 98
   },
   {
+    "name": "enable-desktop-pwas-default-offline-page",
+    "owners": [ "catherinecheng@google.com", "anosua@google.com", "ericwilligers@google.com", "alexbn@google.com", "desktop-pwas-team@google.com" ],
+    "expiry_milestone": 110
+  },
+  {
     "name": "enable-desktop-pwas-elided-extensions-menu",
     "owners": [ "cmp@chromium.org", "desktop-pwas-team@google.com" ],
     "expiry_milestone": 101
@@ -3603,6 +3608,11 @@
     "expiry_milestone": 99
   },
   {
+    "name": "ios-media-permissions-control",
+    "owners": ["ginnyhuang","bling-flags@google.com"],
+    "expiry_milestone": 110
+  },
+  {
     "name": "ios-persist-crash-restore-infobar",
     "owners": [ "thegreenfrog", "bling-flags@google.com" ],
     "expiry_milestone": 98
@@ -3657,6 +3667,14 @@
     "expiry_milestone": 130
   },
   {
+    "name": "lacros-profile-migration-for-any-user",
+    "owners": ["ythjkt", "hidehiko", "lacros-team@google.com"],
+    // Once Lacros is launched, this flag can be removed. Until then, this
+    // absolutely must not expire. We do not yet have a launch milestone.
+    // TODO(https://crbug.com/1148474).
+    "expiry_milestone": 130
+  },
+  {
     "name": "lacros-selection",
     "owners": [ "kimjae", "erikchen", "lacros-team@google.com" ],
     "expiry_milestone": 130
@@ -3720,6 +3738,11 @@
     "expiry_milestone": 104
   },
   {
+    "name": "link-capturing-ui-update",
+    "owners": ["tsergeant", "chromeos-apps-foundation-team@google.com"],
+    "expiry_milestone": 105
+  },
+  {
     "name": "list-all-display-modes",
     "owners": [ "//ui/display/OWNERS" ],
     // This flag is used for debugging and development purposes to list all
@@ -4683,7 +4706,7 @@
   {
     "name": "read-later",
     "owners": [ "chrome-desktop-ui-sea@google.com", "corising", "wylieb", "chrome-collections@google.com" ],
-    "expiry_milestone": 98
+    "expiry_milestone": 102
   },
   {
     "name": "read-later-new-badge-promo",
@@ -4693,7 +4716,7 @@
   {
     "name": "read-later-reminder-notification",
     "owners": [ "wylieb" ],
-    "expiry_milestone": 98
+    "expiry_milestone": 102
   },
   {
     "name": "reader-mode-heuristics",
@@ -5037,11 +5060,6 @@
     "expiry_milestone": 98
   },
   {
-    "name": "sharing-hub-link-toggle",
-    "owners": [ "sophey", "chrome-sharing-eng@google.com" ],
-    "expiry_milestone": 98
-  },
-  {
     "name": "sharing-prefer-vapid",
     "owners": [ "//chrome/browser/sharing/OWNERS" ],
     "expiry_milestone": 87
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 4345791..77165478 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -790,6 +790,11 @@
 const char kDesktopPWAsRemoveStatusBarDescription[] =
     "Hides the status bar popup in Desktop PWA app windows.";
 
+const char kDesktopPWAsDefaultOfflinePageName[] =
+    "Desktop PWAs default offline page";
+const char kDesktopPWAsDefaultOfflinePageDescription[] =
+    "Shows customised default offline page when web app is offline.";
+
 const char kDesktopPWAsElidedExtensionsMenuName[] =
     "Desktop PWAs elided extensions menu";
 const char kDesktopPWAsElidedExtensionsMenuDescription[] =
@@ -1043,14 +1048,6 @@
     "public. This is a first step towards full enforcement of CORS-RFC1918: "
     "https://wicg.github.io/cors-rfc1918";
 
-const char kCrossOriginEmbedderPolicyCredentiallessName[] =
-    "Enable Cross-Origin-Embedder-Policy: credentialless";
-const char kCrossOriginEmbedderPolicyCredentiallessDescription[] =
-    "Credentialless is a Cross-Origin-Embedder-Policy (COEP) variant. "
-    "COEP:credentialless causes no-cors cross-origin requests not to include "
-    "credentials (cookies, client certificates, etc...). Similarly to "
-    "require-corp, it can be used to enable cross-origin-isolation.";
-
 const char kDeprecateAltClickName[] =
     "Enable Alt+Click deprecation notifications";
 const char kDeprecateAltClickDescription[] =
@@ -3495,10 +3492,6 @@
     "Enable showing the start surface when launching Chrome via the "
     "launcher.";
 
-const char kSharingHubLinkToggleName[] = "Sharing Hub Link Toggle";
-const char kSharingHubLinkToggleDescription[] =
-    "Enable the link toggle in the Sharing Hub.";
-
 const char kStrictSiteIsolationName[] = "Strict site isolation";
 const char kStrictSiteIsolationDescription[] =
     "Security mode that enables site isolation for all sites (SitePerProcess). "
@@ -4890,6 +4883,15 @@
     "first restart can take some time to setup lacros-chrome. Please DO NOT "
     "attempt to turn off the device during the restart.";
 
+const char kLacrosProfileMigrationForAnyUserName[] =
+    "Lacros profile migration for any user";
+const char kLacrosProfileMigrationForAnyUserDescription[] =
+    "Enables lacros profile migration that are currently only enabled for "
+    "certain users. Please enable with CAUTION. Enabling profile migration "
+    "means that any pre-existing lacros data will be wiped and replaced with "
+    "data migrated from ash. It also has a side effect that lacros will be "
+    "disbled until profile migration is completed.";
+
 const char kLimitShelfItemsToActiveDeskName[] =
     "Limit Shelf items to active desk";
 const char kLimitShelfItemsToActiveDeskDescription[] =
@@ -5293,6 +5295,12 @@
 const char kEnableTtsLacrosSupportName[] = "Enable tts lacros support";
 const char kEnableTtsLacrosSupportDescription[] =
     "Enable or disable lacros support for text to speech.";
+
+extern const char kLinkCapturingUiUpdateName[] =
+    "Enable updated link capturing UI";
+extern const char kLinkCapturingUiUpdateDescription[] =
+    "Enables updated UI for link capturing flows from the browser to apps, "
+    "including the intent picker and an in-app link capturing prompt.";
 #endif  // defined(OS_CHROMEOS)
 
 #if defined(ARCH_CPU_X86_FAMILY) && BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 2388d0a..0c480d7 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -473,6 +473,9 @@
 extern const char kDesktopPWAsRemoveStatusBarName[];
 extern const char kDesktopPWAsRemoveStatusBarDescription[];
 
+extern const char kDesktopPWAsDefaultOfflinePageName[];
+extern const char kDesktopPWAsDefaultOfflinePageDescription[];
+
 extern const char kDesktopPWAsElidedExtensionsMenuName[];
 extern const char kDesktopPWAsElidedExtensionsMenuDescription[];
 
@@ -569,9 +572,6 @@
 extern const char kBlockInsecurePrivateNetworkRequestsName[];
 extern const char kBlockInsecurePrivateNetworkRequestsDescription[];
 
-extern const char kCrossOriginEmbedderPolicyCredentiallessName[];
-extern const char kCrossOriginEmbedderPolicyCredentiallessDescription[];
-
 extern const char kDeprecateAltClickName[];
 extern const char kDeprecateAltClickDescription[];
 
@@ -2009,9 +2009,6 @@
 extern const char kStartSurfaceAndroidName[];
 extern const char kStartSurfaceAndroidDescription[];
 
-extern const char kSharingHubLinkToggleName[];
-extern const char kSharingHubLinkToggleDescription[];
-
 extern const char kStrictSiteIsolationName[];
 extern const char kStrictSiteIsolationDescription[];
 
@@ -2776,6 +2773,9 @@
 extern const char kImeAssistMultiWordLacrosSupportName[];
 extern const char kImeAssistMultiWordLacrosSupportDescription[];
 
+extern const char kLacrosProfileMigrationForAnyUserName[];
+extern const char kLacrosProfileMigrationForAnyUserDescription[];
+
 extern const char kImeAssistPersonalInfoName[];
 extern const char kImeAssistPersonalInfoDescription[];
 
@@ -3063,6 +3063,9 @@
 
 extern const char kEnableTtsLacrosSupportName[];
 extern const char kEnableTtsLacrosSupportDescription[];
+
+extern const char kLinkCapturingUiUpdateName[];
+extern const char kLinkCapturingUiUpdateDescription[];
 #endif  // defined(OS_CHROMEOS)
 
 #if defined(ARCH_CPU_X86_FAMILY) && BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 0b6cacb..2f25277 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -260,7 +260,6 @@
     &kServiceManagerForDownload,
     &kShareButtonInTopToolbar,
     &kSharedClipboardUI,
-    &kSharingHubLinkToggle,
     &kSpannableInlineAutocomplete,
     &kSpecialLocaleWrapper,
     &kSpecialUserDecision,
@@ -715,9 +714,6 @@
 const base::Feature kShareButtonInTopToolbar{"ShareButtonInTopToolbar",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kSharingHubLinkToggle{"SharingHubLinkToggle",
-                                          base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kSpannableInlineAutocomplete{
     "SpannableInlineAutocomplete", base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 15ba65d..af6a878c 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -481,7 +481,6 @@
     public static final String SHARED_CLIPBOARD_UI = "SharedClipboardUI";
     public static final String SHARED_HIGHLIGHTING_V2 = "SharedHighlightingV2";
     public static final String SHARED_HIGHLIGHTING_AMP = "SharedHighlightingAmp";
-    public static final String SHARING_HUB_LINK_TOGGLE = "SharingHubLinkToggle";
     public static final String SHOPPING_LIST = "ShoppingList";
     public static final String SHOW_TRUSTED_PUBLISHER_URL = "ShowTrustedPublisherURL";
     public static final String SMART_SUGGESTION_FOR_LARGE_DOWNLOADS =
diff --git a/chrome/browser/font_prewarmer_tab_helper.cc b/chrome/browser/font_prewarmer_tab_helper.cc
new file mode 100644
index 0000000..3c878da
--- /dev/null
+++ b/chrome/browser/font_prewarmer_tab_helper.cc
@@ -0,0 +1,225 @@
+// 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 "chrome/browser/font_prewarmer_tab_helper.h"
+
+#include <set>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/history_clusters/history_clusters_tab_helper.h"
+#include "chrome/browser/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/common/font_prewarmer.mojom.h"
+#include "components/history/content/browser/history_context_helper.h"
+#include "components/history/core/browser/history_constants.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/no_state_prefetch/browser/no_state_prefetch_manager.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+#include "components/search_engines/template_url_service.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_process_host_observer.h"
+#include "content/public/browser/web_contents.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+
+namespace {
+
+const char kSearchResultsPagePrimaryFontsPref[] =
+    "cached_fonts.search_results_page.primary";
+const char kSearchResultsPageFallbackFontsPref[] =
+    "cached_fonts.search_results_page.fallback";
+
+// Key used to associate FontPrewarmerProfileState with BrowserContext.
+const void* const kUserDataKey = &kUserDataKey;
+
+// Returns the font names previously stored to the specified key.
+std::vector<std::string> GetFontNamesFromPrefsForKey(Profile* profile,
+                                                     const char* pref_name) {
+  const base::Value* font_name_list = profile->GetPrefs()->GetList(pref_name);
+  DCHECK(font_name_list && font_name_list->is_list());
+  const auto font_name_list_view = font_name_list->GetList();
+  if (font_name_list_view.empty())
+    return {};
+
+  std::vector<std::string> font_names;
+  for (const auto& font_name_value : font_name_list_view) {
+    if (const std::string* font_name = font_name_value.GetIfString())
+      font_names.push_back(*font_name);
+  }
+  return font_names;
+}
+
+// Saves font names to prefs.
+void SaveFontNamesToPref(Profile* profile,
+                         const char* pref_name,
+                         const std::vector<std::string>& font_family_names) {
+  std::vector<base::Value> font_family_names_values;
+  for (auto& name : font_family_names)
+    font_family_names_values.push_back(base::Value(name));
+  profile->GetPrefs()->Set(pref_name,
+                           base::Value(std::move(font_family_names_values)));
+}
+
+// FontPrewarmerCoordinator is responsible for coordinating with the renderer
+// to request the fonts used by a page as well as prewarm the last set of fonts
+// used. There is one FontPrewarmerCoordinator per Profile.
+class FontPrewarmerCoordinator : public base::SupportsUserData::Data,
+                                 public content::RenderProcessHostObserver {
+ public:
+  using RemoteFontPrewarmer = mojo::Remote<chrome::mojom::FontPrewarmer>;
+
+  explicit FontPrewarmerCoordinator(Profile* profile) : profile_(profile) {}
+
+  FontPrewarmerCoordinator(const FontPrewarmerCoordinator&) = delete;
+  FontPrewarmerCoordinator& operator=(const FontPrewarmerCoordinator&) = delete;
+
+  ~FontPrewarmerCoordinator() override {
+    for (content::RenderProcessHost* rph : prewarmed_hosts_)
+      rph->RemoveObserver(this);
+  }
+
+  static FontPrewarmerCoordinator& ForProfile(Profile* profile) {
+    FontPrewarmerCoordinator* instance = static_cast<FontPrewarmerCoordinator*>(
+        profile->GetUserData(kUserDataKey));
+    if (!instance) {
+      profile->SetUserData(kUserDataKey,
+                           std::make_unique<FontPrewarmerCoordinator>(profile));
+      instance = static_cast<FontPrewarmerCoordinator*>(
+          profile->GetUserData(kUserDataKey));
+    }
+    return *instance;
+  }
+
+  // Requests the renderer to prewarm the last set of fonts used for displaying
+  // a search page. Prewarming is done at most once per RenderProcessHost.
+  void SendFontsToPrewarm(content::RenderProcessHost* rph) {
+    // Only need to prewarm a particular host once.
+    if (prewarmed_hosts_.count(rph))
+      return;
+
+    // The following code may early out. Insert the entry to ensure an early out
+    // doesn't attempt to send the fonts again.
+    prewarmed_hosts_.insert(rph);
+    rph->AddObserver(this);
+
+    std::vector<std::string> primary_font_names = GetFontNamesFromPrefsForKey(
+        profile_, kSearchResultsPagePrimaryFontsPref);
+    std::vector<std::string> fallback_font_names = GetFontNamesFromPrefsForKey(
+        profile_, kSearchResultsPageFallbackFontsPref);
+    if (primary_font_names.empty() && fallback_font_names.empty())
+      return;
+
+    RemoteFontPrewarmer remote_font_prewarmer;
+    rph->BindReceiver(remote_font_prewarmer.BindNewPipeAndPassReceiver());
+    remote_font_prewarmer->PrewarmFonts(std::move(primary_font_names),
+                                        std::move(fallback_font_names));
+  }
+
+  // Requests the set of fonts needed to display a search page from `rfh`.
+  void RequestFonts(content::RenderFrameHost* rfh) {
+    mojo::AssociatedRemote<chrome::mojom::RenderFrameFontFamilyAccessor>
+        font_family_accessor;
+    rfh->GetRemoteAssociatedInterfaces()->GetInterface(&font_family_accessor);
+    auto* font_family_accessor_raw = font_family_accessor.get();
+    // Pass ownership of the remote to the callback as otherwise the callback
+    // will never be run (because the mojo connection was destroyed).
+    font_family_accessor_raw->GetFontFamilyNames(base::BindOnce(
+        &FontPrewarmerCoordinator::OnGotFontsForFrame,
+        weak_factory_.GetWeakPtr(), std::move(font_family_accessor)));
+  }
+
+ private:
+  void OnGotFontsForFrame(
+      mojo::AssociatedRemote<chrome::mojom::RenderFrameFontFamilyAccessor>
+          font_family_accessor,
+      const std::vector<std::string>& primary_family_names,
+      const std::vector<std::string>& fallback_family_names) {
+    // TODO(sky): add some metrics here so that we know how often the
+    // fonts change.
+    SaveFontNamesToPref(profile_, kSearchResultsPagePrimaryFontsPref,
+                        primary_family_names);
+    SaveFontNamesToPref(profile_, kSearchResultsPageFallbackFontsPref,
+                        fallback_family_names);
+  }
+
+  // content::RenderProcessHostObserver:
+  void RenderProcessHostDestroyed(content::RenderProcessHost* host) override {
+    host->RemoveObserver(this);
+    prewarmed_hosts_.erase(host);
+  }
+
+  Profile* profile_;
+  // Set of hosts that were requested to be prewarmed.
+  std::set<content::RenderProcessHost*> prewarmed_hosts_;
+  base::WeakPtrFactory<FontPrewarmerCoordinator> weak_factory_{this};
+};
+
+}  // namespace
+
+// static
+void FontPrewarmerTabHelper::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  registry->RegisterListPref(kSearchResultsPagePrimaryFontsPref);
+  registry->RegisterListPref(kSearchResultsPageFallbackFontsPref);
+}
+
+FontPrewarmerTabHelper::~FontPrewarmerTabHelper() = default;
+
+FontPrewarmerTabHelper::FontPrewarmerTabHelper(
+    content::WebContents* web_contents)
+    : content::WebContentsObserver(web_contents),
+      content::WebContentsUserData<FontPrewarmerTabHelper>(*web_contents) {}
+
+// static
+std::string FontPrewarmerTabHelper::GetSearchResultsPagePrimaryFontsPref() {
+  return kSearchResultsPagePrimaryFontsPref;
+}
+
+// static
+std::vector<std::string> FontPrewarmerTabHelper::GetPrimaryFontNames(
+    Profile* profile) {
+  return GetFontNamesFromPrefsForKey(profile,
+                                     kSearchResultsPagePrimaryFontsPref);
+}
+
+Profile* FontPrewarmerTabHelper::GetProfile() {
+  return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+}
+
+bool FontPrewarmerTabHelper::IsSearchResultsPageNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->IsInPrimaryMainFrame())
+    return false;
+
+  TemplateURLService* template_url_service =
+      TemplateURLServiceFactory::GetForProfile(GetProfile());
+  return template_url_service &&
+         template_url_service->IsSearchResultsPageFromDefaultSearchProvider(
+             navigation_handle->GetURL());
+}
+
+void FontPrewarmerTabHelper::ReadyToCommitNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (!IsSearchResultsPageNavigation(navigation_handle))
+    return;
+
+  content::RenderFrameHost* rfh = navigation_handle->GetRenderFrameHost();
+  DCHECK(rfh);
+  FontPrewarmerCoordinator& coordinator =
+      FontPrewarmerCoordinator::ForProfile(GetProfile());
+  coordinator.SendFontsToPrewarm(rfh->GetProcess());
+  coordinator.RequestFonts(rfh);
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(FontPrewarmerTabHelper);
diff --git a/chrome/browser/font_prewarmer_tab_helper.h b/chrome/browser/font_prewarmer_tab_helper.h
new file mode 100644
index 0000000..ec0147c
--- /dev/null
+++ b/chrome/browser/font_prewarmer_tab_helper.h
@@ -0,0 +1,58 @@
+// 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 CHROME_BROWSER_FONT_PREWARMER_TAB_HELPER_H_
+#define CHROME_BROWSER_FONT_PREWARMER_TAB_HELPER_H_
+
+#include <string>
+#include <vector>
+
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+class Profile;
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+// FontPrewarmerTabHelper is responsible for tracking navigations to the search
+// results page of the default search engine and prewarming the fonts that were
+// previously used the last time a search results page of the default search
+// engine was visited.
+class FontPrewarmerTabHelper
+    : public content::WebContentsObserver,
+      public content::WebContentsUserData<FontPrewarmerTabHelper> {
+ public:
+  FontPrewarmerTabHelper(const FontPrewarmerTabHelper&) = delete;
+  FontPrewarmerTabHelper& operator=(const FontPrewarmerTabHelper&) = delete;
+  ~FontPrewarmerTabHelper() override;
+
+  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ private:
+  friend class content::WebContentsUserData<FontPrewarmerTabHelper>;
+  friend class FontPrewarmerTabHelperTest;
+
+  explicit FontPrewarmerTabHelper(content::WebContents* web_contents);
+
+  // Testing helpers:
+  static std::string GetSearchResultsPagePrimaryFontsPref();
+  static std::vector<std::string> GetPrimaryFontNames(Profile* profile);
+
+  Profile* GetProfile();
+
+  // Returns true if the url of `navigation_handle` is a search results page of
+  // the default search provider.
+  bool IsSearchResultsPageNavigation(
+      content::NavigationHandle* navigation_handle);
+
+  // content::WebContentsObserver implementation.
+  void ReadyToCommitNavigation(
+      content::NavigationHandle* navigation_handle) override;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+#endif  // CHROME_BROWSER_FONT_PREWARMER_TAB_HELPER_H_
diff --git a/chrome/browser/font_prewarmer_tab_helper_browsertest.cc b/chrome/browser/font_prewarmer_tab_helper_browsertest.cc
new file mode 100644
index 0000000..d4772c4
--- /dev/null
+++ b/chrome/browser/font_prewarmer_tab_helper_browsertest.cc
@@ -0,0 +1,129 @@
+// 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 "chrome/browser/font_prewarmer_tab_helper.h"
+
+#include "base/command_line.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/browser_features.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/google/core/common/google_switches.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/search_engines/template_url.h"
+#include "components/search_engines/template_url_service.h"
+#include "components/search_engines/testing_search_terms_data.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/controllable_http_response.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "ui/base/window_open_disposition.h"
+
+class FontPrewarmerTabHelperTest : public InProcessBrowserTest {
+ public:
+  FontPrewarmerTabHelperTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kPrewarmSearchResultsPageFonts);
+  }
+
+  TemplateURLService* LoadTemplateUrlService() {
+    TemplateURLService* service =
+        TemplateURLServiceFactory::GetInstance()->GetForProfile(
+            browser()->profile());
+    if (service->loaded())
+      return service;
+    base::RunLoop run_loop;
+    base::CallbackListSubscription subscription =
+        service->RegisterOnLoadedCallback(
+            base::BindLambdaForTesting([&]() { run_loop.Quit(); }));
+    service->Load();
+    run_loop.Run();
+    return service;
+  }
+
+  // BrowserTest:
+  void SetUp() override { InProcessBrowserTest::SetUp(); }
+
+  void SetUpOnMainThread() override {
+    // Setup the server to allow serving separate sites, so we can perform
+    // cross-process navigation.
+    host_resolver()->AddRule("*", "127.0.0.1");
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
+    https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
+    https_server_.RegisterRequestHandler(base::BindRepeating(
+        &FontPrewarmerTabHelperTest::OnHandleRequest, base::Unretained(this)));
+
+    // net::test_server::RegisterDefaultHandlers(&https_server_);
+    // HTTPS server only serves a valid cert for localhost, so this is needed to
+    // load pages from "www.google.com" without an interstitial.
+    command_line->AppendSwitch("ignore-certificate-errors");
+    command_line->AppendSwitchASCII("host-rules", "MAP * 127.0.0.1");
+    // Needed for explicit ports to work (which embedded test uses).
+    command_line->AppendSwitch(switches::kIgnoreGooglePortNumbers);
+    ASSERT_TRUE(https_server_.Start());
+    // Change the google url so that the default search engine picks up the
+    // port used by the test server.
+    command_line->AppendSwitchASCII(
+        switches::kGoogleBaseURL,
+        https_server_.GetURL("www.google.com", "/").spec().c_str());
+    InProcessBrowserTest::SetUpCommandLine(command_line);
+  }
+
+ protected:
+  std::string GetSearchResultsPagePrimaryFontsPref() {
+    return FontPrewarmerTabHelper::GetSearchResultsPagePrimaryFontsPref();
+  }
+
+  std::vector<std::string> GetPrimaryFontNames() {
+    return FontPrewarmerTabHelper::GetPrimaryFontNames(browser()->profile());
+  }
+
+  std::unique_ptr<net::test_server::HttpResponse> OnHandleRequest(
+      const net::test_server::HttpRequest& request) {
+    std::unique_ptr<net::test_server::BasicHttpResponse> response =
+        std::make_unique<net::test_server::BasicHttpResponse>();
+    response->set_content_type("text/html");
+    response->set_code(net::HTTP_OK);
+    response->set_content("<html><body style='font-family:Arial'>Hello");
+    return response;
+  }
+
+  net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(FontPrewarmerTabHelperTest, Basic) {
+  TemplateURLService* service = LoadTemplateUrlService();
+  ASSERT_TRUE(service);
+  const GURL search_results_page_url =
+      service->GetDefaultSearchProvider()->GenerateSearchURL(
+          UIThreadSearchTermsData());
+  ASSERT_TRUE(!search_results_page_url.is_empty());
+  NavigateParams params(browser(), search_results_page_url,
+                        ui::PAGE_TRANSITION_LINK);
+  base::RunLoop run_loop;
+  PrefChangeRegistrar prefs_registrar;
+  prefs_registrar.Init(browser()->profile()->GetPrefs());
+  prefs_registrar.Add(GetSearchResultsPagePrimaryFontsPref(),
+                      base::BindLambdaForTesting([&]() { run_loop.Quit(); }));
+  Navigate(&params);
+  run_loop.Run();
+  auto font_names = GetPrimaryFontNames();
+  std::vector<std::string> expected = {"Arial"};
+  EXPECT_EQ(expected, font_names);
+}
diff --git a/chrome/browser/lacros/browser_service_lacros.cc b/chrome/browser/lacros/browser_service_lacros.cc
index c5f586b..43d7e7d 100644
--- a/chrome/browser/lacros/browser_service_lacros.cc
+++ b/chrome/browser/lacros/browser_service_lacros.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/lacros/system_logs/lacros_system_log_fetcher.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/sessions/session_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -24,6 +25,7 @@
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/profile_picker.h"
 #include "chrome/browser/ui/views/tabs/tab_scrubber_chromeos.h"
 #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_util.h"
 #include "chrome/common/channel_info.h"
@@ -135,10 +137,24 @@
   // TODO(crbug.com/1102815): Find what profile should be used.
   Profile* profile = ProfileManager::GetLastUsedProfileAllowedByPolicy();
   DCHECK(profile) << "No last used profile is found.";
-  chrome::NewEmptyWindow(
-      incognito ? profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)
-                : profile,
-      should_trigger_session_restore);
+
+  bool session_restore_available = false;
+  if (should_trigger_session_restore) {
+    SessionService* sessionService =
+        SessionServiceFactory::GetForProfileForSessionRestore(profile);
+    if (sessionService && sessionService->ShouldRestore(nullptr))
+      session_restore_available = true;
+  }
+
+  if (ProfilePicker::ShouldShowAtLaunch() && !session_restore_available) {
+    ProfilePicker::Show(
+        ProfilePicker::EntryPoint::kNewSessionOnExistingProcess);
+  } else {
+    chrome::NewEmptyWindow(
+        incognito ? profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)
+                  : profile,
+        should_trigger_session_restore);
+  }
   std::move(callback).Run();
 }
 
diff --git a/chrome/browser/lacros/browser_service_lacros_browsertest.cc b/chrome/browser/lacros/browser_service_lacros_browsertest.cc
index 0c355c7..e69fa9d0 100644
--- a/chrome/browser/lacros/browser_service_lacros_browsertest.cc
+++ b/chrome/browser/lacros/browser_service_lacros_browsertest.cc
@@ -6,9 +6,13 @@
 #include "chrome/browser/chromeos/app_mode/app_session.h"
 #include "chrome/browser/lacros/app_mode/kiosk_session_service_lacros.h"
 #include "chrome/browser/lacros/browser_service_lacros.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/profile_picker.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom-test-utils.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"
@@ -119,3 +123,28 @@
   CreateNewWindow();
   EXPECT_EQ(BrowserList::GetInstance()->size(), browser_count + 1);
 }
+
+IN_PROC_BROWSER_TEST_F(BrowserServiceLacrosBrowserTest,
+                       ProfilePickerOpensOnStartup) {
+  // Create an additional profile.
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  ProfileAttributesStorage& storage =
+      profile_manager->GetProfileAttributesStorage();
+  base::FilePath path_profile2 =
+      profile_manager->user_data_dir().Append(FILE_PATH_LITERAL("Profile 2"));
+
+  base::RunLoop run_loop;
+  profile_manager->CreateProfileAsync(
+      path_profile2, base::BindLambdaForTesting(
+                         [&](Profile* profile, Profile::CreateStatus status) {
+                           run_loop.Quit();
+                         }));
+  run_loop.Run();
+  ASSERT_EQ(2u, storage.GetNumberOfProfiles());
+  storage.GetProfileAttributesWithPath(path_profile2)->SetActiveTimeToNow();
+
+  browser_service()->NewWindow(
+      /*incognito=*/false, /*should_trigger_session_restore=*/false,
+      /*callback=*/base::BindLambdaForTesting([]() {}));
+  EXPECT_TRUE(ProfilePicker::IsOpen());
+}
diff --git a/chrome/browser/login_detection/login_detection_prefs.cc b/chrome/browser/login_detection/login_detection_prefs.cc
index f9fe47b..49ffe5d 100644
--- a/chrome/browser/login_detection/login_detection_prefs.cc
+++ b/chrome/browser/login_detection/login_detection_prefs.cc
@@ -72,7 +72,7 @@
 
 bool IsSiteInOAuthSignedInList(PrefService* pref_service, const GURL& url) {
   if (auto* dict = pref_service->GetDictionary(kOAuthSignedInSitesPref))
-    return dict->HasKey(GetSiteNameForURL(url));
+    return dict->FindKey(GetSiteNameForURL(url));
   return false;
 }
 
diff --git a/chrome/browser/login_detection/password_store_sites.cc b/chrome/browser/login_detection/password_store_sites.cc
index 912c533..35527c91 100644
--- a/chrome/browser/login_detection/password_store_sites.cc
+++ b/chrome/browser/login_detection/password_store_sites.cc
@@ -16,10 +16,9 @@
     password_manager::PasswordStoreInterface* password_store)
     : password_store_(password_store) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (password_store_) {
-    password_store_->AddObserver(this);
-    password_store_->GetAllLogins(weak_ptr_factory_.GetWeakPtr());
-  }
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&PasswordStoreSites::DoDeferredInitialization,
+                                weak_ptr_factory_.GetWeakPtr()));
 }
 
 PasswordStoreSites::~PasswordStoreSites() {
@@ -28,6 +27,14 @@
     password_store_->RemoveObserver(this);
 }
 
+void PasswordStoreSites::DoDeferredInitialization() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (password_store_) {
+    password_store_->AddObserver(this);
+    password_store_->GetAllLogins(weak_ptr_factory_.GetWeakPtr());
+  }
+}
+
 void PasswordStoreSites::OnLoginsChanged(
     password_manager::PasswordStoreInterface* /*store*/,
     const password_manager::PasswordStoreChangeList& /*changes*/) {
diff --git a/chrome/browser/login_detection/password_store_sites.h b/chrome/browser/login_detection/password_store_sites.h
index 3e039be..0260216 100644
--- a/chrome/browser/login_detection/password_store_sites.h
+++ b/chrome/browser/login_detection/password_store_sites.h
@@ -32,6 +32,10 @@
   bool IsSiteInPasswordStore(const GURL& url) const;
 
  private:
+  // Reads all logins from the password store and starts observing the store for
+  //  future changes.
+  void DoDeferredInitialization();
+
   // PasswordStoreInterface::Observer:
   void OnLoginsChanged(
       password_manager::PasswordStoreInterface* store,
diff --git a/chrome/browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc b/chrome/browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc
index bfa2fc6..86c16e1 100644
--- a/chrome/browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc
+++ b/chrome/browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc
@@ -105,7 +105,8 @@
   }
 
   const base::DictionaryValue* GetDictionary(const std::string& path) const {
-    return profile_->GetTestingPrefService()->GetDictionary(path);
+    return &base::Value::AsDictionaryValue(
+        *profile_->GetTestingPrefService()->GetDictionary(path));
   }
 
   // On devices that support per-application provisioning pre-provisioning
diff --git a/chrome/browser/media/cast_mirroring_service_host_browsertest.cc b/chrome/browser/media/cast_mirroring_service_host_browsertest.cc
index a130535..4667c03 100644
--- a/chrome/browser/media/cast_mirroring_service_host_browsertest.cc
+++ b/chrome/browser/media/cast_mirroring_service_host_browsertest.cc
@@ -82,7 +82,8 @@
   MOCK_METHOD1(OnBufferCreatedCall, void(int buffer_id));
   MOCK_METHOD1(OnBufferReadyCall, void(int buffer_id));
   MOCK_METHOD1(OnBufferDestroyedCall, void(int buffer_id));
-  MOCK_METHOD1(OnStateChanged, void(media::mojom::VideoCaptureState state));
+  MOCK_METHOD1(OnStateChangedCall, void(media::mojom::VideoCaptureState state));
+  MOCK_METHOD1(OnVideoCaptureErrorCall, void(media::VideoCaptureError error));
 
   // media::mojom::VideoCaptureObserver implementation.
   void OnNewBuffer(int32_t buffer_id,
@@ -112,6 +113,13 @@
     OnBufferDestroyedCall(buffer_id);
   }
 
+  void OnStateChanged(media::mojom::VideoCaptureResultPtr result) override {
+    if (result->which() == media::mojom::VideoCaptureResult::Tag::STATE)
+      OnStateChangedCall(result->get_state());
+    else
+      OnVideoCaptureErrorCall(result->get_error_code());
+  }
+
   void Start() {
     host_->Start(device_id_, session_id_, DefaultVideoCaptureParams(),
                  receiver_.BindNewPipeAndPassRemote());
@@ -177,7 +185,7 @@
   void StartVideoCapturing() {
     base::RunLoop run_loop;
     EXPECT_CALL(*video_frame_receiver_,
-                OnStateChanged(media::mojom::VideoCaptureState::STARTED))
+                OnStateChangedCall(media::mojom::VideoCaptureState::STARTED))
         .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
     video_frame_receiver_->Start();
     run_loop.Run();
@@ -187,7 +195,7 @@
     if (video_frame_receiver_) {
       base::RunLoop run_loop;
       EXPECT_CALL(*video_frame_receiver_,
-                  OnStateChanged(media::mojom::VideoCaptureState::ENDED))
+                  OnStateChangedCall(media::mojom::VideoCaptureState::ENDED))
           .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
       video_frame_receiver_->Stop();
       run_loop.Run();
diff --git a/chrome/browser/media/cdm_pref_service_helper.cc b/chrome/browser/media/cdm_pref_service_helper.cc
index f6ffcc2..00c1e65 100644
--- a/chrome/browser/media/cdm_pref_service_helper.cc
+++ b/chrome/browser/media/cdm_pref_service_helper.cc
@@ -222,7 +222,7 @@
   // Access to the PrefService must be made from the UI thread.
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       user_prefs->GetDictionary(prefs::kMediaCdmOriginData);
 
   DCHECK(!cdm_origin.opaque());
@@ -296,7 +296,7 @@
 std::map<std::string, url::Origin> CdmPrefServiceHelper::GetOriginIdMapping(
     PrefService* user_prefs) {
   std::map<std::string, url::Origin> mapping;
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       user_prefs->GetDictionary(prefs::kMediaCdmOriginData);
 
   for (auto key_value : dict->DictItems()) {
diff --git a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
index 34cc6b1..ac53c1b 100644
--- a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
+++ b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
@@ -835,7 +835,7 @@
 
   // Setting the pref in OffTheRecord shouldn't set it for the regular
   // profile.
-  const base::ListValue* non_off_the_record_origins =
+  const base::Value* non_off_the_record_origins =
       profile()->GetPrefs()->GetList(prefs::kMediaRouterTabMirroringSources);
   EXPECT_FALSE(base::Contains(non_off_the_record_origins->GetList(),
                               base::Value(origin)));
diff --git a/chrome/browser/media/router/providers/cast/cast_media_controller.cc b/chrome/browser/media/router/providers/cast/cast_media_controller.cc
index 7d3c12c..a681787d 100644
--- a/chrome/browser/media/router/providers/cast/cast_media_controller.cc
+++ b/chrome/browser/media/router/providers/cast/cast_media_controller.cc
@@ -27,29 +27,30 @@
   if (value && value->is_string())
     *out = value->GetString();
 }
-void SetIfValid(float* out, const base::Value* value) {
-  if (!value)
-    return;
-  if (value->is_double()) {
-    *out = value->GetDouble();
-  } else if (value->is_int()) {
-    *out = value->GetInt();
-  }
-}
-void SetIfValid(int* out, const base::Value* value) {
-  if (value && value->is_int())
-    *out = value->GetInt();
-}
 void SetIfValid(bool* out, const base::Value* value) {
   if (value && value->is_bool())
     *out = value->GetBool();
 }
-void SetIfValid(base::TimeDelta* out, const base::Value* value) {
+
+void SetIfNonNegative(float* out, const base::Value* value) {
   if (!value)
     return;
-  if (value->is_double()) {
+  if (value->is_double() && value->GetDouble() >= 0) {
+    *out = value->GetDouble();
+  } else if (value->is_int() && value->GetInt() >= 0) {
+    *out = value->GetInt();
+  }
+}
+void SetIfNonNegative(int* out, const base::Value* value) {
+  if (value && value->is_int() && value->GetInt() >= 0)
+    *out = value->GetInt();
+}
+void SetIfNonNegative(base::TimeDelta* out, const base::Value* value) {
+  if (!value)
+    return;
+  if (value->is_double() && value->GetDouble() >= 0) {
     *out = base::Seconds(value->GetDouble());
-  } else if (value->is_int()) {
+  } else if (value->is_int() && value->GetInt() >= 0) {
     *out = base::Seconds(value->GetInt());
   }
 }
@@ -61,8 +62,8 @@
     return absl::nullopt;
   int width = 0;
   int height = 0;
-  SetIfValid(&width, value->FindPath("width"));
-  SetIfValid(&height, value->FindPath("height"));
+  SetIfNonNegative(&width, value->FindPath("width"));
+  SetIfNonNegative(&height, value->FindPath("height"));
   if (width <= 0 || height <= 0)
     return absl::nullopt;
   return absl::make_optional<gfx::Size>(width, height);
@@ -155,7 +156,7 @@
   const base::Value* volume = session.value().FindPath("receiver.volume");
   if (!volume || !volume->is_dict())
     return;
-  SetIfValid(&media_status_.volume, volume->FindKey("level"));
+  SetIfNonNegative(&media_status_.volume, volume->FindKey("level"));
   SetIfValid(&media_status_.is_muted, volume->FindKey("muted"));
   const base::Value* volume_type = volume->FindKey("controlType");
   if (volume_type && volume_type->is_string()) {
@@ -205,13 +206,15 @@
   const base::Value& status_value = status_list[0];
   if (!status_value.is_dict())
     return;
-  SetIfValid(&media_session_id_, status_value.FindKey("mediaSessionId"));
+  SetIfNonNegative(&media_session_id_, status_value.FindKey("mediaSessionId"));
   SetIfValid(&media_status_.title,
              status_value.FindPath("media.metadata.title"));
   SetIfValid(&media_status_.secondary_title,
              status_value.FindPath("media.metadata.subtitle"));
-  SetIfValid(&media_status_.current_time, status_value.FindKey("currentTime"));
-  SetIfValid(&media_status_.duration, status_value.FindPath("media.duration"));
+  SetIfNonNegative(&media_status_.current_time,
+                   status_value.FindKey("currentTime"));
+  SetIfNonNegative(&media_status_.duration,
+                   status_value.FindPath("media.duration"));
 
   const base::Value* images = status_value.FindPath("media.metadata.images");
   if (images && images->is_list()) {
diff --git a/chrome/browser/media/router/providers/cast/cast_media_controller_unittest.cc b/chrome/browser/media/router/providers/cast/cast_media_controller_unittest.cc
index a3a8aca..9a020e2 100644
--- a/chrome/browser/media/router/providers/cast/cast_media_controller_unittest.cc
+++ b/chrome/browser/media/router/providers/cast/cast_media_controller_unittest.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/json/json_reader.h"
+#include "base/time/time.h"
 #include "chrome/browser/media/router/providers/cast/app_activity.h"
 #include "chrome/browser/media/router/providers/cast/mock_app_activity.h"
 #include "chrome/browser/media/router/test/media_router_mojo_test.h"
@@ -29,6 +30,7 @@
 
 namespace {
 
+constexpr char kMediaTitle[] = "media title";
 constexpr char kSessionId[] = "sessionId123";
 constexpr int kMediaSessionId = 12345678;
 
@@ -113,7 +115,7 @@
 
 mojom::MediaStatusPtr CreateSampleMediaStatus() {
   mojom::MediaStatusPtr status = mojom::MediaStatus::New();
-  status->title = "media title";
+  status->title = kMediaTitle;
   status->can_play_pause = true;
   status->can_mute = true;
   status->can_set_volume = false;
@@ -337,6 +339,24 @@
   VerifyAndClearExpectations();
 }
 
+TEST_F(CastMediaControllerTest, IgnoreInvalidUpdate) {
+  Value invalid_status = CreateMediaStatus(*CreateSampleMediaStatus());
+  invalid_status.SetIntPath("media.duration", -100);
+  invalid_status.SetIntPath("currentTime", -100);
+
+  EXPECT_CALL(*status_observer_, OnMediaStatusUpdated(_))
+      .WillOnce([&](mojom::MediaStatusPtr status) {
+        // Valid fields are copied over.
+        EXPECT_EQ(kMediaTitle, status->title);
+        // Invalid fields (negative durations) are ignored, and the default
+        // value of zero is used.
+        EXPECT_EQ(base::Seconds(0), status->duration);
+        EXPECT_EQ(base::Seconds(0), status->current_time);
+      });
+  SetMediaStatus(std::move(invalid_status));
+  VerifyAndClearExpectations();
+}
+
 TEST_F(CastMediaControllerTest, UpdateMediaImages) {
   mojom::MediaStatusPtr expected_status = CreateSampleMediaStatus();
   expected_status->images.emplace_back(
diff --git a/chrome/browser/media/webrtc/media_stream_device_permissions.cc b/chrome/browser/media/webrtc/media_stream_device_permissions.cc
index feb020e..e73e10e 100644
--- a/chrome/browser/media/webrtc/media_stream_device_permissions.cc
+++ b/chrome/browser/media/webrtc/media_stream_device_permissions.cc
@@ -27,7 +27,7 @@
 
   const PrefService* prefs = profile->GetPrefs();
 
-  const base::ListValue* list = prefs->GetList(allowed_urls_pref_name);
+  const base::Value* list = prefs->GetList(allowed_urls_pref_name);
   for (const base::Value& i : list->GetList()) {
     const std::string* value = i.GetIfString();
     if (value) {
diff --git a/chrome/browser/media_galleries/media_galleries_preferences.cc b/chrome/browser/media_galleries/media_galleries_preferences.cc
index 591d926..0c0ebc1 100644
--- a/chrome/browser/media_galleries/media_galleries_preferences.cc
+++ b/chrome/browser/media_galleries/media_galleries_preferences.cc
@@ -573,8 +573,8 @@
   device_map_.clear();
 
   PrefService* prefs = profile_->GetPrefs();
-  const base::ListValue* list = prefs->GetList(
-      prefs::kMediaGalleriesRememberedGalleries);
+  const base::Value* list =
+      prefs->GetList(prefs::kMediaGalleriesRememberedGalleries);
   if (list) {
     for (const auto& gallery_value : list->GetList()) {
       if (!gallery_value.is_dict())
@@ -1035,7 +1035,8 @@
              MediaGalleryPrefInfo::kAutoDetected);
   ExtensionPrefs* prefs = GetExtensionPrefs();
   const base::DictionaryValue* extensions =
-      prefs->pref_service()->GetDictionary(extensions::pref_names::kExtensions);
+      &base::Value::AsDictionaryValue(*prefs->pref_service()->GetDictionary(
+          extensions::pref_names::kExtensions));
   if (!extensions)
     return true;
 
@@ -1254,7 +1255,8 @@
   DCHECK(IsInitialized());
   ExtensionPrefs* prefs = GetExtensionPrefs();
   const base::DictionaryValue* extensions =
-      prefs->pref_service()->GetDictionary(extensions::pref_names::kExtensions);
+      &base::Value::AsDictionaryValue(*prefs->pref_service()->GetDictionary(
+          extensions::pref_names::kExtensions));
   if (!extensions)
     return;
 
diff --git a/chrome/browser/metrics/plugin_metrics_provider.cc b/chrome/browser/metrics/plugin_metrics_provider.cc
index 469658d..865240b 100644
--- a/chrome/browser/metrics/plugin_metrics_provider.cc
+++ b/chrome/browser/metrics/plugin_metrics_provider.cc
@@ -134,8 +134,8 @@
 void PluginMetricsProvider::ProvideStabilityMetrics(
     metrics::SystemProfileProto* system_profile_proto) {
   RecordCurrentStateIfPending();
-  const base::ListValue* plugin_stats_list = local_state_->GetList(
-      prefs::kStabilityPluginStats);
+  const base::Value* plugin_stats_list =
+      local_state_->GetList(prefs::kStabilityPluginStats);
   if (!plugin_stats_list)
     return;
 
diff --git a/chrome/browser/metrics/usertype_by_devicetype_metrics_provider_browsertest.cc b/chrome/browser/metrics/usertype_by_devicetype_metrics_provider_browsertest.cc
index 70c67c4..aec8e4f 100644
--- a/chrome/browser/metrics/usertype_by_devicetype_metrics_provider_browsertest.cc
+++ b/chrome/browser/metrics/usertype_by_devicetype_metrics_provider_browsertest.cc
@@ -323,8 +323,8 @@
     auto* controller = ash::ExistingUserController::current_controller();
     ASSERT_TRUE(controller);
 
-    chromeos::UserContext user_context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
-                                       account_id_1_);
+    ash::UserContext user_context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
+                                  account_id_1_);
     user_context.SetPublicSessionLocale(std::string());
     user_context.SetPublicSessionInputMethod(std::string());
     controller->Login(user_context, ash::SigninSpecifics());
diff --git a/chrome/browser/nearby_sharing/nearby_share_settings.cc b/chrome/browser/nearby_sharing/nearby_share_settings.cc
index c14d73a..bc1a2b06c 100644
--- a/chrome/browser/nearby_sharing/nearby_share_settings.cc
+++ b/chrome/browser/nearby_sharing/nearby_share_settings.cc
@@ -98,7 +98,7 @@
 
 const std::vector<std::string> NearbyShareSettings::GetAllowedContacts() const {
   std::vector<std::string> allowed_contacts;
-  const base::ListValue* list =
+  const base::Value* list =
       pref_service_->GetList(prefs::kNearbySharingAllowedContactsPrefName);
   if (list) {
     base::Value::ConstListView view = list->GetList();
diff --git a/chrome/browser/net/convert_explicitly_allowed_network_ports_pref.cc b/chrome/browser/net/convert_explicitly_allowed_network_ports_pref.cc
index adeb603d..313968af 100644
--- a/chrome/browser/net/convert_explicitly_allowed_network_ports_pref.cc
+++ b/chrome/browser/net/convert_explicitly_allowed_network_ports_pref.cc
@@ -15,7 +15,7 @@
 std::vector<uint16_t> ConvertExplicitlyAllowedNetworkPortsPref(
     PrefService* local_state) {
   std::vector<uint16_t> explicitly_allowed_network_ports;
-  const base::ListValue* explicitly_allowed_network_ports_list_value =
+  const base::Value* explicitly_allowed_network_ports_list_value =
       local_state->GetList(prefs::kExplicitlyAllowedNetworkPorts);
   DCHECK(explicitly_allowed_network_ports_list_value);
   auto list_view = explicitly_allowed_network_ports_list_value->GetList();
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index 598fb5e2..e0a3e473 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -110,7 +110,10 @@
 const char kHttpCacheFinchExperimentGroups[] =
     "profile_network_context_service.http_cache_finch_experiment_groups";
 
-std::vector<std::string> TranslateStringArray(const base::ListValue* list) {
+std::vector<std::string> TranslateStringArray(const base::Value* list) {
+  if (!list->is_list())
+    return std::vector<std::string>();
+
   std::vector<std::string> strings;
   for (const base::Value& value : list->GetList()) {
     DCHECK(value.is_string());
@@ -397,13 +400,13 @@
 
 network::mojom::CTPolicyPtr ProfileNetworkContextService::GetCTPolicy() {
   auto* prefs = profile_->GetPrefs();
-  const base::ListValue* ct_required =
+  const base::Value* ct_required =
       prefs->GetList(certificate_transparency::prefs::kCTRequiredHosts);
-  const base::ListValue* ct_excluded =
+  const base::Value* ct_excluded =
       prefs->GetList(certificate_transparency::prefs::kCTExcludedHosts);
-  const base::ListValue* ct_excluded_spkis =
+  const base::Value* ct_excluded_spkis =
       prefs->GetList(certificate_transparency::prefs::kCTExcludedSPKIs);
-  const base::ListValue* ct_excluded_legacy_spkis =
+  const base::Value* ct_excluded_legacy_spkis =
       prefs->GetList(certificate_transparency::prefs::kCTExcludedLegacySPKIs);
 
   std::vector<std::string> required(TranslateStringArray(ct_required));
@@ -745,7 +748,7 @@
     network_context_params->file_paths->transport_security_persister_file_name =
         base::FilePath(chrome::kTransportSecurityPersisterFilename);
   }
-  const base::ListValue* hsts_policy_bypass_list =
+  const base::Value* hsts_policy_bypass_list =
       g_browser_process->local_state()->GetList(prefs::kHSTSPolicyBypassList);
   for (const auto& value : hsts_policy_bypass_list->GetList()) {
     const std::string* string_value = value.GetIfString();
diff --git a/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc b/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc
index 05ce1d4..1a5137b 100644
--- a/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc
+++ b/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc
@@ -400,7 +400,7 @@
 bool TaskModuleService::IsTaskDismissed(
     task_module::mojom::TaskModuleType task_module_type,
     const std::string& task_name) {
-  const base::ListValue* dismissed_tasks = profile_->GetPrefs()->GetList(
+  const base::Value* dismissed_tasks = profile_->GetPrefs()->GetList(
       GetDismissedTasksPrefName(task_module_type));
   DCHECK(dismissed_tasks);
   return base::Contains(dismissed_tasks->GetList(), base::Value(task_name));
diff --git a/chrome/browser/new_tab_page/promos/promo_service_unittest.cc b/chrome/browser/new_tab_page/promos/promo_service_unittest.cc
index 1ddc789b..ef3866e 100644
--- a/chrome/browser/new_tab_page/promos/promo_service_unittest.cc
+++ b/chrome/browser/new_tab_page/promos/promo_service_unittest.cc
@@ -256,7 +256,7 @@
 
   const auto* blocklist = prefs()->GetDictionary(prefs::kNtpPromoBlocklist);
   ASSERT_EQ(1u, blocklist->DictSize());
-  ASSERT_TRUE(blocklist->HasKey("42"));
+  ASSERT_TRUE(blocklist->FindKey("42"));
 }
 
 TEST_F(PromoServiceTest, BlocklistExpiration) {
diff --git a/chrome/browser/notifications/notifier_state_tracker.cc b/chrome/browser/notifications/notifier_state_tracker.cc
index 38ebb6e..6f9c642 100644
--- a/chrome/browser/notifications/notifier_state_tracker.cc
+++ b/chrome/browser/notifications/notifier_state_tracker.cc
@@ -144,7 +144,7 @@
   // Separate GetPrefs()->GetList() to analyze the crash. See crbug.com/322320
   const PrefService* pref_service = profile_->GetPrefs();
   CHECK(pref_service);
-  const base::ListValue* pref_list = pref_service->GetList(pref_name);
+  const base::Value* pref_list = pref_service->GetList(pref_name);
   base::Value::ConstListView pref_list_view = pref_list->GetList();
   for (size_t i = 0; i < pref_list_view.size(); ++i) {
     const std::string* element = pref_list_view[i].GetIfString();
diff --git a/chrome/browser/notifications/stub_notification_display_service.cc b/chrome/browser/notifications/stub_notification_display_service.cc
index f51cd29..ed43855 100644
--- a/chrome/browser/notifications/stub_notification_display_service.cc
+++ b/chrome/browser/notifications/stub_notification_display_service.cc
@@ -254,7 +254,7 @@
 StubNotificationDisplayService::NotificationData::NotificationData(
     NotificationData&& other)
     : type(other.type),
-      notification(other.notification),
+      notification(std::move(other.notification)),
       metadata(std::move(other.metadata)) {}
 
 StubNotificationDisplayService::NotificationData::~NotificationData() {}
@@ -274,7 +274,7 @@
     const std::string& notification_id) {
   return std::find_if(
       notifications_.begin(), notifications_.end(),
-      [notification_type, notification_id](const NotificationData& data) {
+      [notification_type, &notification_id](const NotificationData& data) {
         return data.type == notification_type &&
                data.notification.id() == notification_id;
       });
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.cc b/chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.cc
index 50bde64..274174a 100644
--- a/chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.cc
+++ b/chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.cc
@@ -56,7 +56,8 @@
 
 std::unique_ptr<net::BackoffEntry>
 PrefetchBackgroundTaskHandlerImpl::GetCurrentBackoff() const {
-  const base::ListValue* value = prefs_->GetList(prefetch_prefs::kBackoff);
+  const base::ListValue* value =
+      &base::Value::AsListValue(*prefs_->GetList(prefetch_prefs::kBackoff));
   std::unique_ptr<net::BackoffEntry> result;
   if (value) {
     result = net::BackoffEntrySerializer::DeserializeFromValue(
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
index a928b6bf..9d1aaeee 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -1647,7 +1647,9 @@
 
   // Ensure that the previous page won't be stored in the back/forward cache, so
   // that the histogram will be recorded when the previous page is unloaded.
-  // TODO(https://crbug.com/1229122): Investigate if this needs further fix.
+  // UKM/UMA logging after BFCache eviction is checked by
+  // PageLoadMetricsBrowserTestWithBackForwardCache's
+  // UseCounterUkmFeaturesLoggedOnBFCacheEviction test.
   browser()
       ->tab_strip_model()
       ->GetActiveWebContents()
@@ -1695,7 +1697,9 @@
 
   // Ensure that the previous page won't be stored in the back/forward cache, so
   // that the histogram will be recorded when the previous page is unloaded.
-  // TODO(https://crbug.com/1229122): Investigate if this needs further fix.
+  // UKM/UMA logging after BFCache eviction is checked by
+  // PageLoadMetricsBrowserTestWithBackForwardCache's
+  // UseCounterUkmFeaturesLoggedOnBFCacheEviction test.
   browser()
       ->tab_strip_model()
       ->GetActiveWebContents()
@@ -3358,6 +3362,67 @@
       internal::PageLoadBackForwardCacheEvent::kRestoreFromBackForwardCache, 0);
 }
 
+// Test UseCounter UKM features observed when a page is in the BFCache and is
+// evicted from it.
+IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTestWithBackForwardCache,
+                       UseCounterUkmFeaturesLoggedOnBFCacheEviction) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url = embedded_test_server()->GetURL(
+      "/page_load_metrics/use_counter_features.html");
+  {
+    auto waiter = CreatePageLoadMetricsTestWaiter();
+    waiter->AddPageExpectation(TimingField::kLoadEvent);
+    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+    MakeComponentFullscreen("testvideo");
+    waiter->Wait();
+  }
+  NavigateToUntrackedUrl();
+
+  // Force the BFCache to evict all entries. This should cause the
+  // UseCounter histograms to be logged.
+  browser()
+      ->tab_strip_model()
+      ->GetActiveWebContents()
+      ->GetController()
+      .GetBackForwardCache()
+      .Flush();
+
+  // Navigate to a new URL. This gives the various page load tracking
+  // mechanisms time to process the BFCache evictions.
+  auto url1 = embedded_test_server()->GetURL("a.com", "/title1.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url1));
+
+  const auto& entries = test_ukm_recorder_->GetEntriesByName(
+      ukm::builders::Blink_UseCounter::kEntryName);
+  EXPECT_THAT(entries, SizeIs(4));
+  std::vector<int64_t> ukm_features;
+  for (const auto* entry : entries) {
+    test_ukm_recorder_->ExpectEntryMetric(
+        entry, ukm::builders::Blink_UseCounter::kIsMainFrameFeatureName, 1);
+    const auto* metric = test_ukm_recorder_->GetEntryMetric(
+        entry, ukm::builders::Blink_UseCounter::kFeatureName);
+    DCHECK(metric);
+    ukm_features.push_back(*metric);
+  }
+  EXPECT_THAT(ukm_features,
+              UnorderedElementsAre(
+                  static_cast<int64_t>(WebFeature::kPageVisits),
+                  static_cast<int64_t>(WebFeature::kFullscreenSecureOrigin),
+                  static_cast<int64_t>(WebFeature::kNavigatorVibrate),
+                  static_cast<int64_t>(WebFeature::kPageVisits)));
+
+  // Check histogram counts.
+  histogram_tester_->ExpectBucketCount(
+      internal::kFeaturesHistogramName,
+      static_cast<int32_t>(WebFeature::kPageVisits), 2);
+  histogram_tester_->ExpectBucketCount(
+      internal::kFeaturesHistogramName,
+      static_cast<int32_t>(WebFeature::kFullscreenSecureOrigin), 1);
+  histogram_tester_->ExpectBucketCount(
+      internal::kFeaturesHistogramName,
+      static_cast<int32_t>(WebFeature::kNavigatorVibrate), 1);
+}
+
 class NavigationPageLoadMetricsBrowserTest
     : public PageLoadMetricsBrowserTest,
       public ::testing::WithParamInterface<std::string> {
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java
index 0fad594..10da6a0 100644
--- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java
+++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java
@@ -90,26 +90,30 @@
     /**
      *  Checks whether the sync feature is enabled, the user has chosen to sync passwords and
      *  they haven't set up a custom passphrase.
+     *  The caller should make sure that the sync engine is initialized before calling this method.
+     *
      *  Note that this doesn't mean that passwords are actively syncing.
      *
      * @param syncService the service to query about the sync status.
-     * @return true if syncing passwords is enabled
+     * @return true if syncing passwords is enabled without custom passphrase.
      */
     public static boolean hasChosenToSyncPasswordsWithNoCustomPassphrase(SyncService syncService) {
+        assert syncService.isEngineInitialized();
         return PasswordManagerHelper.hasChosenToSyncPasswords(syncService)
-                && syncService.isEngineInitialized() && !syncService.isUsingExplicitPassphrase();
+                && !syncService.isUsingExplicitPassphrase();
     }
 
     /**
      * Checks whether the user is actively syncing passwords without a custom passphrase.
+     * The caller should make sure that the sync engine is initialized before calling this method.
      *
      * @param syncService the service to query about the sync status.
      * @return true if actively syncing passwords and no custom passphrase was set.
      */
     public static boolean isSyncingPasswordsWithNoCustomPassphrase(SyncService syncService) {
+        assert syncService.isEngineInitialized();
         if (syncService == null || !syncService.hasSyncConsent()) return false;
         if (!syncService.getActiveDataTypes().contains(ModelType.PASSWORDS)) return false;
-        if (!syncService.isEngineInitialized()) return false;
         if (syncService.isUsingExplicitPassphrase()) return false;
         return true;
     }
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
index 67940dc..6860d2f 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
@@ -92,6 +92,7 @@
     public void setUp() {
         ShadowRecordHistogram.reset();
         MockitoAnnotations.initMocks(this);
+        when(mSyncServiceMock.isEngineInitialized()).thenReturn(true);
     }
 
     @Test
@@ -139,18 +140,6 @@
     }
 
     @Test
-    public void testSyncEnabledButInitializing() {
-        when(mSyncServiceMock.isSyncFeatureEnabled()).thenReturn(true);
-        when(mSyncServiceMock.getChosenDataTypes())
-                .thenReturn(CollectionUtil.newHashSet(ModelType.PASSWORDS));
-        when(mSyncServiceMock.isEngineInitialized()).thenReturn(false);
-        Assert.assertTrue(PasswordManagerHelper.hasChosenToSyncPasswords(mSyncServiceMock));
-        Assert.assertFalse(PasswordManagerHelper.hasChosenToSyncPasswordsWithNoCustomPassphrase(
-                mSyncServiceMock));
-        verify(mSyncServiceMock, never()).isUsingExplicitPassphrase();
-    }
-
-    @Test
     public void testActivelySyncingPasswordsWithNoCustomPassphrase() {
         when(mSyncServiceMock.hasSyncConsent()).thenReturn(true);
         when(mSyncServiceMock.getActiveDataTypes())
diff --git a/chrome/browser/password_manager/android/password_store_android_backend.cc b/chrome/browser/password_manager/android/password_store_android_backend.cc
index ab54890..55a5e19 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend.cc
+++ b/chrome/browser/password_manager/android/password_store_android_backend.cc
@@ -354,12 +354,12 @@
 
   // Create and run a callbacks chain that retrieves logins and invokes
   // |barrier_callback| afterwards.
-  base::RepeatingClosure callbacks_chain = base::DoNothing();
+  base::OnceClosure callbacks_chain = base::DoNothing();
   for (const PasswordFormDigest& form : forms) {
-    callbacks_chain = base::BindRepeating(
+    callbacks_chain = base::BindOnce(
         &PasswordStoreAndroidBackend::GetLoginsAsync,
         weak_ptr_factory_.GetWeakPtr(), std::move(form), include_psl,
-        barrier_callback.Then(std::move(callbacks_chain)));
+        base::BindOnce(barrier_callback).Then(std::move(callbacks_chain)));
   }
   std::move(callbacks_chain).Run();
 }
@@ -420,12 +420,12 @@
           base::BindOnce(&JoinPasswordStoreChanges).Then(std::move(reply)));
 
   // Create and run the callback chain that removes the logins.
-  base::RepeatingClosure callbacks_chain = base::DoNothing();
+  base::OnceClosure callbacks_chain = base::DoNothing();
   for (const auto& login : logins_to_remove) {
-    callbacks_chain =
-        base::BindRepeating(&PasswordStoreAndroidBackend::RemoveLoginAsync,
-                            weak_ptr_factory_.GetWeakPtr(), std::move(login),
-                            barrier_callback.Then(std::move(callbacks_chain)));
+    callbacks_chain = base::BindOnce(
+        &PasswordStoreAndroidBackend::RemoveLoginAsync,
+        weak_ptr_factory_.GetWeakPtr(), std::move(login),
+        base::BindOnce(barrier_callback).Then(std::move(callbacks_chain)));
   }
   std::move(callbacks_chain).Run();
 }
@@ -479,7 +479,31 @@
 void PasswordStoreAndroidBackend::DisableAutoSignInForOriginsAsync(
     const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
     base::OnceClosure completion) {
-  // TODO(https://crbug.com/1229655):Implement.
+  // TODO(https://crbug.com/1229655) Switch to using base::PassThrough to handle
+  // this callback more gracefully when it's implemented.
+  PasswordStoreChangeListReply record_metrics_and_run_completion =
+      base::BindOnce(
+          [](MetricsRecorder metrics_recorder, base::OnceClosure completion,
+             absl::optional<PasswordStoreChangeList> changes) {
+            // Errors are not recorded at the moment.
+            // TODO(https://crbug.com/1278807): Implement error handling, when
+            // actual store changes will be received from the store.
+            metrics_recorder.RecordMetrics(/*success=*/true,
+                                           /*error=*/absl::nullopt);
+            std::move(completion).Run();
+          },
+          MetricsRecorder(MetricInfix("DisableAutoSignInForOriginsAsync")),
+          std::move(completion));
+
+  JobId get_logins_job_id =
+      bridge_->GetAllLogins(PasswordStoreOperationTarget::kDefault);
+  QueueNewJob(get_logins_job_id,
+              JobReturnHandler(
+                  base::BindOnce(
+                      &PasswordStoreAndroidBackend::FilterAndDisableAutoSignIn,
+                      weak_ptr_factory_.GetWeakPtr(), origin_filter,
+                      std::move(record_metrics_and_run_completion)),
+                  MetricsRecorder(MetricInfix("GetAllLoginsAsync"))));
 }
 
 SmartBubbleStatsStore* PasswordStoreAndroidBackend::GetSmartBubbleStatsStore() {
@@ -576,6 +600,42 @@
                           MetricsRecorder(MetricInfix("GetLoginsAsync"))));
 }
 
+void PasswordStoreAndroidBackend::FilterAndDisableAutoSignIn(
+    const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
+    PasswordStoreChangeListReply completion,
+    LoginsResultOrError result) {
+  if (absl::holds_alternative<PasswordStoreBackendError>(result)) {
+    std::move(completion).Run({});
+    return;
+  }
+
+  LoginsResult logins = std::move(absl::get<LoginsResult>(result));
+  std::vector<PasswordForm> logins_to_update;
+  for (std::unique_ptr<PasswordForm>& login : logins) {
+    // Update login if it matches |origin_filer| and has autosignin enabled.
+    if (origin_filter.Run(login->url) && !login->skip_zero_click) {
+      logins_to_update.push_back(std::move(*login));
+      logins_to_update.back().skip_zero_click = true;
+    }
+  }
+
+  auto barrier_callback =
+      base::BarrierCallback<absl::optional<PasswordStoreChangeList>>(
+          logins_to_update.size(), base::BindOnce(&JoinPasswordStoreChanges)
+                                       .Then(std::move(completion)));
+
+  // Create and run a callbacks chain that updates the logins.
+  base::OnceClosure callbacks_chain = base::DoNothing();
+  for (PasswordForm& login : logins_to_update) {
+    callbacks_chain = base::BindOnce(
+        &PasswordStoreAndroidBackend::UpdateLoginAsync,
+        weak_ptr_factory_.GetWeakPtr(), std::move(login),
+        base::BindOnce(barrier_callback).Then(std::move(callbacks_chain)));
+  }
+  std::move(callbacks_chain).Run();
+}
+
+// static
 LoginsOrErrorReply
 PasswordStoreAndroidBackend::ReportMetricsAndInvokeCallbackForLoginsRetrieval(
     const MetricInfix& metric_infix,
@@ -595,6 +655,7 @@
       MetricsRecorder(metric_infix), std::move(callback));
 }
 
+// static
 PasswordStoreChangeListReply PasswordStoreAndroidBackend::
     ReportMetricsAndInvokeCallbackForStoreModifications(
         const MetricInfix& metric_infix,
@@ -605,9 +666,9 @@
       [](MetricsRecorder metrics_recorder,
          PasswordStoreChangeListReply callback,
          absl::optional<PasswordStoreChangeList> results) {
-        // Errors are not recorded at the moment. This should be implemented
-        // in the future, when actual store changes will be received from the
-        // store modifying operations.
+        // Errors are not recorded at the moment.
+        // TODO(https://crbug.com/1278807): Implement error handling, when
+        // actual store changes will be received from the store.
         metrics_recorder.RecordMetrics(/*success=*/true,
                                        /*error=*/absl::nullopt);
         std::move(callback).Run(std::move(results));
diff --git a/chrome/browser/password_manager/android/password_store_android_backend.h b/chrome/browser/password_manager/android/password_store_android_backend.h
index 6f3360e..bff952d 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend.h
+++ b/chrome/browser/password_manager/android/password_store_android_backend.h
@@ -81,9 +81,9 @@
 
   using MetricInfix = base::StrongAlias<struct MetricNameTag, std::string>;
 
-  // Records metrics for an asynchronous job or a series of jobs. The job is expected to have
-  // started when the MetricsRecorder instance is created. Latency is reported in RecordMetrics()
-  // under that assumption.
+  // Records metrics for an asynchronous job or a series of jobs. The job is
+  // expected to have started when the MetricsRecorder instance is created.
+  // Latency is reported in RecordMetrics() under that assumption.
   class MetricsRecorder {
    public:
     MetricsRecorder();
@@ -219,17 +219,24 @@
       PasswordStoreChangeListReply reply,
       LoginsResultOrError result);
 
+  // Filters logins that match |origin_filer| and asynchronously disables
+  // autosignin by updating stored logins.
+  void FilterAndDisableAutoSignIn(
+      const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
+      PasswordStoreChangeListReply completion,
+      LoginsResultOrError result);
+
   // Creates a metrics recorder that records latency and success metrics for
   // logins retrieval operation with |metric_infix| name prior to calling
   // |callback|.
-  LoginsOrErrorReply ReportMetricsAndInvokeCallbackForLoginsRetrieval(
+  static LoginsOrErrorReply ReportMetricsAndInvokeCallbackForLoginsRetrieval(
       const MetricInfix& metric_infix,
       LoginsReply callback);
 
   // Creates a metrics recorder that records latency and success metrics for
   // store modification operation with |metric_infix| name prior to
   // calling |callback|.
-  PasswordStoreChangeListReply
+  static PasswordStoreChangeListReply
   ReportMetricsAndInvokeCallbackForStoreModifications(
       const MetricInfix& metric_infix,
       PasswordStoreChangeListReply callback);
diff --git a/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc b/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc
index c20d24c..2f5d31e 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc
+++ b/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc
@@ -75,6 +75,12 @@
   return forms;
 }
 
+PasswordForm FormWithDisabledAutoSignIn(const PasswordForm& form_to_update) {
+  PasswordForm result = form_to_update;
+  result.skip_zero_click = 1;
+  return result;
+}
+
 class MockPasswordStoreAndroidBackendBridge
     : public PasswordStoreAndroidBackendBridge {
  public:
@@ -469,6 +475,86 @@
   histogram_tester.ExpectBucketCount(kAPIErrorMetric, 11004, 1);
 }
 
+TEST_F(PasswordStoreAndroidBackendTest, DisableAutoSignInForOrigins) {
+  base::HistogramTester histogram_tester;
+  constexpr auto kLatencyDelta = base::Milliseconds(123u);
+
+  backend().InitBackend(PasswordStoreAndroidBackend::RemoteChangesReceived(),
+                        base::RepeatingClosure(), base::DoNothing());
+
+  // Check that calling DisableAutoSignInForOrigins triggers logins retrieval
+  // first.
+  const JobId kGetLoginsJobId{13387};
+  EXPECT_CALL(*bridge(), GetAllLogins).WillOnce(Return(kGetLoginsJobId));
+
+  base::RepeatingCallback<bool(const GURL&)> origin_filter =
+      base::BindRepeating(
+          [](const GURL& url) { return url == GURL(kTestUrl); });
+  base::MockCallback<base::OnceClosure> mock_reply;
+  backend().DisableAutoSignInForOriginsAsync(origin_filter, mock_reply.Get());
+
+  // Imitate login retrieval and check that it triggers updating of matching
+  // forms.
+  PasswordForm form_to_update1 =
+      CreateTestLogin(kTestUsername, kTestPassword, kTestUrl, kTestDateCreated);
+  PasswordForm form_to_update2 = CreateTestLogin(
+      u"OtherUsername", u"OtherPassword", kTestUrl, kTestDateCreated);
+  PasswordForm form_with_autosignin_disabled =
+      FormWithDisabledAutoSignIn(form_to_update1);
+  PasswordForm form_with_different_origin =
+      CreateTestLogin(kTestUsername, kTestPassword,
+                      "https://differentorigin.com", kTestDateCreated);
+
+  const JobId kUpdateJobId1{13388};
+  // Forms are updated in reverse order.
+  EXPECT_CALL(*bridge(),
+              UpdateLogin(FormWithDisabledAutoSignIn(form_to_update2)))
+      .WillOnce(Return(kUpdateJobId1));
+
+  consumer().OnCompleteWithLogins(
+      kGetLoginsJobId,
+      {form_to_update1, form_to_update2, form_with_autosignin_disabled,
+       form_with_different_origin});
+  RunUntilIdle();
+
+  // Fast forward to check latency metric recording.
+  task_environment_.FastForwardBy(kLatencyDelta);
+
+  // Receiving callback after updating the first login should trigger
+  // updating of the second login.
+  PasswordStoreChangeList change1;
+  change1.emplace_back(
+      PasswordStoreChange(PasswordStoreChange::UPDATE,
+                          FormWithDisabledAutoSignIn(form_to_update2)));
+  const JobId kUpdateJobId2{13389};
+  EXPECT_CALL(*bridge(),
+              UpdateLogin(FormWithDisabledAutoSignIn(form_to_update1)))
+      .WillOnce(Return(kUpdateJobId2));
+  consumer().OnLoginsChanged(kUpdateJobId1, change1);
+  RunUntilIdle();
+
+  // Verify that the callback is called.
+  EXPECT_CALL(mock_reply, Run());
+  PasswordStoreChangeList change2;
+  change2.emplace_back(
+      PasswordStoreChange(PasswordStoreChange::UPDATE,
+                          FormWithDisabledAutoSignIn(form_to_update1)));
+  consumer().OnLoginsChanged(kUpdateJobId2, change2);
+  RunUntilIdle();
+
+  histogram_tester.ExpectTimeBucketCount(
+      "PasswordManager.PasswordStoreAndroidBackend."
+      "DisableAutoSignInForOriginsAsync."
+      "Latency",
+      kLatencyDelta, 1);
+
+  histogram_tester.ExpectTotalCount(
+      "PasswordManager.PasswordStoreAndroidBackend."
+      "DisableAutoSignInForOriginsAsync."
+      "Success",
+      1);
+}
+
 class PasswordStoreAndroidBackendTestForMetrics
     : public PasswordStoreAndroidBackendTest,
       public testing::WithParamInterface<bool> {
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index fdcb866..3c141c4 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -1651,7 +1651,13 @@
 // static
 constexpr int PDFExtensionKeyEventTest::kScrollIncrement;
 
-IN_PROC_BROWSER_TEST_P(PDFExtensionKeyEventTest, ScrollWithSpace) {
+// crbug.com/1281749
+#if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+#define MAYBE_ScrollWithSpace DISABLED_ScrollWithSpace
+#else
+#define MAYBE_ScrollWithSpace ScrollWithSpace
+#endif
+IN_PROC_BROWSER_TEST_P(PDFExtensionKeyEventTest, MAYBE_ScrollWithSpace) {
   WebContents* guest_contents = LoadPdfGetGuestContents(
       embedded_test_server()->GetURL("/pdf/test-bookmarks.pdf"));
   SetInputFocusOnPlugin(guest_contents);
@@ -1729,7 +1735,14 @@
   EXPECT_EQ(viewport_height, GetViewportScrollPositionY(guest_contents));
 }
 
-IN_PROC_BROWSER_TEST_P(PDFExtensionKeyEventTest, ScrollWithArrowLeftRight) {
+// crbug.com/1281749
+#if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+#define MAYBE_ScrollWithArrowLeftRight DISABLED_ScrollWithArrowLeftRight
+#else
+#define MAYBE_ScrollWithArrowLeftRight ScrollWithArrowLeftRight
+#endif
+IN_PROC_BROWSER_TEST_P(PDFExtensionKeyEventTest,
+                       MAYBE_ScrollWithArrowLeftRight) {
   WebContents* guest_contents = LoadPdfGetGuestContents(
       embedded_test_server()->GetURL("/pdf/test-bookmarks.pdf#zoom=200"));
   SetInputFocusOnPlugin(guest_contents);
diff --git a/chrome/browser/plugins/plugins_resource_service.cc b/chrome/browser/plugins/plugins_resource_service.cc
index dea3c4f..4e3e508 100644
--- a/chrome/browser/plugins/plugins_resource_service.cc
+++ b/chrome/browser/plugins/plugins_resource_service.cc
@@ -90,8 +90,8 @@
           base::BindOnce(&content::GetNetworkConnectionTracker)) {}
 
 void PluginsResourceService::Init() {
-  const base::DictionaryValue* metadata =
-      prefs_->GetDictionary(prefs::kPluginsMetadata);
+  const base::DictionaryValue* metadata = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(prefs::kPluginsMetadata));
   PluginFinder::GetInstance()->ReinitializePlugins(metadata);
   StartAfterDelay();
 }
diff --git a/chrome/browser/policy/system_features_disable_list_policy_handler.cc b/chrome/browser/policy/system_features_disable_list_policy_handler.cc
index 4a5b5fdc..c65db58 100644
--- a/chrome/browser/policy/system_features_disable_list_policy_handler.cc
+++ b/chrome/browser/policy/system_features_disable_list_policy_handler.cc
@@ -61,7 +61,7 @@
   if (!pref_service)  // Sometimes it's not available in tests.
     return false;
 
-  const base::ListValue* disabled_system_features_pref =
+  const base::Value* disabled_system_features_pref =
       pref_service->GetList(policy::policy_prefs::kSystemFeaturesDisableList);
   if (!disabled_system_features_pref)
     return false;
diff --git a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_decider.cc b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_decider.cc
index b4476af..13f3e28 100644
--- a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_decider.cc
+++ b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_decider.cc
@@ -79,7 +79,7 @@
 void PrefetchProxyOriginDecider::LoadFromPrefs() {
   origin_retry_afters_.clear();
 
-  const base::DictionaryValue* dictionary =
+  const base::Value* dictionary =
       pref_service_->GetDictionary(prefetch::prefs::kRetryAfterPrefPath);
   DCHECK(dictionary);
 
diff --git a/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
index 054af44..f672d30 100644
--- a/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
+++ b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
@@ -530,7 +530,7 @@
 
 bool SearchPrefetchService::LoadFromPrefs() {
   prefetch_cache_.clear();
-  const base::DictionaryValue* dictionary =
+  const base::Value* dictionary =
       profile_->GetPrefs()->GetDictionary(prefetch::prefs::kCachePrefPath);
   DCHECK(dictionary);
 
diff --git a/chrome/browser/prefetch/zero_suggest_prefetch/DIR_METADATA b/chrome/browser/prefetch/zero_suggest_prefetch/DIR_METADATA
new file mode 100644
index 0000000..a3400a2e
--- /dev/null
+++ b/chrome/browser/prefetch/zero_suggest_prefetch/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail {
+  component: "UI>Browser>Omnibox"
+}
\ No newline at end of file
diff --git a/chrome/browser/prefetch/zero_suggest_prefetch/OWNERS b/chrome/browser/prefetch/zero_suggest_prefetch/OWNERS
new file mode 100644
index 0000000..9c068f3
--- /dev/null
+++ b/chrome/browser/prefetch/zero_suggest_prefetch/OWNERS
@@ -0,0 +1,2 @@
+mahmadi@chromium.org
+file://components/omnibox/OWNERS
\ No newline at end of file
diff --git a/chrome/browser/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper.cc b/chrome/browser/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper.cc
new file mode 100644
index 0000000..e3ca782c
--- /dev/null
+++ b/chrome/browser/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper.cc
@@ -0,0 +1,71 @@
+// 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 "chrome/browser/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper.h"
+
+#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/location_bar/location_bar.h"
+#include "chrome/browser/ui/search/omnibox_utils.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/webui_url_constants.h"
+#include "components/omnibox/browser/autocomplete_controller.h"
+#include "components/omnibox/browser/autocomplete_input.h"
+#include "components/omnibox/browser/omnibox_edit_model.h"
+#include "components/omnibox/browser/omnibox_view.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+
+namespace {
+
+// Starts prefetching zero-prefix suggestions using the AutocompleteController
+// instance owned by the omnibox with a dedicated NTP_ZPS_PREFETCH page context.
+void StartPrefetch(content::WebContents* web_contents) {
+  auto* omnibox_view = search::GetOmniboxView(web_contents);
+  if (!omnibox_view) {
+    return;
+  }
+
+  auto* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  AutocompleteInput autocomplete_input(
+      u"", metrics::OmniboxEventProto::NTP_ZPS_PREFETCH,
+      ChromeAutocompleteSchemeClassifier(profile));
+  autocomplete_input.set_focus_type(OmniboxFocusType::ON_FOCUS);
+  omnibox_view->StartPrefetch(autocomplete_input);
+}
+
+}  // namespace
+
+ZeroSuggestPrefetchTabHelper::ZeroSuggestPrefetchTabHelper(
+    content::WebContents* web_contents)
+    : content::WebContentsUserData<ZeroSuggestPrefetchTabHelper>(
+          *web_contents) {
+  auto* browser = chrome::FindBrowserWithProfile(
+      Profile::FromBrowserContext(web_contents->GetBrowserContext()));
+  if (browser) {
+    browser->tab_strip_model()->AddObserver(this);
+  }
+}
+
+ZeroSuggestPrefetchTabHelper::~ZeroSuggestPrefetchTabHelper() = default;
+
+void ZeroSuggestPrefetchTabHelper::OnTabStripModelChanged(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange& change,
+    const TabStripSelectionChange& selection) {
+  if (!selection.active_tab_changed() ||
+      &GetWebContents() != selection.new_contents ||
+      GetWebContents().GetVisibleURL() != GURL(chrome::kChromeUINewTabURL)) {
+    return;
+  }
+
+  // We get here when a New Tab Page is opened in foreground or is switched to.
+  StartPrefetch(&GetWebContents());
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(ZeroSuggestPrefetchTabHelper);
diff --git a/chrome/browser/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper.h b/chrome/browser/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper.h
new file mode 100644
index 0000000..44453a9
--- /dev/null
+++ b/chrome/browser/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper.h
@@ -0,0 +1,35 @@
+// 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 CHROME_BROWSER_PREFETCH_ZERO_SUGGEST_PREFETCH_ZERO_SUGGEST_PREFETCH_TAB_HELPER_H_
+#define CHROME_BROWSER_PREFETCH_ZERO_SUGGEST_PREFETCH_ZERO_SUGGEST_PREFETCH_TAB_HELPER_H_
+
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+// Prefetches zero-prefix suggestions on opening or switching to a New Tab Page.
+class ZeroSuggestPrefetchTabHelper
+    : public content::WebContentsUserData<ZeroSuggestPrefetchTabHelper>,
+      public TabStripModelObserver {
+ public:
+  ~ZeroSuggestPrefetchTabHelper() override;
+
+  ZeroSuggestPrefetchTabHelper(const ZeroSuggestPrefetchTabHelper&) = delete;
+  ZeroSuggestPrefetchTabHelper& operator=(const ZeroSuggestPrefetchTabHelper&) =
+      delete;
+
+  // TabStripModelObserver:
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override;
+
+ private:
+  explicit ZeroSuggestPrefetchTabHelper(content::WebContents* web_contents);
+
+  friend class content::WebContentsUserData<ZeroSuggestPrefetchTabHelper>;
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+#endif  // CHROME_BROWSER_PREFETCH_ZERO_SUGGEST_PREFETCH_ZERO_SUGGEST_PREFETCH_TAB_HELPER_H_
diff --git a/chrome/browser/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper_browsertest.cc b/chrome/browser/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper_browsertest.cc
new file mode 100644
index 0000000..94ee1efc
--- /dev/null
+++ b/chrome/browser/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper_browsertest.cc
@@ -0,0 +1,144 @@
+// 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 <memory>
+
+#include "base/memory/raw_ptr.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/location_bar/location_bar.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/omnibox/browser/autocomplete_controller.h"
+#include "components/omnibox/browser/mock_autocomplete_provider_client.h"
+#include "components/omnibox/browser/omnibox_edit_model.h"
+#include "components/omnibox/browser/omnibox_view.h"
+#include "components/omnibox/common/omnibox_features.h"
+#include "components/search_engines/template_url_service.h"
+#include "components/search_engines/template_url_service_client.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace {
+
+class MockAutocompleteController : public AutocompleteController {
+ public:
+  MockAutocompleteController(
+      std::unique_ptr<AutocompleteProviderClient> provider_client,
+      int provider_types)
+      : AutocompleteController(std::move(provider_client), provider_types) {}
+  ~MockAutocompleteController() override = default;
+  MockAutocompleteController(const MockAutocompleteController&) = delete;
+  MockAutocompleteController& operator=(const MockAutocompleteController&) =
+      delete;
+
+  // AutocompleteController:
+  MOCK_METHOD1(Start, void(const AutocompleteInput&));
+  MOCK_METHOD1(StartPrefetch, void(const AutocompleteInput&));
+};
+
+}  // namespace
+
+class ZeroSuggestPrefetchTabHelperBrowserTest : public InProcessBrowserTest {
+ public:
+  ZeroSuggestPrefetchTabHelperBrowserTest() {
+    feature_list_.InitWithFeatures({omnibox::kZeroSuggestPrefetching}, {});
+  }
+
+ protected:
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+
+    auto template_url_service = std::make_unique<TemplateURLService>(
+        /*prefs=*/nullptr, std::make_unique<SearchTermsData>(),
+        /*web_data_service=*/nullptr,
+        std::unique_ptr<TemplateURLServiceClient>(), base::RepeatingClosure());
+
+    auto client_ = std::make_unique<MockAutocompleteProviderClient>();
+    client_->set_template_url_service(std::move(template_url_service));
+
+    auto controller =
+        std::make_unique<testing::NiceMock<MockAutocompleteController>>(
+            std::move(client_), 0);
+    controller_ = controller.get();
+    browser()
+        ->window()
+        ->GetLocationBar()
+        ->GetOmniboxView()
+        ->model()
+        ->set_autocomplete_controller(std::move(controller));
+  }
+
+  base::test::ScopedFeatureList feature_list_;
+  raw_ptr<testing::NiceMock<MockAutocompleteController>> controller_;
+};
+
+// Tests that navigating to or switching to the NTP starts a prefetch request.
+IN_PROC_BROWSER_TEST_F(ZeroSuggestPrefetchTabHelperBrowserTest, StartPrefetch) {
+  {
+    // Opening a background NTP does not trigger prefetching.
+    EXPECT_CALL(*controller_, StartPrefetch).Times(0);
+    EXPECT_CALL(*controller_, Start).Times(0);
+
+    EXPECT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+        browser(), GURL(chrome::kChromeUINewTabURL),
+        WindowOpenDisposition::NEW_BACKGROUND_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+    ASSERT_EQ(2, browser()->tab_strip_model()->GetTabCount());
+
+    testing::Mock::VerifyAndClearExpectations(controller_);
+  }
+  {
+    // Navigating to a url in a new foreground tab does not trigger prefetching.
+    EXPECT_CALL(*controller_, StartPrefetch).Times(0);
+    EXPECT_CALL(*controller_, Start).Times(0);
+
+    ui_test_utils::NavigateToURLWithDisposition(
+        browser(), GURL("https://foo.com"),
+        WindowOpenDisposition::NEW_FOREGROUND_TAB,
+        ui_test_utils::BROWSER_TEST_NONE);
+    ASSERT_EQ(3, browser()->tab_strip_model()->GetTabCount());
+
+    testing::Mock::VerifyAndClearExpectations(controller_);
+  }
+  {
+    // Navigating to the NTP in the current foreground tab does not trigger
+    // prefetching. This is a rare case we do not need to cover for simplicity.
+    EXPECT_CALL(*controller_, StartPrefetch).Times(0);
+    EXPECT_CALL(*controller_, Start).Times(0);
+
+    EXPECT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+        browser(), GURL(chrome::kChromeUINewTabURL),
+        WindowOpenDisposition::CURRENT_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+    ASSERT_EQ(3, browser()->tab_strip_model()->GetTabCount());
+
+    testing::Mock::VerifyAndClearExpectations(controller_);
+  }
+  {
+    // Opening a foreground NTP triggers prefetching.
+    EXPECT_CALL(*controller_, StartPrefetch).Times(1);
+    EXPECT_CALL(*controller_, Start).Times(0);
+
+    EXPECT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+        browser(), GURL(chrome::kChromeUINewTabURL),
+        WindowOpenDisposition::NEW_FOREGROUND_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+    ASSERT_EQ(4, browser()->tab_strip_model()->GetTabCount());
+
+    testing::Mock::VerifyAndClearExpectations(controller_);
+  }
+  {
+    // Switching to an NTP triggers prefetching.
+    EXPECT_CALL(*controller_, StartPrefetch).Times(1);
+    EXPECT_CALL(*controller_, Start).Times(0);
+
+    browser()->tab_strip_model()->ActivateTabAt(1);
+
+    testing::Mock::VerifyAndClearExpectations(controller_);
+  }
+}
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index a6dd765c..3aa33b0 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -400,6 +400,7 @@
 
 #if defined(OS_WIN)
 #include "chrome/browser/component_updater/sw_reporter_installer_win.h"
+#include "chrome/browser/font_prewarmer_tab_helper.h"
 #include "chrome/browser/media/cdm_pref_service_helper.h"
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
 #include "chrome/browser/win/conflicts/incompatible_applications_updater.h"
@@ -1412,6 +1413,7 @@
 #if defined(OS_WIN)
   CdmPrefServiceHelper::RegisterProfilePrefs(registry);
   component_updater::RegisterProfilePrefsForSwReporter(registry);
+  FontPrewarmerTabHelper::RegisterProfilePrefs(registry);
   NetworkProfileBubble::RegisterProfilePrefs(registry);
   safe_browsing::SettingsResetPromptPrefsManager::RegisterProfilePrefs(
       registry);
diff --git a/chrome/browser/prefs/pref_metrics_service.cc b/chrome/browser/prefs/pref_metrics_service.cc
index 2e8e440..51bec2b 100644
--- a/chrome/browser/prefs/pref_metrics_service.cc
+++ b/chrome/browser/prefs/pref_metrics_service.cc
@@ -77,7 +77,7 @@
   if (SessionStartupPref(
           SessionStartupPref::PrefValueToType(restore_on_startup))
           .ShouldOpenUrls()) {
-    const base::ListValue* url_list =
+    const base::Value* url_list =
         prefs_->GetList(prefs::kURLsToRestoreOnStartup);
     // Similarly, check startup pages for known search engine TLD+1s.
     for (const base::Value& i : url_list->GetList()) {
diff --git a/chrome/browser/prefs/session_startup_pref.cc b/chrome/browser/prefs/session_startup_pref.cc
index 6fce334..0cd7cbe 100644
--- a/chrome/browser/prefs/session_startup_pref.cc
+++ b/chrome/browser/prefs/session_startup_pref.cc
@@ -117,8 +117,8 @@
 
   // Always load the urls, even if the pref type isn't URLS. This way the
   // preferences panels can show the user their last choice.
-  const base::ListValue* url_list =
-      prefs->GetList(prefs::kURLsToRestoreOnStartup);
+  const base::ListValue* url_list = &base::Value::AsListValue(
+      *prefs->GetList(prefs::kURLsToRestoreOnStartup));
   URLListToPref(url_list, &pref);
 
   return pref;
diff --git a/chrome/browser/printing/print_preview_sticky_settings.cc b/chrome/browser/printing/print_preview_sticky_settings.cc
index 40da6cf0..1420bb0 100644
--- a/chrome/browser/printing/print_preview_sticky_settings.cc
+++ b/chrome/browser/printing/print_preview_sticky_settings.cc
@@ -47,7 +47,7 @@
 }
 
 void PrintPreviewStickySettings::RestoreFromPrefs(PrefService* prefs) {
-  const base::DictionaryValue* value =
+  const base::Value* value =
       prefs->GetDictionary(prefs::kPrintPreviewStickySettings);
   const base::Value* app_state =
       value->FindKeyOfType(kSettingAppState, base::Value::Type::STRING);
diff --git a/chrome/browser/profiles/host_zoom_map_browsertest.cc b/chrome/browser/profiles/host_zoom_map_browsertest.cc
index 1b4943a..2e4bcac4 100644
--- a/chrome/browser/profiles/host_zoom_map_browsertest.cc
+++ b/chrome/browser/profiles/host_zoom_map_browsertest.cc
@@ -121,8 +121,8 @@
 
   std::vector<std::string> GetHostsWithZoomLevelsFromPrefs() {
     PrefService* prefs = browser()->profile()->GetPrefs();
-    const base::DictionaryValue* dictionaries =
-        prefs->GetDictionary(prefs::kPartitionPerHostZoomLevels);
+    const base::DictionaryValue* dictionaries = &base::Value::AsDictionaryValue(
+        *prefs->GetDictionary(prefs::kPartitionPerHostZoomLevels));
     const base::DictionaryValue* values = NULL;
     std::string partition_key =
         ChromeZoomLevelPrefs::GetPartitionKeyForTesting(base::FilePath());
diff --git a/chrome/browser/profiles/profile_attributes_entry.cc b/chrome/browser/profiles/profile_attributes_entry.cc
index 92cba47..58ad8e7 100644
--- a/chrome/browser/profiles/profile_attributes_entry.cc
+++ b/chrome/browser/profiles/profile_attributes_entry.cc
@@ -902,7 +902,7 @@
 }
 
 const base::Value* ProfileAttributesEntry::GetEntryData() const {
-  const base::DictionaryValue* attributes =
+  const base::Value* attributes =
       prefs_->GetDictionary(prefs::kProfileAttributes);
   return attributes->FindDictKey(storage_key_);
 }
diff --git a/chrome/browser/profiles/profile_attributes_storage_unittest.cc b/chrome/browser/profiles/profile_attributes_storage_unittest.cc
index b70c3fc..dc177c6 100644
--- a/chrome/browser/profiles/profile_attributes_storage_unittest.cc
+++ b/chrome/browser/profiles/profile_attributes_storage_unittest.cc
@@ -534,7 +534,7 @@
   // Check that the profiles can be extracted from the local state.
   std::vector<std::string> names;
   PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* attributes =
+  const base::Value* attributes =
       local_state->GetDictionary(prefs::kProfileAttributes);
   for (const auto kv : attributes->DictItems()) {
     const base::Value& info = kv.second;
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 76505930..dce671bb 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -1187,7 +1187,7 @@
 void ProfileManager::CleanUpDeletedProfiles() {
   PrefService* local_state = g_browser_process->local_state();
   DCHECK(local_state);
-  const base::ListValue* deleted_profiles =
+  const base::Value* deleted_profiles =
       local_state->GetList(prefs::kProfilesDeleted);
   DCHECK(deleted_profiles);
 
diff --git a/chrome/browser/profiles/profile_manager_unittest.cc b/chrome/browser/profiles/profile_manager_unittest.cc
index cb43ec8..7f79fd5 100644
--- a/chrome/browser/profiles/profile_manager_unittest.cc
+++ b/chrome/browser/profiles/profile_manager_unittest.cc
@@ -353,6 +353,14 @@
          arg == Profile::CREATE_STATUS_INITIALIZED;
 }
 
+MATCHER(Created, "Profile creation status is created.") {
+  return arg == Profile::CREATE_STATUS_CREATED;
+}
+
+MATCHER(Initialized, "Profile creation status is initialized.") {
+  return arg == Profile::CREATE_STATUS_INITIALIZED;
+}
+
 MATCHER(SameNotNull, "The same non-NULL value for all calls.") {
   if (!g_created_profile)
     g_created_profile = arg;
@@ -578,15 +586,17 @@
 
   const std::string profile_name = "New Profile";
 
+  base::RunLoop run_loop;
   MockObserver mock_observer;
   Profile* profile = nullptr;
-  EXPECT_CALL(mock_observer, OnProfileCreated(testing::NotNull(), NotFail()))
-      .Times(testing::AtLeast(2))
-      .WillRepeatedly(testing::SaveArg<0>(&profile));
+  EXPECT_CALL(mock_observer, OnProfileCreated(testing::NotNull(), Created()))
+      .WillOnce(testing::SaveArg<0>(&profile));
+  EXPECT_CALL(mock_observer,
+              OnProfileCreated(testing::NotNull(), Initialized()))
+      .WillOnce([&run_loop] { run_loop.Quit(); });
 
   CreateMultiProfileAsync(profile_manager, profile_name, &mock_observer);
-
-  content::RunAllTasksUntilIdle();
+  run_loop.Run();
 
   // Check that the profile name is set correctly both in the profile prefs and
   // in storage.
@@ -603,42 +613,41 @@
   const std::string profile_name1 = "New Profile 1";
   const std::string profile_name2 = "New Profile 2";
 
+  base::RunLoop run_loop;
   MockObserver mock_observer;
-  EXPECT_CALL(mock_observer, OnProfileCreated(
-      testing::NotNull(), NotFail())).Times(testing::AtLeast(3));
+  EXPECT_CALL(mock_observer, OnProfileCreated(testing::NotNull(), Created()))
+      .Times(2);
+  EXPECT_CALL(mock_observer,
+              OnProfileCreated(testing::NotNull(), Initialized()))
+      .WillOnce(testing::Return())
+      .WillOnce([&run_loop] { run_loop.Quit(); });
 
   ProfileManager* profile_manager = g_browser_process->profile_manager();
 
   CreateMultiProfileAsync(profile_manager, profile_name1, &mock_observer);
   CreateMultiProfileAsync(profile_manager, profile_name2, &mock_observer);
-
-  content::RunAllTasksUntilIdle();
+  run_loop.Run();
 }
 
 TEST_F(ProfileManagerTest, CreateMultiProfileAsyncMultipleRequests) {
-  Profile* profile1 = nullptr;
-  MockObserver mock_observer1;
-  EXPECT_CALL(mock_observer1, OnProfileCreated(testing::NotNull(), NotFail()))
-      .Times(testing::AtLeast(1))
-      .WillRepeatedly(testing::SaveArg<0>(&profile1));
-  Profile* profile2 = nullptr;
-  MockObserver mock_observer2;
-  EXPECT_CALL(mock_observer2, OnProfileCreated(testing::NotNull(), NotFail()))
-      .Times(testing::AtLeast(1))
-      .WillRepeatedly(testing::SaveArg<0>(&profile2));
-  Profile* profile3 = nullptr;
-  MockObserver mock_observer3;
-  EXPECT_CALL(mock_observer3, OnProfileCreated(testing::NotNull(), NotFail()))
-      .Times(testing::AtLeast(1))
-      .WillRepeatedly(testing::SaveArg<0>(&profile3));
+  base::RunLoop run_loop;
+  MockObserver mock_observer;
+  Profile *profile1 = nullptr, *profile2 = nullptr, *profile3 = nullptr;
+  EXPECT_CALL(mock_observer, OnProfileCreated(testing::NotNull(), Created()))
+      .Times(3);
+  EXPECT_CALL(mock_observer,
+              OnProfileCreated(testing::NotNull(), Initialized()))
+      .WillOnce(testing::SaveArg<0>(&profile1))
+      .WillOnce(testing::SaveArg<0>(&profile2))
+      .WillOnce(testing::DoAll(testing::SaveArg<0>(&profile3),
+                               [&run_loop] { run_loop.Quit(); }));
 
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   const std::string profile_name = "New Profile";
-  CreateMultiProfileAsync(profile_manager, profile_name, &mock_observer1);
-  CreateMultiProfileAsync(profile_manager, profile_name, &mock_observer2);
-  CreateMultiProfileAsync(profile_manager, profile_name, &mock_observer3);
-
-  content::RunAllTasksUntilIdle();
+  CreateMultiProfileAsync(profile_manager, profile_name, &mock_observer);
+  CreateMultiProfileAsync(profile_manager, profile_name, &mock_observer);
+  CreateMultiProfileAsync(profile_manager, profile_name, &mock_observer);
+  run_loop.Run();
 
   // A new profile should have been created for each call.
   EXPECT_NE(profile1, profile2);
@@ -662,13 +671,17 @@
   profile_manager->GetProfileAttributesStorage().AddProfile(
       std::move(params_1));
 
-  MockObserver mock_observer2;
+  base::RunLoop run_loop;
+  MockObserver mock_observer;
   Profile* profile2 = nullptr;
-  EXPECT_CALL(mock_observer2, OnProfileCreated(testing::NotNull(), NotFail()))
-      .Times(testing::AtLeast(2))
-      .WillRepeatedly(testing::SaveArg<0>(&profile2));
-  CreateMultiProfileAsync(profile_manager, "Profile B", &mock_observer2);
-  content::RunAllTasksUntilIdle();
+  EXPECT_CALL(mock_observer, OnProfileCreated(testing::NotNull(), Created()))
+      .WillOnce(testing::SaveArg<0>(&profile2));
+  EXPECT_CALL(mock_observer,
+              OnProfileCreated(testing::NotNull(), Initialized()))
+      .WillOnce([&run_loop] { run_loop.Quit(); });
+
+  CreateMultiProfileAsync(profile_manager, "Profile B", &mock_observer);
+  run_loop.Run();
 
   ASSERT_NE(profile2, nullptr);
   EXPECT_EQ(profile2->GetPath().BaseName().value(),
@@ -684,13 +697,16 @@
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   ASSERT_TRUE(profile_manager);
 
-  MockObserver mock_observer1;
+  base::RunLoop run_loop;
+  MockObserver mock_observer;
   Profile* profile1 = nullptr;
-  EXPECT_CALL(mock_observer1, OnProfileCreated(testing::NotNull(), NotFail()))
-      .Times(testing::AtLeast(2))
-      .WillRepeatedly(testing::SaveArg<0>(&profile1));
-  CreateMultiProfileAsync(profile_manager, "Profile A", &mock_observer1);
-  content::RunAllTasksUntilIdle();
+  EXPECT_CALL(mock_observer, OnProfileCreated(testing::NotNull(), Created()))
+      .WillOnce(testing::SaveArg<0>(&profile1));
+  EXPECT_CALL(mock_observer,
+              OnProfileCreated(testing::NotNull(), Initialized()))
+      .WillOnce([&run_loop] { run_loop.Quit(); });
+  CreateMultiProfileAsync(profile_manager, "Profile A", &mock_observer);
+  run_loop.Run();
 
   ASSERT_NE(profile1, nullptr);
   EXPECT_EQ(profile1->GetPath().BaseName().value(),
@@ -718,13 +734,16 @@
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   ASSERT_TRUE(profile_manager);
 
-  MockObserver mock_observer2;
+  base::RunLoop run_loop;
+  MockObserver mock_observer;
   Profile* profile2 = nullptr;
-  EXPECT_CALL(mock_observer2, OnProfileCreated(testing::NotNull(), NotFail()))
-      .Times(testing::AtLeast(2))
-      .WillRepeatedly(testing::SaveArg<0>(&profile2));
-  CreateMultiProfileAsync(profile_manager, "Profile B", &mock_observer2);
-  content::RunAllTasksUntilIdle();
+  EXPECT_CALL(mock_observer, OnProfileCreated(testing::NotNull(), Created()))
+      .WillOnce(testing::SaveArg<0>(&profile2));
+  EXPECT_CALL(mock_observer,
+              OnProfileCreated(testing::NotNull(), Initialized()))
+      .WillOnce([&run_loop] { run_loop.Quit(); });
+  CreateMultiProfileAsync(profile_manager, "Profile B", &mock_observer);
+  run_loop.Run();
 
   ASSERT_NE(profile2, nullptr);
   // The profile uses the same base name as in the pre test.
@@ -738,11 +757,14 @@
 }
 
 TEST_F(ProfileManagerTest, CreateHiddenProfileAsync) {
+  base::RunLoop run_loop;
   Profile* profile = nullptr;
   MockObserver mock_observer;
-  EXPECT_CALL(mock_observer, OnProfileCreated(testing::NotNull(), NotFail()))
-      .Times(testing::AtLeast(2))
-      .WillRepeatedly(testing::SaveArg<0>(&profile));
+  EXPECT_CALL(mock_observer, OnProfileCreated(testing::NotNull(), Created()))
+      .WillOnce(testing::SaveArg<0>(&profile));
+  EXPECT_CALL(mock_observer,
+              OnProfileCreated(testing::NotNull(), Initialized()))
+      .WillOnce([&run_loop] { run_loop.Quit(); });
 
   ProfileManager* profile_manager = g_browser_process->profile_manager();
 
@@ -751,7 +773,7 @@
       base::BindRepeating(&MockObserver::OnProfileCreated,
                           base::Unretained(&mock_observer)));
 
-  content::RunAllTasksUntilIdle();
+  run_loop.Run();
   ASSERT_NE(profile, nullptr);
 
   ProfileAttributesEntry* entry =
@@ -1531,7 +1553,7 @@
 
   profile_manager->CleanUpEphemeralProfiles();
   content::RunAllTasksUntilIdle();
-  const base::ListValue* final_last_active_profile_list =
+  const base::Value* final_last_active_profile_list =
       local_state->GetList(prefs::kProfilesLastActive);
 
   // The ephemeral profile should be deleted, and the last used profile set to
@@ -1602,7 +1624,7 @@
 
   profile_manager->CleanUpEphemeralProfiles();
   content::RunAllTasksUntilIdle();
-  const base::ListValue* final_last_active_profile_list =
+  const base::Value* final_last_active_profile_list =
       local_state->GetList(prefs::kProfilesLastActive);
 
   // The guest and the non-ephemeral regular profile aren't impacted.
diff --git a/chrome/browser/renderer_preferences_util.cc b/chrome/browser/renderer_preferences_util.cc
index a853f8b..e0e8e53 100644
--- a/chrome/browser/renderer_preferences_util.cc
+++ b/chrome/browser/renderer_preferences_util.cc
@@ -87,7 +87,7 @@
 
 // Extracts the string representation of URLs allowed for local IP exposure.
 std::vector<std::string> GetLocalIpsAllowedUrls(
-    const base::ListValue* allowed_urls) {
+    const base::Value* allowed_urls) {
   std::vector<std::string> ret;
   if (allowed_urls) {
     const auto& urls = allowed_urls->GetList();
@@ -143,7 +143,7 @@
   ParsePortRange(webrtc_udp_port_range, &prefs->webrtc_udp_min_port,
                  &prefs->webrtc_udp_max_port);
 
-  const base::ListValue* allowed_urls =
+  const base::Value* allowed_urls =
       pref_service->GetList(prefs::kWebRtcLocalIpsAllowedUrls);
   prefs->webrtc_local_ips_allowed_urls = GetLocalIpsAllowedUrls(allowed_urls);
   prefs->webrtc_allow_legacy_tls_protocols =
diff --git a/chrome/browser/resources/app_service_internals/app_service_internals.html b/chrome/browser/resources/app_service_internals/app_service_internals.html
index bd67ddf..d81c223c 100644
--- a/chrome/browser/resources/app_service_internals/app_service_internals.html
+++ b/chrome/browser/resources/app_service_internals/app_service_internals.html
@@ -1,5 +1,7 @@
 <h1>App Service Internals</h1>
 
+<button on-click="save_">Save as .txt</button>
+
 <ul>
   <li>
     <a href="#app-list">App List</a>
diff --git a/chrome/browser/resources/app_service_internals/app_service_internals.ts b/chrome/browser/resources/app_service_internals/app_service_internals.ts
index a07dfb2..af1c61a 100644
--- a/chrome/browser/resources/app_service_internals/app_service_internals.ts
+++ b/chrome/browser/resources/app_service_internals/app_service_internals.ts
@@ -65,6 +65,32 @@
 
     selected.scrollIntoView();
   }
+
+  save_() {
+    const fileParts = [];
+    fileParts.push('App List\n');
+    fileParts.push('========\n\n');
+    for (const app of this.appList_) {
+      fileParts.push(app.name + '\n');
+      fileParts.push('-----\n');
+      fileParts.push(app.debugInfo + '\n');
+    }
+
+    fileParts.push('Preferred Apps\n');
+    fileParts.push('==============\n\n');
+    for (const preferredApp of this.preferredAppList_) {
+      fileParts.push(preferredApp.name + '\n');
+      fileParts.push('-----\n');
+      fileParts.push(preferredApp.preferredFilters + '\n');
+    }
+
+    const file = new Blob(fileParts);
+    const a = document.createElement('a');
+    a.href = URL.createObjectURL(file);
+    a.download = 'app-service-internals.txt';
+    a.click();
+    URL.revokeObjectURL(a.href);
+  }
 }
 
 customElements.define(
diff --git a/chrome/browser/resources/bookmarks/folder_node.html b/chrome/browser/resources/bookmarks/folder_node.html
index a7d69bb..4892d7d 100644
--- a/chrome/browser/resources/bookmarks/folder_node.html
+++ b/chrome/browser/resources/bookmarks/folder_node.html
@@ -55,7 +55,7 @@
 
   @media (prefers-color-scheme: dark) {
     .cr-nav-menu-item.drag-on {
-      color: var(--google-grey-refresh-700);
+      color: var(--google-grey-700);
     }
   }
 
@@ -100,7 +100,7 @@
 
     :host-context([enable-branding-update]) .cr-nav-menu-item.drag-on {
       --iron-icon-fill-color: black;
-      background: var(--google-blue-refresh-300);
+      background: var(--google-blue-300);
       color: var(--google-grey-900);
     }
   }
diff --git a/chrome/browser/resources/bookmarks/item.html b/chrome/browser/resources/bookmarks/item.html
index f40cae1d..493271b 100644
--- a/chrome/browser/resources/bookmarks/item.html
+++ b/chrome/browser/resources/bookmarks/item.html
@@ -16,7 +16,7 @@
   }
 
   :host-context([enable-branding-update]):host {
-    --iron-icon-fill-color: var(--google-grey-refresh-700);
+    --iron-icon-fill-color: var(--google-grey-700);
   }
 
   :host-context([enable-branding-update]):host([is-selected-item_]) {
@@ -27,11 +27,11 @@
     :host([is-selected-item_]),
     :host([is-selected-item_]) .folder-icon {
       --cr-icon-button-focus-outline-color: white;
-      color: var(--google-grey-refresh-700);
+      color: var(--google-grey-700);
     }
 
     :host-context([enable-branding-update]):host {
-      --iron-icon-fill-color: var(--google-grey-refresh-500);
+      --iron-icon-fill-color: var(--google-grey-500);
     }
 
     :host-context([enable-branding-update]):host([is-selected-item_]) {
diff --git a/chrome/browser/resources/bookmarks/shared_vars.html b/chrome/browser/resources/bookmarks/shared_vars.html
index 7b5b2680..6ae6b37 100644
--- a/chrome/browser/resources/bookmarks/shared_vars.html
+++ b/chrome/browser/resources/bookmarks/shared_vars.html
@@ -15,10 +15,10 @@
 
   @media (prefers-color-scheme: dark) {
     html {
-      --folder-icon-color: var(--google-grey-refresh-500);
-      --folder-inactive-color: var(--google-grey-refresh-500);
-      --highlight-color: var(--google-blue-refresh-300);
-      --interactive-color: var(--google-blue-refresh-300);
+      --folder-icon-color: var(--google-grey-500);
+      --folder-inactive-color: var(--google-grey-500);
+      --highlight-color: var(--google-blue-300);
+      --interactive-color: var(--google-blue-300);
     }
   }
 </style>
diff --git a/chrome/browser/resources/components/components.js b/chrome/browser/resources/components/components.js
index 757401a..968f172 100644
--- a/chrome/browser/resources/components/components.js
+++ b/chrome/browser/resources/components/components.js
@@ -31,7 +31,7 @@
   jstProcess(input, output);
   output.removeAttribute('hidden');
 
-// <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   const crosUrlRedirectButton = $('os-link-href');
   if (crosUrlRedirectButton) {
     crosUrlRedirectButton.onclick = crosUrlComponentRedirect;
@@ -39,7 +39,7 @@
 // </if>
 }
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 /**
  * Called when the user clicks on the os-link-href button.
  */
diff --git a/chrome/browser/resources/download_shelf/download_item.html b/chrome/browser/resources/download_shelf/download_item.html
index 7d30a57..9fdd77a 100644
--- a/chrome/browser/resources/download_shelf/download_item.html
+++ b/chrome/browser/resources/download_shelf/download_item.html
@@ -1,7 +1,7 @@
 <style>
 :host {
   --button-hover-color: rgba(var(--google-grey-900-rgb), .1);
-  --button-icon-stroke-color: var(--google-grey-refresh-700);
+  --button-icon-stroke-color: var(--google-grey-700);
   --pi: 3.14159265358979;
   --spinner-color-rgb: var(--google-blue-600-rgb);
   --text-width: 140px;
diff --git a/chrome/browser/resources/downloads/item.html b/chrome/browser/resources/downloads/item.html
index 26e60d2..8c29d8b 100644
--- a/chrome/browser/resources/downloads/item.html
+++ b/chrome/browser/resources/downloads/item.html
@@ -91,7 +91,7 @@
 
   @media (prefers-color-scheme: dark) {
     #content:not(.is-active) #details {
-      color: rgba(var(--google-grey-refresh-500-rgb), .6);
+      color: rgba(var(--google-grey-500-rgb), .6);
     }
   }
 
@@ -101,7 +101,7 @@
 
   @media (prefers-color-scheme: dark) {
     #content:not(.is-active) :-webkit-any(#name, #tag) {
-      color: var(--google-grey-refresh-500);
+      color: var(--google-grey-500);
     }
   }
 
@@ -148,7 +148,7 @@
 
   @media (prefers-color-scheme: dark) {
     #file-icon-wrapper iron-icon[icon-color='red'] {
-      color: var(--google-red-refresh-300);
+      color: var(--google-red-300);
     }
   }
 
@@ -225,7 +225,7 @@
 
   @media (prefers-color-scheme: dark) {
     #progress {
-      --paper-progress-active-color: var(--google-blue-refresh-300);
+      --paper-progress-active-color: var(--google-blue-300);
       --paper-progress-container-color: var(--google-grey-800);
     }
   }
diff --git a/chrome/browser/resources/extensions/activity_log/activity_log.html b/chrome/browser/resources/extensions/activity_log/activity_log.html
index 9d04ec2..7dfb735 100644
--- a/chrome/browser/resources/extensions/activity_log/activity_log.html
+++ b/chrome/browser/resources/extensions/activity_log/activity_log.html
@@ -16,7 +16,7 @@
   cr-tabs {
     --cr-tabs-font-size: inherit;
     --cr-tabs-height: 40px;
-    border-bottom: 1px solid var(--google-grey-refresh-300);
+    border-bottom: 1px solid var(--google-grey-300);
   }
 
   .page-content {
diff --git a/chrome/browser/resources/extensions/code_section.html b/chrome/browser/resources/extensions/code_section.html
index 7fc90e9..07ecbfc 100644
--- a/chrome/browser/resources/extensions/code_section.html
+++ b/chrome/browser/resources/extensions/code_section.html
@@ -80,7 +80,7 @@
 
   @media (prefers-color-scheme: dark) {
     mark {
-      background-color: var(--google-yellow-refresh-300);
+      background-color: var(--google-yellow-300);
       color: var(--google-grey-900);
     }
   }
diff --git a/chrome/browser/resources/extensions/detail_view.html b/chrome/browser/resources/extensions/detail_view.html
index 3a4973ba..967e53f 100644
--- a/chrome/browser/resources/extensions/detail_view.html
+++ b/chrome/browser/resources/extensions/detail_view.html
@@ -25,7 +25,7 @@
 
   @media (prefers-color-scheme: dark) {
     #enable-section .enabled-text {
-      color: var(--google-blue-refresh-300);
+      color: var(--google-blue-300);
     }
   }
 
diff --git a/chrome/browser/resources/extensions/error_page.html b/chrome/browser/resources/extensions/error_page.html
index bfb1762..022c85a9 100644
--- a/chrome/browser/resources/extensions/error_page.html
+++ b/chrome/browser/resources/extensions/error_page.html
@@ -5,7 +5,7 @@
   }
 
   iron-icon {
-    --iron-icon-fill-color: var(--google-grey-refresh-700);
+    --iron-icon-fill-color: var(--google-grey-700);
     flex-shrink: 0;
     height: var(--cr-icon-size);
     width: var(--cr-icon-size);
diff --git a/chrome/browser/resources/extensions/shared_vars.html b/chrome/browser/resources/extensions/shared_vars.html
index d10f82cd..37d11172 100644
--- a/chrome/browser/resources/extensions/shared_vars.html
+++ b/chrome/browser/resources/extensions/shared_vars.html
@@ -12,8 +12,8 @@
 
   @media (prefers-color-scheme: dark) {
     html {
-      --error-color: var(--google-red-refresh-300);
-      --warning-color: var(--google-yellow-refresh-300);
+      --error-color: var(--google-red-300);
+      --warning-color: var(--google-yellow-300);
     }
   }
 </style>
diff --git a/chrome/browser/resources/extensions/toolbar.html b/chrome/browser/resources/extensions/toolbar.html
index 62ff38f9..5063d91 100644
--- a/chrome/browser/resources/extensions/toolbar.html
+++ b/chrome/browser/resources/extensions/toolbar.html
@@ -10,7 +10,7 @@
   /* This toggle needs special styling because it's on blue background. */
   @media (prefers-color-scheme: light) {
     :host(:not([enable-branding-update])) cr-toolbar cr-toggle {
-      --cr-toggle-checked-bar-color: var(--google-grey-refresh-100);
+      --cr-toggle-checked-bar-color: var(--google-grey-100);
       --cr-toggle-checked-button-color: white;
       --cr-toggle-checked-ripple-color: rgba(255, 255, 255, .2);
       --cr-toggle-unchecked-bar-color: var(--google-grey-600);
diff --git a/chrome/browser/resources/history/history_clusters/shared_vars.html b/chrome/browser/resources/history/history_clusters/shared_vars.html
index 5f65ac4b..5168553 100644
--- a/chrome/browser/resources/history/history_clusters/shared_vars.html
+++ b/chrome/browser/resources/history/history_clusters/shared_vars.html
@@ -4,20 +4,20 @@
   html {
     --annotation-background-color: var(--google-green-50);
     --annotation-text-color: var(--google-green-600);
-    --border-color: var(--google-grey-refresh-300);
-    --grey-fill-color: var(--google-grey-refresh-100);
+    --border-color: var(--google-grey-300);
+    --grey-fill-color: var(--google-grey-100);
     --icon-color: var(--google-grey-600);
     --url-hostname-color: var(--google-blue-600);
   }
 
 @media (prefers-color-scheme: dark) {
   html {
-    --annotation-background-color: var(--google-green-refresh-300);
+    --annotation-background-color: var(--google-green-300);
     --annotation-text-color: var(--google-grey-900);
-    --border-color: var(--google-grey-refresh-700);
-    --grey-fill-color: var(--google-grey-refresh-700);
+    --border-color: var(--google-grey-700);
+    --grey-fill-color: var(--google-grey-700);
     --icon-color: white;
-    --url-hostname-color: var(--google-blue-refresh-300);
+    --url-hostname-color: var(--google-blue-300);
   }
 }
 
diff --git a/chrome/browser/resources/history/history_item.html b/chrome/browser/resources/history/history_item.html
index e870584b..78f2c68 100644
--- a/chrome/browser/resources/history/history_item.html
+++ b/chrome/browser/resources/history/history_item.html
@@ -123,7 +123,7 @@
 
       @media (prefers-color-scheme: dark) {
         #time-gap-separator {
-          border-color: var(--google-grey-refresh-500);
+          border-color: var(--google-grey-500);
         }
       }
 
diff --git a/chrome/browser/resources/history/shared_vars.html b/chrome/browser/resources/history/shared_vars.html
index 5387f36..820cd591 100644
--- a/chrome/browser/resources/history/shared_vars.html
+++ b/chrome/browser/resources/history/shared_vars.html
@@ -14,7 +14,7 @@
     --item-height: 44px;
     --separator-color: rgba(0, 0, 0, 0.08);
     --side-bar-width: 256px;
-    --sidebar-footer-text-color: var(--google-grey-refresh-700);
+    --sidebar-footer-text-color: var(--google-grey-700);
     --sidebar-unselected-color: #5a5a5a;
     --toolbar-height: 56px;
   }
@@ -23,7 +23,7 @@
     html {
       --card-border-color: var(--cr-separator-color);
       --history-item-time-color: var(--cr-secondary-text-color);
-      --interactive-color: var(--google-blue-refresh-300);
+      --interactive-color: var(--google-blue-300);
       --separator-color: var(--cr-separator-color);
       --sidebar-footer-text-color: rgba(255, 255, 255, 0.6);
       --sidebar-unselected-color: var(--cr-secondary-text-color);
diff --git a/chrome/browser/resources/pdf/viewport.js b/chrome/browser/resources/pdf/viewport.js
index 7954418..2a180d23 100644
--- a/chrome/browser/resources/pdf/viewport.js
+++ b/chrome/browser/resources/pdf/viewport.js
@@ -1825,12 +1825,13 @@
           this.width_ > this.container_.clientWidth);
 
       if (this.container_.dir === 'rtl') {
-        // Right-to-left.
+        // Right-to-left. If `maxX` > 0, clamp to [-maxX, 0]. Else set to 0.
         x = Math.min(Math.max(-maxX, x), 0);
       } else {
-        // Left-to-right.
+        // Left-to-right. If `maxX` > 0, clamp to [0, maxX]. Else set to 0.
         x = Math.max(0, Math.min(x, maxX));
       }
+      // If `maxY` > 0, clamp to [0, maxY]. Else set to 0.
       y = Math.max(0, Math.min(y, maxY));
 
       // To match the DOM's scrollTo() behavior, update the scroll position
diff --git a/chrome/browser/resources/preinstalled_web_apps/resources.grd b/chrome/browser/resources/preinstalled_web_apps/resources.grd
index 96f074b0..c1942e9 100644
--- a/chrome/browser/resources/preinstalled_web_apps/resources.grd
+++ b/chrome/browser/resources/preinstalled_web_apps/resources.grd
@@ -18,7 +18,7 @@
       <include name="IDR_PREINSTALLED_WEB_APPS_GOOGLE_SLIDES_ICON_192_PNG" file="internal/google_slides_192.png" type="BINDATA" />
       <include name="IDR_PREINSTALLED_WEB_APPS_GMAIL_ICON_192_PNG" file="internal/gmail_192.png" type="BINDATA" />
       <include name="IDR_PREINSTALLED_WEB_APPS_YOUTUBE_ICON_192_PNG" file="internal/youtube_192.png" type="BINDATA" />
-      <if expr="chromeos or lacros">
+      <if expr="chromeos_ash or chromeos_lacros">
         <include name="IDR_PREINSTALLED_WEB_APPS_GOOGLE_CALENDAR_ICON_192_PNG" file="internal/google_calendar_192.png" type="BINDATA" />
       </if>
     </includes>
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface_impl.ts b/chrome/browser/resources/print_preview/cloud_print_interface_impl.ts
index 812730f..ec722b5 100644
--- a/chrome/browser/resources/print_preview/cloud_print_interface_impl.ts
+++ b/chrome/browser/resources/print_preview/cloud_print_interface_impl.ts
@@ -10,7 +10,7 @@
 import {CloudOrigins, Destination, DestinationOrigin} from './data/destination.js';
 import {PrinterType} from './data/destination_match.js';
 import {MetricsContext, PrintPreviewInitializationEvents} from './metrics.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {NativeLayerCrosImpl} from './native_layer_cros.js';
 
 // </if>
@@ -51,7 +51,7 @@
    */
   private outstandingCloudSearchRequests_: CloudPrintRequest[] = [];
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * Promise that will be resolved when the access token for
    * DestinationOrigin.DEVICE is available. Null if there is no request
@@ -249,7 +249,7 @@
       return;
     }
 
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     assert(request.origin === DestinationOrigin.DEVICE);
     if (this.accessTokenRequestPromise_ === null) {
       this.accessTokenRequestPromise_ =
@@ -342,7 +342,7 @@
         });
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * Called when a native layer receives access token. Assumes that the
    * destination type for this token is DestinationOrigin.DEVICE.
diff --git a/chrome/browser/resources/print_preview/data/cdd.ts b/chrome/browser/resources/print_preview/data/cdd.ts
index e5ff852..d2a3b2fa 100644
--- a/chrome/browser/resources/print_preview/data/cdd.ts
+++ b/chrome/browser/resources/print_preview/data/cdd.ts
@@ -132,7 +132,7 @@
   page_orientation?: PageOrientationCapability,
   media_size?: MediaSizeCapability,
   dpi?: DpiCapability,
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   pin?: PinCapability,
   // </if>
 };
diff --git a/chrome/browser/resources/print_preview/data/destination.ts b/chrome/browser/resources/print_preview/data/destination.ts
index 2bd65b9..175af086 100644
--- a/chrome/browser/resources/print_preview/data/destination.ts
+++ b/chrome/browser/resources/print_preview/data/destination.ts
@@ -8,13 +8,13 @@
 import {isChromeOS, isLacros} from 'chrome://resources/js/cr.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {NativeLayerCrosImpl} from '../native_layer_cros.js';
 // </if>
 
 import {Cdd, ColorCapability, ColorOption, CopiesCapability} from './cdd.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {getStatusReasonFromPrinterStatus, PrinterStatus, PrinterStatusReason} from './printer_status_cros.js';
 // </if>
 
@@ -34,7 +34,7 @@
 export enum DestinationOrigin {
   LOCAL = 'local',
   COOKIES = 'cookies',
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   DEVICE = 'device',
   // </if>
   // Note: Privet is deprecated, but used to filter any legacy entries in the
@@ -50,7 +50,7 @@
  */
 export const CloudOrigins: DestinationOrigin[] = [
   DestinationOrigin.COOKIES,
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   DestinationOrigin.DEVICE,
   // </if>
 ];
@@ -267,7 +267,7 @@
    */
   private certificateStatus_: DestinationCertificateStatus;
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * EULA url for printer's PPD. Empty string indicates no provided EULA.
    */
@@ -457,7 +457,7 @@
     }
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   get eulaUrl(): string {
     return this.eulaUrl_;
   }
@@ -634,7 +634,7 @@
 
   /** @return Path to the SVG for the destination's icon. */
   get icon(): string {
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     if (this.id_ === GooglePromotedDestinationId.SAVE_TO_DRIVE_CROS) {
       return 'print-preview:save-to-drive';
     }
@@ -812,7 +812,7 @@
 export enum GooglePromotedDestinationId {
   DOCS = '__google__docs',
   SAVE_AS_PDF = 'Save as PDF',
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   SAVE_TO_DRIVE_CROS = 'Save to Drive CrOS',
   // </if>
 }
@@ -821,7 +821,7 @@
 export const PDF_DESTINATION_KEY: string =
     `${GooglePromotedDestinationId.SAVE_AS_PDF}/${DestinationOrigin.LOCAL}/`;
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 /* Unique identifier for the Save to Drive CrOS destination */
 export const SAVE_TO_DRIVE_CROS_DESTINATION_KEY: string =
     `${GooglePromotedDestinationId.SAVE_TO_DRIVE_CROS}/${
diff --git a/chrome/browser/resources/print_preview/data/destination_match.ts b/chrome/browser/resources/print_preview/data/destination_match.ts
index af2c7938..cc1e0b5 100644
--- a/chrome/browser/resources/print_preview/data/destination_match.ts
+++ b/chrome/browser/resources/print_preview/data/destination_match.ts
@@ -31,7 +31,7 @@
 
 export function getPrinterTypeForDestination(
     destination: (Destination|RecentDestination)): PrinterType {
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   if (destination.id === GooglePromotedDestinationId.SAVE_TO_DRIVE_CROS) {
     return PrinterType.PDF_PRINTER;
   }
@@ -102,7 +102,7 @@
    *     destination selection.
    */
   private isVirtualDestination_(destination: Destination): boolean {
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     if (destination.id === GooglePromotedDestinationId.SAVE_TO_DRIVE_CROS) {
       return true;
     }
diff --git a/chrome/browser/resources/print_preview/data/destination_store.ts b/chrome/browser/resources/print_preview/data/destination_store.ts
index 36a39c2d..1385d3ff 100644
--- a/chrome/browser/resources/print_preview/data/destination_store.ts
+++ b/chrome/browser/resources/print_preview/data/destination_store.ts
@@ -10,7 +10,7 @@
 import {CloudPrintInterface, CloudPrintInterfaceEventType, CloudPrintInterfacePrinterFailedDetail, CloudPrintInterfaceSearchDoneDetail} from '../cloud_print_interface.js';
 import {DestinationSearchBucket, MetricsContext, PrintPreviewInitializationEvents} from '../metrics.js';
 import {CapabilitiesResponse, NativeLayer, NativeLayerImpl} from '../native_layer.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {NativeLayerCros, NativeLayerCrosImpl, PrinterSetupResponse} from '../native_layer_cros.js';
 
 // </if>
@@ -150,7 +150,7 @@
   ERROR = 'DestinationStore.ERROR',
   SELECTED_DESTINATION_CAPABILITIES_READY = 'DestinationStore' +
       '.SELECTED_DESTINATION_CAPABILITIES_READY',
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   DESTINATION_EULA_READY = 'DestinationStore.DESTINATION_EULA_READY',
   // </if>
 }
@@ -209,7 +209,7 @@
    */
   private nativeLayer_: NativeLayer = NativeLayerImpl.getInstance();
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * Used to fetch information about Chrome OS local print destinations.
    */
@@ -276,7 +276,7 @@
     ]);
 
     this.platformOrigin_ = DestinationOrigin.LOCAL;
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     this.platformOrigin_ = DestinationOrigin.CROS;
     // </if>
 
@@ -346,7 +346,7 @@
    */
   init(
       pdfPrinterDisabled: boolean,
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       isDriveMounted: boolean,
       // </if>
       // <if expr="not chromeos and not lacros">
@@ -381,7 +381,7 @@
 
     this.pdfPrinterEnabled_ = !pdfPrinterDisabled;
     this.createLocalPdfPrintDestination_();
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     if (isDriveMounted) {
       this.createLocalDrivePrintDestination_();
     }
@@ -532,7 +532,7 @@
   }
 
   private isDestinationLocal_(destinationId: string|null): boolean {
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     if (destinationId === GooglePromotedDestinationId.SAVE_TO_DRIVE_CROS) {
       return true;
     }
@@ -546,7 +546,7 @@
     this.tracker_.removeAll();
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * Attempts to find the EULA URL of the the destination ID.
    */
@@ -721,7 +721,7 @@
     }
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * Attempt to resolve the capabilities for a Chrome OS printer.
    */
@@ -861,7 +861,7 @@
     return this.destinationMap_.get(key);
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * Removes the provisional destination with ID |provisionalId| from
    * |destinationMap_| and |destinations_|.
@@ -991,7 +991,7 @@
     }
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * Creates a local Drive print destination.
    */
@@ -1061,7 +1061,7 @@
       }
       dest.capabilities = settingsInfo.capabilities;
       this.updateDestination_(dest);
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       // Start the fetch for the PPD EULA URL.
       this.fetchEulaUrl(dest.id);
       // </if>
diff --git a/chrome/browser/resources/print_preview/data/model.ts b/chrome/browser/resources/print_preview/data/model.ts
index e01676d..fe2ca96 100644
--- a/chrome/browser/resources/print_preview/data/model.ts
+++ b/chrome/browser/resources/print_preview/data/model.ts
@@ -8,7 +8,7 @@
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {BackgroundGraphicsModeRestriction, ColorModeRestriction, DuplexModeRestriction, Policies} from '../native_layer.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {PinModeRestriction} from '../native_layer.js';
 // </if>
 import {CapabilityWithReset, Cdd, CddCapabilities, ColorOption, DpiOption, DuplexOption, MediaSizeOption, VendorCapability} from './cdd.js';
@@ -58,7 +58,7 @@
   otherOptions: Setting,
   ranges: Setting,
   pagesPerSheet: Setting,
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   pin: Setting,
   pinValue: Setting,
   // </if>
@@ -82,7 +82,7 @@
   scalingType?: ScalingType,
   scalingTypePdf?: ScalingType,
   vendorOptions?: object,
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   isPinEnabled?: boolean,
   pinValue?: string,
   // </if>
@@ -222,7 +222,7 @@
   'scalingTypePdf',
   'vendorItems',
 ];
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 STICKY_SETTING_NAMES.push('pin', 'pinValue');
 // </if>
 
@@ -493,7 +493,7 @@
               key: 'recentDestinations',
               updatesPreview: false,
             },
-            // <if expr="chromeos or lacros">
+            // <if expr="chromeos_ash or chromeos_lacros">
             pin: {
               value: false,
               unavailableValue: false,
@@ -739,7 +739,7 @@
     this.setSettingPath_(
         'vendorItems.available', !!caps && !!caps.vendor_capability);
 
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     const pinSupported = !!caps && !!caps.pin && !!caps.pin.supported &&
         loadTimeData.getBoolean('isEnterpriseManaged');
     this.set('settings.pin.available', pinSupported);
@@ -1068,7 +1068,7 @@
       return d.origin !== DestinationOrigin.PRIVET;
     });
 
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     // Remove Cloud Print Drive destination. The Chrome OS version will always
     // be shown in the dropdown and is still supported.
     recentDestinations = recentDestinations.filter((d: RecentDestination) => {
@@ -1205,7 +1205,7 @@
       const allowedMode = policiesObject[settingName].allowedMode;
       this.configurePolicySetting_(settingName, allowedMode, defaultMode);
     });
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     if (policiesObject['sheets']) {
       if (!this.policySettings_) {
         this.policySettings_ = {};
@@ -1300,7 +1300,7 @@
       for (const [settingName, policy] of Object.entries(
                this.policySettings_)) {
         const policyEntry = policy as PolicyEntry;
-        // <if expr="chromeos or lacros">
+        // <if expr="chromeos_ash or chromeos_lacros">
         if (settingName === 'sheets') {
           this.maxSheets = policyEntry.value;
           continue;
@@ -1472,7 +1472,7 @@
 
   private updateManaged_() {
     let managedSettings = ['cssBackground', 'headerFooter'];
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     managedSettings =
         managedSettings.concat(['color', 'duplex', 'duplexShortEdge', 'pin']);
     // </if>
@@ -1565,7 +1565,7 @@
       pageHeight: this.pageSize.height,
       showSystemDialog: showSystemDialog,
     };
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     ticket['printToGoogleDrive'] = ticket['printToGoogleDrive'] ||
         destination.id === GooglePromotedDestinationId.SAVE_TO_DRIVE_CROS;
     // </if>
@@ -1590,7 +1590,7 @@
       ticket['capabilities'] = JSON.stringify(destination.capabilities);
     }
 
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     if (this.getSettingValue('pin')) {
       ticket['pinValue'] = this.getSettingValue('pinValue');
     }
diff --git a/chrome/browser/resources/print_preview/native_layer.ts b/chrome/browser/resources/print_preview/native_layer.ts
index b4de7e8..f4da40fe 100644
--- a/chrome/browser/resources/print_preview/native_layer.ts
+++ b/chrome/browser/resources/print_preview/native_layer.ts
@@ -46,7 +46,7 @@
   DUPLEX = 0x6,
 }
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 /**
  * Enumeration of PIN printing mode restrictions used by Chromium.
  * This has to coincide with |printing::PinModeRestriction| as defined in
@@ -78,7 +78,7 @@
     allowedMode?: DuplexModeRestriction,
     defaultMode?: DuplexModeRestriction
   },
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   pin?: {allowedMode?: PinModeRestriction, defaultMode?: PinModeRestriction},
   // </if>
   printPdfAsImage?: {defaultMode?: boolean},
diff --git a/chrome/browser/resources/print_preview/print_preview.ts b/chrome/browser/resources/print_preview/print_preview.ts
index 09693e743..cd32eec0 100644
--- a/chrome/browser/resources/print_preview/print_preview.ts
+++ b/chrome/browser/resources/print_preview/print_preview.ts
@@ -10,7 +10,7 @@
 export {CloudPrintInterfaceImpl} from './cloud_print_interface_impl.js';
 export {Cdd, MediaSizeCapability, MediaSizeOption, VendorCapabilityValueType} from './data/cdd.js';
 export {ColorMode, createDestinationKey, Destination, DestinationCertificateStatus, DestinationConnectionStatus, DestinationOrigin, DestinationType, GooglePromotedDestinationId, makeRecentDestination, RecentDestination} from './data/destination.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 export {SAVE_TO_DRIVE_CROS_DESTINATION_KEY} from './data/destination.js';
 // </if>
 export {PrinterType} from './data/destination_match.js';
@@ -20,10 +20,10 @@
 export {CustomMarginsOrientation, Margins, MarginsSetting, MarginsType} from './data/margins.js';
 export {MeasurementSystem, MeasurementSystemUnitType} from './data/measurement_system.js';
 export {DuplexMode, DuplexType, getInstance, PolicyObjectEntry, PrintPreviewModelElement, PrintTicket, SerializedSettings, Setting, whenReady} from './data/model.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 export {PrintServerStore, PrintServerStoreEventType} from './data/print_server_store.js';
 // </if>
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 export {PrinterState, PrinterStatus, PrinterStatusReason, PrinterStatusSeverity} from './data/printer_status_cros.js';
 // </if>
 export {ScalingType} from './data/scaling.js';
@@ -31,7 +31,7 @@
 export {Error, State} from './data/state.js';
 export {PrintPreviewUserManagerElement} from './data/user_manager.js';
 export {BackgroundGraphicsModeRestriction, CapabilitiesResponse, ColorModeRestriction, DuplexModeRestriction, NativeInitialSettings, NativeLayer, NativeLayerImpl} from './native_layer.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 export {PinModeRestriction} from './native_layer.js';
 export {NativeLayerCros, NativeLayerCrosImpl, PrinterSetupResponse, PrintServersConfig} from './native_layer_cros.js';
 // </if>
@@ -46,7 +46,7 @@
 // <if expr="not chromeos and not lacros">
 export {PrintPreviewDestinationDialogElement} from './ui/destination_dialog.js';
 // </if>
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 export {PrintPreviewDestinationDialogCrosElement} from './ui/destination_dialog_cros.js';
 export {PrintPreviewDestinationDropdownCrosElement} from './ui/destination_dropdown_cros.js';
 // </if>
@@ -55,7 +55,7 @@
 // <if expr="not chromeos and not lacros">
 export {PrintPreviewDestinationSelectElement} from './ui/destination_select.js';
 // </if>
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 export {PrintPreviewDestinationSelectCrosElement} from './ui/destination_select_cros.js';
 // </if>
 export {DestinationState, NUM_PERSISTED_DESTINATIONS, PrintPreviewDestinationSettingsElement} from './ui/destination_settings.js';
@@ -74,7 +74,7 @@
 export {PrintPreviewOtherOptionsSettingsElement} from './ui/other_options_settings.js';
 export {PrintPreviewPagesPerSheetSettingsElement} from './ui/pages_per_sheet_settings.js';
 export {PagesValue, PrintPreviewPagesSettingsElement} from './ui/pages_settings.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 export {PrintPreviewPinSettingsElement} from './ui/pin_settings.js';
 // </if>
 export {PluginProxy, PluginProxyImpl, ViewportChangedCallback} from './ui/plugin_proxy.js';
diff --git a/chrome/browser/resources/print_preview/ui/app.ts b/chrome/browser/resources/print_preview/ui/app.ts
index fce9b41f..0b367d2a 100644
--- a/chrome/browser/resources/print_preview/ui/app.ts
+++ b/chrome/browser/resources/print_preview/ui/app.ts
@@ -30,7 +30,7 @@
 import {Error, PrintPreviewStateElement, State} from '../data/state.js';
 import {MetricsContext, PrintPreviewInitializationEvents} from '../metrics.js';
 import {NativeInitialSettings, NativeLayer, NativeLayerImpl} from '../native_layer.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {NativeLayerCros, NativeLayerCrosImpl} from '../native_layer_cros.js';
 
 // </if>
@@ -138,7 +138,7 @@
   private maxSheets_: number;
 
   private nativeLayer_: NativeLayer|null = null;
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private nativeLayerCros_: NativeLayerCros|null = null;
   // </if>
   private tracker_: EventTracker = new EventTracker();
@@ -174,7 +174,7 @@
 
     document.documentElement.classList.remove('loading');
     this.nativeLayer_ = NativeLayerImpl.getInstance();
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     this.nativeLayerCros_ = NativeLayerCrosImpl.getInstance();
     // </if>
     this.addWebUIListener('cr-dialog-open', this.onCrDialogOpen_.bind(this));
@@ -228,7 +228,7 @@
         e.preventDefault();
       }
 
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       if (this.destination_ &&
           this.destination_.origin === DestinationOrigin.CROS) {
         this.nativeLayerCros_!.recordPrinterStatusHistogram(
@@ -392,7 +392,7 @@
         break;
       case DestinationState.ERROR:
         let newState = State.ERROR;
-        // <if expr="chromeos or lacros">
+        // <if expr="chromeos_ash or chromeos_lacros">
         if (this.error_ === Error.NO_DESTINATIONS) {
           newState = State.FATAL_ERROR;
         }
@@ -469,7 +469,7 @@
       this.printRequested_ = true;
       return;
     }
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     if (this.destination_ &&
         this.destination_.origin === DestinationOrigin.CROS) {
       this.nativeLayerCros_!.recordPrinterStatusHistogram(
@@ -481,7 +481,7 @@
   }
 
   private onCancelRequested_() {
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     if (this.destination_ &&
         this.destination_.origin === DestinationOrigin.CROS) {
       this.nativeLayerCros_!.recordPrinterStatusHistogram(
diff --git a/chrome/browser/resources/print_preview/ui/button_strip.html b/chrome/browser/resources/print_preview/ui/button_strip.html
index 9271326..88448ff 100644
--- a/chrome/browser/resources/print_preview/ui/button_strip.html
+++ b/chrome/browser/resources/print_preview/ui/button_strip.html
@@ -23,7 +23,7 @@
     margin-inline-end: 0;
   }
 
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
   .error-message {
     color: red;
     margin: 16px 16px 0;
@@ -31,7 +31,7 @@
   }
 </if>
 </style>
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
 <div class="error-message"
     hidden="[[!showSheetsError_(destination.id, maxSheets, sheetCount)]]">
   [[errorMessage_]]
diff --git a/chrome/browser/resources/print_preview/ui/button_strip.ts b/chrome/browser/resources/print_preview/ui/button_strip.ts
index 71287b5..ae1dc74 100644
--- a/chrome/browser/resources/print_preview/ui/button_strip.ts
+++ b/chrome/browser/resources/print_preview/ui/button_strip.ts
@@ -51,7 +51,7 @@
         },
       },
 
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       errorMessage_: {
         type: String,
         observer: 'errorMessageChanged_',
@@ -64,7 +64,7 @@
     return [
       'updatePrintButtonLabel_(destination.id)',
       'updatePrintButtonEnabled_(state, destination.id, maxSheets, sheetCount)',
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       'updateErrorMessage_(state, destination.id, maxSheets, sheetCount)',
       // </if>
 
@@ -78,7 +78,7 @@
   state: State;
   private printButtonEnabled_: boolean;
   private printButtonLabel_: string;
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private errorMessage_: string;
   // </if>
 
@@ -115,7 +115,7 @@
         this.printButtonEnabled_ = false;
         break;
       case (State.READY):
-        // <if expr="chromeos or lacros">
+        // <if expr="chromeos_ash or chromeos_lacros">
         this.printButtonEnabled_ = !this.printButtonDisabled_();
         // </if>
         // <if expr="not chromeos and not lacros">
@@ -135,7 +135,7 @@
     this.lastState_ = this.state;
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * @return Whether to disable "Print" button because of sheets limit policy.
    */
diff --git a/chrome/browser/resources/print_preview/ui/destination_list_item.html b/chrome/browser/resources/print_preview/ui/destination_list_item.html
index d2e430b9..12a14d3 100644
--- a/chrome/browser/resources/print_preview/ui/destination_list_item.html
+++ b/chrome/browser/resources/print_preview/ui/destination_list_item.html
@@ -73,7 +73,7 @@
     opacity: 0.4;
   }
 
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
   :host([is-destination-cros-local_]) .connection-status {
     color: var(--google-red-600);
   }
@@ -100,7 +100,7 @@
   <span class="extension-icon" role="button" tabindex="0"
       title="[[getExtensionPrinterTooltip_(destination)]]"></span>
 </span>
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
 <span class="configuring-in-progress-text"
   hidden$="[[!checkConfigurationStatus_(statusEnum_.IN_PROGRESS,
                                         configurationStatus_)]]">
diff --git a/chrome/browser/resources/print_preview/ui/destination_list_item.ts b/chrome/browser/resources/print_preview/ui/destination_list_item.ts
index 550be33..ece2317 100644
--- a/chrome/browser/resources/print_preview/ui/destination_list_item.ts
+++ b/chrome/browser/resources/print_preview/ui/destination_list_item.ts
@@ -17,13 +17,13 @@
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {Destination, DestinationOrigin} from '../data/destination.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {ERROR_STRING_KEY_MAP, getPrinterStatusIcon, PrinterStatusReason} from '../data/printer_status_cros.js';
 // </if>
 
 import {updateHighlights} from './highlight_utils.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 enum DestinationConfigStatus {
   IDLE = 0,
   IN_PROGRESS = 1,
@@ -69,7 +69,7 @@
             'configurationStatus_)',
       },
 
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       isDestinationCrosLocal_: {
         type: Boolean,
         computed: 'computeIsDestinationCrosLocal_(destination)',
@@ -98,7 +98,7 @@
           'destination.displayName, destination.isOfflineOrInvalid, ' +
           'destination.isExtension)',
       'updateHighlightsAndHint_(destination, searchQuery)',
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       'requestPrinterStatus_(destination.key)',
       // </if>
     ];
@@ -111,7 +111,7 @@
   private searchHint_: string;
   private statusText_: string;
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private isDestinationCrosLocal_: boolean;
   private configurationStatus_: DestinationConfigStatus;
   // </if>
@@ -132,7 +132,7 @@
     }
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * Called if the printer configuration request is accepted. Show the waiting
    * message to the user as the configuration might take longer than expected.
@@ -196,7 +196,7 @@
       return '';
     }
 
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     if (this.destination.origin === DestinationOrigin.CROS) {
       // Don't show status text when destination is configuring.
       if (this.configurationStatus_ !== DestinationConfigStatus.IDLE) {
@@ -225,7 +225,7 @@
       return '';
     }
 
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     if (this.destination.origin === DestinationOrigin.CROS) {
       return getPrinterStatusIcon(
           this.destination.printerStatusReason,
@@ -236,7 +236,7 @@
     return this.destination.icon;
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * True when the destination is a CrOS local printer.
    */
diff --git a/chrome/browser/resources/print_preview/ui/destination_settings.html b/chrome/browser/resources/print_preview/ui/destination_settings.html
index 2afa6a5..013f62e 100644
--- a/chrome/browser/resources/print_preview/ui/destination_settings.html
+++ b/chrome/browser/resources/print_preview/ui/destination_settings.html
@@ -1,5 +1,5 @@
 <style include="print-preview-shared">
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
   :host([has-pin-setting_]) {
     margin-bottom: 0 !important;
   }
@@ -35,7 +35,7 @@
     </template>
   </cr-lazy-render>
 </if>
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
   <print-preview-destination-select-cros id="destinationSelect"
       active-user="[[activeUser_]]" dark="[[dark]]"
       destination="[[destination]]"
diff --git a/chrome/browser/resources/print_preview/ui/destination_settings.ts b/chrome/browser/resources/print_preview/ui/destination_settings.ts
index fa85c62..3ed6181a 100644
--- a/chrome/browser/resources/print_preview/ui/destination_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/destination_settings.ts
@@ -8,13 +8,13 @@
 // <if expr="not chromeos and not lacros">
 import './destination_dialog.js';
 // </if>
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import './destination_dialog_cros.js';
 // </if>
 // <if expr="not chromeos and not lacros">
 import './destination_select.js';
 // </if>
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import './destination_select_cros.js';
 // </if>
 import './print_preview_shared_css.js';
@@ -34,7 +34,7 @@
 
 import {CloudPrintInterfaceImpl} from '../cloud_print_interface_impl.js';
 import {CloudOrigins, createDestinationKey, createRecentDestinationKey, Destination, DestinationOrigin, GooglePromotedDestinationId, makeRecentDestination, RecentDestination} from '../data/destination.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {SAVE_TO_DRIVE_CROS_DESTINATION_KEY} from '../data/destination.js';
 // </if>
 import {getPrinterTypeForDestination, PrinterType} from '../data/destination_match.js';
@@ -44,13 +44,13 @@
 // <if expr="not chromeos and not lacros">
 import {PrintPreviewDestinationDialogElement} from './destination_dialog.js';
 // </if>
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {PrintPreviewDestinationDialogCrosElement} from './destination_dialog_cros.js';
 // </if>
 // <if expr="not chromeos and not lacros">
 import {PrintPreviewDestinationSelectElement} from './destination_select.js';
 // </if>
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {PrintPreviewDestinationSelectCrosElement} from './destination_select_cros.js';
 // </if>
 import {SettingsMixin} from './settings_mixin.js';
@@ -67,7 +67,7 @@
 // <if expr="not chromeos and not lacros">
 export const NUM_PERSISTED_DESTINATIONS: number = 5;
 // </if>
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 export const NUM_PERSISTED_DESTINATIONS: number = 10;
 // </if>
 
@@ -84,7 +84,7 @@
         CrLazyRenderElement<PrintPreviewDestinationDialogElement>,
     destinationSelect: PrintPreviewDestinationSelectElement,
     // </if>
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     destinationDialog:
         CrLazyRenderElement<PrintPreviewDestinationDialogCrosElement>,
     destinationSelect: PrintPreviewDestinationSelectCrosElement,
@@ -151,7 +151,7 @@
 
       displayedDestinations_: Array,
 
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       driveDestinationKey_: {
         type: String,
         value: '',
@@ -197,7 +197,7 @@
   private destinationStore_: DestinationStore|null;
   private displayedDestinations_: Destination[];
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private driveDestinationKey_: string;
   private hasPinSetting_: boolean;
   // </if>
@@ -236,7 +236,7 @@
         DestinationStoreEventType.DESTINATIONS_INSERTED,
         this.updateDropdownDestinations_.bind(this));
 
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     this.tracker_.add(
         this.destinationStore_,
         DestinationStoreEventType.DESTINATION_EULA_READY,
@@ -309,7 +309,7 @@
     this.pdfPrinterDisabled_ = pdfPrinterDisabled;
     let recentDestinations =
         this.getSettingValue('recentDestinations') as RecentDestination[];
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     this.driveDestinationKey_ =
         isDriveMounted ? SAVE_TO_DRIVE_CROS_DESTINATION_KEY : '';
     // </if>
@@ -427,7 +427,7 @@
    * @return Whether the destination is Save as PDF or Save to Drive.
    */
   private destinationIsDriveOrPdf_(destination: RecentDestination): boolean {
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     if (destination.id === GooglePromotedDestinationId.SAVE_TO_DRIVE_CROS) {
       return true;
     }
@@ -537,7 +537,7 @@
               PrinterType.PDF_PRINTER));
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private computeHasPinSetting_(): boolean {
     return this.getSetting('pin').available;
   }
@@ -603,7 +603,7 @@
     return assert(this.destinationStore_!);
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * @param e Event containing the current destination's EULA URL.
    */
diff --git a/chrome/browser/resources/print_preview/ui/icons.html b/chrome/browser/resources/print_preview/ui/icons.html
index 2aad0a4..0ac6957 100644
--- a/chrome/browser/resources/print_preview/ui/icons.html
+++ b/chrome/browser/resources/print_preview/ui/icons.html
@@ -21,7 +21,7 @@
         <path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-8.5 11l-1.1-2.14 2.84-4.96 1.5 2.66L12.25 17h-.75zm6.8 0h-5.55l1.4-2.5h5.11l.26.46L18.3 17zm-4.55-8h2.39l2.84 5h-2.93l-2.56-4.54.26-.46z"></path>
       </g>
 
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <g id="printer-status-green">
         <path d="M19,8 C20.66,8 22,9.34 22,11 L22,11 L22.0008411,12.1834702 C20.9260374,10.5660653 19.0875152,9.5 17,9.5 C14.2041481,9.5 11.8549346,11.412286 11.1889599,14.0002575 L8,14 L8,19 L12.1267078,19.0009178 C12.7530956,19.8713157 13.6069102,20.5670952 14.6011413,21.0012461 L6,21 L6,17 L2,17 L2,11 C2,9.34 3.34,8 5,8 L5,8 Z M18,3 L18,7 L6,7 L6,3 L18,3 Z"></path>
         <circle fill="#188038" cx="17" cy="15.5" r="3.5"></circle>
diff --git a/chrome/browser/resources/print_preview/ui/preview_area.ts b/chrome/browser/resources/print_preview/ui/preview_area.ts
index d6d4e2b11..a2c6e4e 100644
--- a/chrome/browser/resources/print_preview/ui/preview_area.ts
+++ b/chrome/browser/resources/print_preview/ui/preview_area.ts
@@ -775,7 +775,7 @@
           substitutions: [],
           tags: ['BR'],
         });
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       case Error.NO_DESTINATIONS:
         return this.i18n('noDestinationsMessage');
       // </if>
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.html b/chrome/browser/resources/print_preview/ui/sidebar.html
index 6e86e6a..90824145 100644
--- a/chrome/browser/resources/print_preview/ui/sidebar.html
+++ b/chrome/browser/resources/print_preview/ui/sidebar.html
@@ -49,7 +49,7 @@
       disabled="[[controlsDisabled_]]"
       available class="settings-section">
   </print-preview-destination-settings>
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
   <print-preview-pin-settings state="[[state]]" settings="[[settings]]"
       disabled="[[controlsDisabled_]]"
       hidden$="[[!settings.pin.available]]" class="settings-section">
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.ts b/chrome/browser/resources/print_preview/ui/sidebar.ts
index 3c2e856..dac7363 100644
--- a/chrome/browser/resources/print_preview/ui/sidebar.ts
+++ b/chrome/browser/resources/print_preview/ui/sidebar.ts
@@ -20,7 +20,7 @@
 import './other_options_settings.js';
 import './pages_per_sheet_settings.js';
 import './pages_settings.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import './pin_settings.js';
 // </if>
 import './print_preview_vars_css.js';
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.html b/chrome/browser/resources/settings/appearance_page/appearance_page.html
index dc660ab..b2617b3 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_page.html
+++ b/chrome/browser/resources/settings/appearance_page/appearance_page.html
@@ -27,7 +27,7 @@
           <cr-link-row class="first" hidden="[[!pageVisibility.setTheme]]"
               label="$i18n{themes}" sub-label="[[themeSublabel_]]"
               on-click="openThemeUrl_" external></cr-link-row>
-<if expr="not is_linux or chromeos or lacros">
+<if expr="not is_linux or chromeos_ash or chromeos_lacros">
           <template is="dom-if" if="[[prefs.extensions.theme.id.value]]">
             <div class="separator"></div>
             <cr-button id="useDefault" on-click="onUseDefaultTap_">
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.ts b/chrome/browser/resources/settings/appearance_page/appearance_page.ts
index 60d2a6b5..95b8cd7 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_page.ts
+++ b/chrome/browser/resources/settings/appearance_page/appearance_page.ts
@@ -328,7 +328,7 @@
     // <if expr="is_linux and not chromeos and not lacros">
     i18nId = useSystemTheme ? 'systemTheme' : 'classicTheme';
     // </if>
-    // <if expr="not is_linux or chromeos or lacros">
+    // <if expr="not is_linux or chromeos_ash or chromeos_lacros">
     i18nId = 'chooseFromWebStore';
     // </if>
     this.themeSublabel_ = this.i18n(i18nId);
diff --git a/chrome/browser/resources/settings/autofill_page/password_check.html b/chrome/browser/resources/settings/autofill_page/password_check.html
index 7ead7d9..f4ef697 100644
--- a/chrome/browser/resources/settings/autofill_page/password_check.html
+++ b/chrome/browser/resources/settings/autofill_page/password_check.html
@@ -131,7 +131,7 @@
               clicked-change-password=
               "[[clickedChangePassword_(item, clickedChangePasswordIds_.size)]]"
               on-change-password-clicked="onChangePasswordClick_"
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
               token-request-manager="[[tokenRequestManager_]]"
 </if>
               on-already-changed-password-click="onAlreadyChangedClick_">
@@ -164,7 +164,7 @@
               clicked-change-password=
               "[[clickedChangePassword_(item, clickedChangePasswordIds_.size)]]"
               on-change-password-clicked="onChangePasswordClick_"
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
               token-request-manager="[[tokenRequestManager_]]"
 </if>
               on-already-changed-password-click="onAlreadyChangedClick_">
@@ -203,7 +203,7 @@
           on-close="onEditDisclaimerClosed_">
       </settings-password-edit-disclaimer-dialog>
     </template>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <template is="dom-if" if="[[showPasswordPromptDialog_]]" restamp>
         <settings-password-prompt-dialog on-token-obtained="onTokenObtained_"
             on-close="onPasswordPromptClosed_">
diff --git a/chrome/browser/resources/settings/autofill_page/password_check.ts b/chrome/browser/resources/settings/autofill_page/password_check.ts
index 5bed3aa..a844507 100644
--- a/chrome/browser/resources/settings/autofill_page/password_check.ts
+++ b/chrome/browser/resources/settings/autofill_page/password_check.ts
@@ -18,7 +18,7 @@
 import './password_check_edit_disclaimer_dialog.js';
 import './password_check_list_item.js';
 import './password_remove_confirmation_dialog.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import '../controls/password_prompt_dialog.js';
 
 // </if>
@@ -27,13 +27,13 @@
 import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {focusWithoutInk} from 'chrome://resources/js/cr/ui/focus_without_ink.m.js';
 import {I18nMixin, I18nMixinInterface} from 'chrome://resources/js/i18n_mixin.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 // </if>
 import {WebUIListenerMixin, WebUIListenerMixinInterface} from 'chrome://resources/js/web_ui_listener_mixin.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {loadTimeData} from '../i18n_setup.js';
 // </if>
 
@@ -42,7 +42,7 @@
 import {routes} from '../route.js';
 import {Route, RouteObserverMixin, RouteObserverMixinInterface, Router} from '../router.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {BlockingRequestManager} from './blocking_request_manager.js';
 // </if>
 
@@ -149,7 +149,7 @@
         value: new Set(),
       },
 
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       showPasswordPromptDialog_: Boolean,
       tokenRequestManager_: Object,
       // </if>
@@ -174,7 +174,7 @@
   private iconHaloClass_: string;
   private clickedChangePasswordIds_: Set<number>;
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private showPasswordPromptDialog_: boolean;
   private tokenRequestManager_: BlockingRequestManager;
   // </if>
@@ -212,7 +212,7 @@
   connectedCallback() {
     super.connectedCallback();
 
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     // If the user's account supports the password check, an auth token will be
     // required in order for them to view or export passwords. Otherwise there
     // is no additional security so |tokenRequestManager_| will immediately
@@ -250,7 +250,7 @@
     syncBrowserProxy.sendSyncPrefsChanged();
 
     // For non-ChromeOS, also check whether accounts are available.
-    // <if expr="not (chromeos or lacros)">
+    // <if expr="not (chromeos_ash or chromeos_lacros)">
     const storedAccountsChanged = (accounts: Array<StoredAccount>) =>
         this.storedAccounts_ = accounts;
     syncBrowserProxy.getStoredAccounts().then(storedAccountsChanged);
@@ -386,12 +386,12 @@
               this.showPasswordEditDialog_ = true;
             },
             _error => {
-              // <if expr="chromeos or lacros">
+              // <if expr="chromeos_ash or chromeos_lacros">
               // If no password was found, refresh auth token and retry.
               this.tokenRequestManager_.request(
                   () => this.onEditPasswordClick_());
               // </if>
-              // <if expr="not (chromeos or lacros)">
+              // <if expr="not (chromeos_ash or chromeos_lacros)">
               this.activePassword_ = null;
               this.onPasswordEditDialogClosed_();
               // </if>
@@ -742,7 +742,7 @@
     return this.clickedChangePasswordIds_.has(item.id);
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * Copied from passwords_section.js.
    * TODO(crbug.com/1074228): Extract to a separate behavior
diff --git a/chrome/browser/resources/settings/autofill_page/password_check_list_item.ts b/chrome/browser/resources/settings/autofill_page/password_check_list_item.ts
index 5b24b661..30439b0 100644
--- a/chrome/browser/resources/settings/autofill_page/password_check_list_item.ts
+++ b/chrome/browser/resources/settings/autofill_page/password_check_list_item.ts
@@ -20,7 +20,7 @@
 import {loadTimeData} from '../i18n_setup.js';
 import {OpenWindowProxyImpl} from '../open_window_proxy.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {BlockingRequestManager} from './blocking_request_manager.js';
 // </if>
 import {PasswordCheckInteraction, PasswordManagerImpl, PasswordManagerProxy} from './password_manager_proxy.js';
@@ -37,7 +37,7 @@
 
   static get properties() {
     return {
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       tokenRequestManager: Object,
       // </if>
 
@@ -73,7 +73,7 @@
     };
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   tokenRequestManager: BlockingRequestManager;
   // </if>
 
@@ -174,7 +174,7 @@
               this.set('item', insecureCredential);
             },
             _error => {
-              // <if expr="chromeos or lacros">
+              // <if expr="chromeos_ash or chromeos_lacros">
               // If no password was found, refresh auth token and retry.
               this.tokenRequestManager.request(() => this.showPassword());
               // </if>
diff --git a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.ts b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.ts
index 08f1acc..89c1213c 100644
--- a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.ts
+++ b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.ts
@@ -24,7 +24,7 @@
 import {I18nMixin} from 'chrome://resources/js/i18n_mixin.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {BlockingRequestManager} from './blocking_request_manager.js';
 // </if>
 import {MultiStorePasswordUiEntry} from './multi_store_password_ui_entry.js';
@@ -113,7 +113,7 @@
         value: () => [],
       },
 
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       /**
        * Used for authentication when switching from ADD to EDIT mode.
        */
@@ -194,7 +194,7 @@
   readonly storeOptionAccountValue: string;
   readonly storeOptionDeviceValue: string;
   savedPasswords: Array<MultiStorePasswordUiEntry>;
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   tokenRequestManager: BlockingRequestManager|null;
   // </if>
   private usernamesByOrigin_: Map<string, Set<string>>|null = null;
@@ -545,7 +545,7 @@
           .requestPlaintextPassword(
               id, chrome.passwordsPrivate.PlaintextReason.EDIT)
           .then(password => resolve(password), () => {
-            // <if expr="chromeos or lacros">
+            // <if expr="chromeos_ash or chromeos_lacros">
             // If no password was found, refresh auth token and retry.
             this.tokenRequestManager!.request(() => {
               this.requestPlaintextPasswordForEditing_(id).then(resolve);
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_export_dialog.ts b/chrome/browser/resources/settings/autofill_page/passwords_export_dialog.ts
index 67335c9..1060889 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_export_dialog.ts
+++ b/chrome/browser/resources/settings/autofill_page/passwords_export_dialog.ts
@@ -17,7 +17,7 @@
 import {I18nMixin} from 'chrome://resources/js/i18n_mixin.js';
 import {html, microTask, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {BlockingRequestManager} from './blocking_request_manager.js';
 // </if>
 import {PasswordManagerImpl, PasswordManagerProxy, PasswordsFileExportProgressListener} from './password_manager_proxy.js';
@@ -67,7 +67,7 @@
       showProgressDialog_: Boolean,
       showErrorDialog_: Boolean,
 
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       tokenRequestManager: Object
       // </if>
     };
@@ -85,7 +85,7 @@
   private delayedCompletionToken_: number|null;
   private delayedProgress_: chrome.passwordsPrivate.PasswordExportProgress|null;
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   tokenRequestManager: BlockingRequestManager;
   // </if>
 
@@ -205,10 +205,10 @@
   }
 
   private onExportTap_() {
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     this.tokenRequestManager.request(() => this.exportPasswords_());
     // </if>
-    // <if expr="not (chromeos or lacros)">
+    // <if expr="not (chromeos_ash or chromeos_lacros)">
     this.exportPasswords_();
     // </if>
   }
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.ts b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.ts
index ee5cd22..819fae3 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.ts
+++ b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.ts
@@ -29,7 +29,7 @@
 
 import {StoredAccount, SyncBrowserProxyImpl} from '../people_page/sync_browser_proxy.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {BlockingRequestManager} from './blocking_request_manager.js';
 // </if>
 import {MultiStorePasswordUiEntry} from './multi_store_password_ui_entry.js';
@@ -94,7 +94,7 @@
         value: false,
       },
 
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       tokenRequestManager: Object,
       // </if>
 
@@ -150,7 +150,7 @@
   isAccountStoreUser: boolean;
   allowMoveToAccountOption: boolean;
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   tokenRequestManager: BlockingRequestManager;
   // </if>
 
@@ -240,7 +240,7 @@
         .requestPlaintextPassword(
             this.activePassword_!.entry.getAnyId(), reason)
         .then(callback, _error => {
-          // <if expr="chromeos or lacros">
+          // <if expr="chromeos_ash or chromeos_lacros">
           // If no password was found, refresh auth token and retry.
           this.tokenRequestManager.request(() => {
             this.requestActivePlaintextPassword_(reason, callback);
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.html b/chrome/browser/resources/settings/autofill_page/passwords_section.html
index 2889aac..e7065c08f 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.html
@@ -178,7 +178,7 @@
     </div>
 
     <passwords-list-handler id="passwordsListHandler"
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
       token-request-manager="[[tokenRequestManager_]]"
 </if>
       is-account-store-user="[[isAccountStoreUser_]]"
@@ -237,7 +237,7 @@
               filter="[[passwordFilter_(filter)]]"
               rendered-item-count="{{shownPasswordsCount_::dom-change}}">
             <password-list-item entry="[[item]]"
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
                 token-request-manager="[[tokenRequestManager_]]"
 </if>
                >
@@ -264,7 +264,7 @@
       <password-edit-dialog on-close="onAddPasswordDialogClosed_"
           id="addPasswordDialog" account-email="[[profileEmail_]]"
           saved-passwords="[[savedPasswords]]"
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
           token-request-manager="[[tokenRequestManager_]]"
 </if>
           is-account-store-user="[[isAccountStoreUser_]]">
@@ -272,13 +272,13 @@
     </template>
     <template is="dom-if" if="[[showPasswordsExportDialog_]]" restamp>
       <passwords-export-dialog
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
         token-request-manager="[[tokenRequestManager_]]"
 </if>
         on-passwords-export-dialog-close="onPasswordsExportDialogClosed_">
       </passwords-export-dialog>
     </template>
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
     <template is="dom-if" if="[[showPasswordPromptDialog_]]" restamp>
       <settings-password-prompt-dialog on-token-obtained="onTokenObtained_"
           on-close="onPasswordPromptClosed_">
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.ts b/chrome/browser/resources/settings/autofill_page/passwords_section.ts
index 3f6c820..ec4e8418 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.ts
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.ts
@@ -17,7 +17,7 @@
 import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
 import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 import '../controls/extension_controlled_indicator.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import '../controls/password_prompt_dialog.js';
 // </if>
 import '../controls/settings_toggle_button.js';
@@ -48,7 +48,7 @@
 import {routes} from '../route.js';
 import {Route, Router} from '../router.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {BlockingRequestManager} from './blocking_request_manager.js';
 // </if>
 import {MergeExceptionsStoreCopiesMixin} from './merge_exceptions_store_copies_mixin.js';
@@ -246,7 +246,7 @@
       syncPrefs_: Object,
       syncStatus_: Object,
 
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       showPasswordPromptDialog_: Boolean,
       tokenRequestManager_: Object,
       // </if>
@@ -291,7 +291,7 @@
   private syncPrefs_: SyncPrefs;
   private syncStatus_: SyncStatus;
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private showPasswordPromptDialog_: boolean;
   private tokenRequestManager_: BlockingRequestManager;
   // </if>
@@ -349,7 +349,7 @@
     this.setIsOptedInForAccountStorageListener_ =
         setIsOptedInForAccountStorageListener;
 
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     // If the user's account supports the password check, an auth token will be
     // required in order for them to view or export passwords. Otherwise there
     // is no additional security so |tokenRequestManager_| will immediately
@@ -383,7 +383,7 @@
     syncBrowserProxy.sendSyncPrefsChanged();
 
     // For non-ChromeOS, non-Lacros, also check whether accounts are available.
-    // <if expr="not (chromeos or lacros)">
+    // <if expr="not (chromeos_ash or chromeos_lacros)">
     const storedAccountsChanged = (accounts: Array<StoredAccount>) =>
         this.storedAccounts_ = accounts;
     syncBrowserProxy.getStoredAccounts().then(storedAccountsChanged);
@@ -515,7 +515,7 @@
     Router.getInstance().navigateTo(routes.DEVICE_PASSWORDS);
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * When this event fired, it means that the password-prompt-dialog succeeded
    * in creating a fresh token in the quickUnlockPrivate API. Because new tokens
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_shared_css.html b/chrome/browser/resources/settings/autofill_page/passwords_shared_css.html
index eac718ac..b92d364 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_shared_css.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_shared_css.html
@@ -64,7 +64,7 @@
          * necessary to prevent Chrome from using the operating system's font
          * instead of the Material Design font.
          * TODO(dbeam): why not font: inherit? */
-<if expr="chromeos or lacros or is_linux">
+<if expr="chromeos_ash or chromeos_lacros or is_linux">
         font-family: 'DejaVu Sans Mono', monospace;
 </if>
 <if expr="is_win">
diff --git a/chrome/browser/resources/settings/autofill_page/show_password_mixin.ts b/chrome/browser/resources/settings/autofill_page/show_password_mixin.ts
index 5542546..a9db16e 100644
--- a/chrome/browser/resources/settings/autofill_page/show_password_mixin.ts
+++ b/chrome/browser/resources/settings/autofill_page/show_password_mixin.ts
@@ -6,7 +6,7 @@
 
 import {loadTimeData} from '../i18n_setup.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {BlockingRequestManager} from './blocking_request_manager.js';
 // </if>
 import {MultiStorePasswordUiEntry} from './multi_store_password_ui_entry.js';
@@ -26,7 +26,7 @@
           return {
             entry: Object,
 
-            // <if expr="chromeos or lacros">
+            // <if expr="chromeos_ash or chromeos_lacros">
             tokenRequestManager: Object
             // </if>
           };
@@ -34,7 +34,7 @@
 
         entry: MultiStorePasswordUiEntry;
 
-        // <if expr="chromeos or lacros">
+        // <if expr="chromeos_ash or chromeos_lacros">
         tokenRequestManager: BlockingRequestManager;
         // </if>
 
@@ -78,7 +78,7 @@
                     this.set('entry.password', password);
                   },
                   _error => {
-                    // <if expr="chromeos or lacros">
+                    // <if expr="chromeos_ash or chromeos_lacros">
                     // If no password was found, refresh auth token and retry.
                     this.tokenRequestManager.request(
                         () => this.onShowPasswordButtonTap());
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html
index 21f042f..8fbfd6ec1 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -23,7 +23,7 @@
           <settings-reset-profile-banner on-close="onResetProfileBannerClosed_">
           </settings-reset-profile-banner>
         </template>
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
         <template is="dom-if" if="[[showOSSettingsBanner_]]">
           <div id="osSettingsBanner" class="cr-row first">
             <div class="flex cr-padded-text"
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.ts b/chrome/browser/resources/settings/basic_page/basic_page.ts
index 07bd17f1..8139f6e30 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.ts
+++ b/chrome/browser/resources/settings/basic_page/basic_page.ts
@@ -22,7 +22,7 @@
 import '../search_page/search_page.js';
 import '../settings_page/settings_section.js';
 import '../settings_page_css.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
 // </if>
 
@@ -37,7 +37,7 @@
 import {SettingsIdleLoadElement} from '../controls/settings_idle_load.js';
 import {loadTimeData} from '../i18n_setup.js';
 import {PageVisibility} from '../page_visibility.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {PrefsMixin, PrefsMixinInterface} from '../prefs/prefs_mixin.js';
 // </if>
 import {routes} from '../route.js';
@@ -45,7 +45,7 @@
 import {getSearchManager, SearchResult} from '../search_settings.js';
 import {MainPageMixin, MainPageMixinInterface} from '../settings_page/main_page_mixin.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 const OS_BANNER_INTERACTION_METRIC_NAME =
     'ChromeOS.Settings.OsBannerInteraction';
 
@@ -66,7 +66,7 @@
 // TypeScript.
 type Constructor<T> = new (...args: any[]) => T;
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 const SettingsBasicPageElementBase =
     PrefsMixin(MainPageMixin(
         RouteObserverMixin(PolymerElement) as unknown as
@@ -148,7 +148,7 @@
         },
       },
 
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       showOSSettingsBanner_: {
         type: Boolean,
         computed: 'computeShowOSSettingsBanner_(' +
@@ -175,7 +175,7 @@
   private hasExpandedSection_: boolean;
   private showResetProfileBanner_: boolean;
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private showOSSettingsBanner_: boolean;
   private osBannerShowMetricRecorded_: boolean = false;
   // </if>
@@ -276,7 +276,7 @@
     });
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private computeShowOSSettingsBanner_(): boolean|undefined {
     // this.prefs is implicitly used by this.getPref() below.
     if (!this.prefs || !this.currentRoute_) {
diff --git a/chrome/browser/resources/settings/lazy_load.ts b/chrome/browser/resources/settings/lazy_load.ts
index fa69f55..8e7ade3 100644
--- a/chrome/browser/resources/settings/lazy_load.ts
+++ b/chrome/browser/resources/settings/lazy_load.ts
@@ -71,7 +71,7 @@
 export {SettingsAddressRemoveConfirmationDialogElement} from './autofill_page/address_remove_confirmation_dialog.js';
 export {AutofillManagerImpl, AutofillManagerProxy, PersonalDataChangedListener} from './autofill_page/autofill_manager_proxy.js';
 export {SettingsAutofillSectionElement} from './autofill_page/autofill_section.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 export {BlockingRequestManager} from './autofill_page/blocking_request_manager.js';
 // </if>
 export {SettingsCreditCardEditDialogElement} from './autofill_page/credit_card_edit_dialog.js';
diff --git a/chrome/browser/resources/settings/page_visibility.ts b/chrome/browser/resources/settings/page_visibility.ts
index 76324d49..b21c9d00 100644
--- a/chrome/browser/resources/settings/page_visibility.ts
+++ b/chrome/browser/resources/settings/page_visibility.ts
@@ -43,7 +43,7 @@
 if (loadTimeData.getBoolean('isGuest')) {
   // "if not chromeos" and "if chromeos" in two completely separate blocks
   // to work around closure compiler.
-  // <if expr="not (chromeos or lacros)">
+  // <if expr="not (chromeos_ash or chromeos_lacros)">
   pageVisibility = {
     autofill: false,
     people: false,
@@ -58,7 +58,7 @@
     languages: false,
   };
   // </if>
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   pageVisibility = {
     autofill: false,
     people: false,
diff --git a/chrome/browser/resources/settings/people_page/sync_controls.html b/chrome/browser/resources/settings/people_page/sync_controls.html
index eae309d..3ed9c2c 100644
--- a/chrome/browser/resources/settings/people_page/sync_controls.html
+++ b/chrome/browser/resources/settings/people_page/sync_controls.html
@@ -12,7 +12,7 @@
       }
     </style>
 
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
     <!-- TODO(crbug.com/1231500): Remove this warning after Lacros side-by-side
       rollout stage. -->
     <template is="dom-if" if="[[shouldShowLacrosSideBySideWarning_()]]">
diff --git a/chrome/browser/resources/settings/people_page/sync_controls.ts b/chrome/browser/resources/settings/people_page/sync_controls.ts
index 041a0e8..0176a54 100644
--- a/chrome/browser/resources/settings/people_page/sync_controls.ts
+++ b/chrome/browser/resources/settings/people_page/sync_controls.ts
@@ -15,7 +15,7 @@
 import {WebUIListenerMixin} from '//resources/js/web_ui_listener_mixin.js';
 import {html, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {loadTimeData} from '../i18n_setup.js';
 // </if>
 
@@ -104,7 +104,7 @@
   }
 
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private shouldShowLacrosSideBySideWarning_(): boolean {
     return loadTimeData.getBoolean('shouldShowLacrosSideBySideWarning');
   }
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html
index 4e446234..b4bb9aa 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.html
+++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -83,7 +83,7 @@
       </div>
     </div>
 
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
     <!-- TODO(crbug.com/1231500): Remove this warning after Lacros side-by-side
       rollout stage. -->
     <template is="dom-if" if="[[shouldShowLacrosSideBySideWarning_()]]">
diff --git a/chrome/browser/resources/settings/people_page/sync_page.ts b/chrome/browser/resources/settings/people_page/sync_page.ts
index dffb21e2..7da1192 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.ts
+++ b/chrome/browser/resources/settings/people_page/sync_page.ts
@@ -319,7 +319,7 @@
   }
   // </if>
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private shouldShowLacrosSideBySideWarning_(): boolean {
     return loadTimeData.getBoolean('shouldShowLacrosSideBySideWarning');
   }
diff --git a/chrome/browser/resources/settings/privacy_page/security_page.html b/chrome/browser/resources/settings/privacy_page/security_page.html
index 534f587..b3da3f2 100644
--- a/chrome/browser/resources/settings/privacy_page/security_page.html
+++ b/chrome/browser/resources/settings/privacy_page/security_page.html
@@ -166,7 +166,7 @@
     <template is="dom-if" if="[[showSecureDnsSetting_]]">
       <settings-secure-dns prefs="{{prefs}}"></settings-secure-dns>
     </template>
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
     <template is="dom-if" if="[[showSecureDnsSettingLink_]]">
         <cr-link-row id="openChromeOSSecureDnsSettings"
             on-click="onOpenChromeOSSecureDnsSettingsClicked_"
diff --git a/chrome/browser/resources/settings/privacy_page/security_page.ts b/chrome/browser/resources/settings/privacy_page/security_page.ts
index 64efbc4..e72520a9 100644
--- a/chrome/browser/resources/settings/privacy_page/security_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/security_page.ts
@@ -24,7 +24,7 @@
 import {loadTimeData} from '../i18n_setup.js';
 import {MetricsBrowserProxy, MetricsBrowserProxyImpl, PrivacyElementInteractions, SafeBrowsingInteractions} from '../metrics_browser_proxy.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {OpenWindowProxyImpl} from '../open_window_proxy.js';
 // </if>
 
@@ -107,7 +107,7 @@
         },
       },
 
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       /**
        * Whether a link to secure DNS OS setting should be displayed.
        */
@@ -148,7 +148,7 @@
   private showHttpsOnlyModeSetting_: boolean;
   private showSecureDnsSetting_: boolean;
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private showSecureDnsSettingLink_: boolean;
   // </if>
 
@@ -321,7 +321,7 @@
     this.recordActionOnExpandButtonClicked_(SafeBrowsingSetting.STANDARD);
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private onOpenChromeOSSecureDnsSettingsClicked_() {
     const path =
         loadTimeData.getString('chromeOSPrivacyAndSecuritySectionPath');
diff --git a/chrome/browser/resources/settings/settings.ts b/chrome/browser/resources/settings/settings.ts
index b0ec606..39bfd6e5 100644
--- a/chrome/browser/resources/settings/settings.ts
+++ b/chrome/browser/resources/settings/settings.ts
@@ -26,6 +26,7 @@
 export {MultiStoreExceptionEntry} from './autofill_page/multi_store_exception_entry.js';
 export {MultiStorePasswordUiEntry} from './autofill_page/multi_store_password_ui_entry.js';
 export {AccountStorageOptInStateChangedListener, CredentialsChangedListener, PasswordCheckInteraction, PasswordCheckReferrer, PasswordCheckStatusChangedListener, PasswordExceptionListChangedListener, PasswordManagerImpl, PasswordManagerProxy, PasswordsFileExportProgressListener, SavedPasswordListChangedListener} from './autofill_page/password_manager_proxy.js';
+export {BaseMixin} from './base_mixin.js';
 export {SettingsBasicPageElement} from './basic_page/basic_page.js';
 export {ControlledRadioButtonElement} from './controls/controlled_radio_button.js';
 export {ExtensionControlledIndicatorElement} from './controls/extension_controlled_indicator.js';
@@ -81,5 +82,6 @@
 export {getSearchManager, SearchManager, SearchRequest, setSearchManagerForTesting} from './search_settings.js';
 export {SettingsMainElement} from './settings_main/settings_main.js';
 export {SettingsMenuElement} from './settings_menu/settings_menu.js';
+export {SettingsSectionElement} from './settings_page/settings_section.js';
 export {SettingsUiElement} from './settings_ui/settings_ui.js';
 export {SiteFaviconElement} from './site_favicon.js';
diff --git a/chrome/browser/resources/settings/settings_page/main_page_mixin.ts b/chrome/browser/resources/settings/settings_page/main_page_mixin.ts
index 3f644879..009aff2 100644
--- a/chrome/browser/resources/settings/settings_page/main_page_mixin.ts
+++ b/chrome/browser/resources/settings/settings_page/main_page_mixin.ts
@@ -419,5 +419,6 @@
     });
 
 export interface MainPageMixinInterface {
+  scroller: HTMLElement|null;
   containsRoute(route: Route|null): boolean;
 }
diff --git a/chrome/browser/resources/settings/settings_page/settings_animated_pages.ts b/chrome/browser/resources/settings/settings_page/settings_animated_pages.ts
index 92096574..7bc0f98 100644
--- a/chrome/browser/resources/settings/settings_page/settings_animated_pages.ts
+++ b/chrome/browser/resources/settings/settings_page/settings_animated_pages.ts
@@ -266,5 +266,11 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'settings-animated-pages': SettingsAnimatedPagesElement;
+  }
+}
+
 customElements.define(
     SettingsAnimatedPagesElement.is, SettingsAnimatedPagesElement);
diff --git a/chrome/browser/resources/settings/settings_page/settings_subpage.ts b/chrome/browser/resources/settings/settings_page/settings_subpage.ts
index 8f7e62c..51d6e51 100644
--- a/chrome/browser/resources/settings/settings_page/settings_subpage.ts
+++ b/chrome/browser/resources/settings/settings_page/settings_subpage.ts
@@ -290,4 +290,10 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'settings-subpage': SettingsSubpageElement;
+  }
+}
+
 customElements.define(SettingsSubpageElement.is, SettingsSubpageElement);
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.ts b/chrome/browser/resources/settings/settings_ui/settings_ui.ts
index 047bbc8..ac2d904 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.ts
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.ts
@@ -363,4 +363,10 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'settings-ui': SettingsUiElement;
+  }
+}
+
 customElements.define(SettingsUiElement.is, SettingsUiElement);
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index 3fe7e99d..4408423 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -197,7 +197,7 @@
 
 int64_t GetNavigationIDFromPrefsByOrigin(PrefService* prefs,
                                          const Origin& origin) {
-  const base::DictionaryValue* unhandled_sync_password_reuses =
+  const base::Value* unhandled_sync_password_reuses =
       prefs->GetDictionary(prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
   if (!unhandled_sync_password_reuses)
     return 0;
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
index e09fe4e2..68af3c8 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
@@ -1336,12 +1336,12 @@
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
 
-  const base::DictionaryValue* new_state =
+  const base::Value* new_state =
       profile->GetPrefs()->GetDictionary(prefs::kSafeBrowsingIncidentsSent);
   // The legacy value must be gone.
-  ASSERT_FALSE(new_state->HasKey(blocklist_load_type));
+  ASSERT_FALSE(new_state->FindKey(blocklist_load_type));
   // But other data must be untouched.
-  ASSERT_TRUE(new_state->HasKey(preference_type));
+  ASSERT_TRUE(new_state->FindKey(preference_type));
 }
 
 // Tests that an identical incident added after an incident is pruned and
diff --git a/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_browsertest_win.cc b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_browsertest_win.cc
index f4f7d5e4..57c45bf 100644
--- a/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_browsertest_win.cc
+++ b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_browsertest_win.cc
@@ -173,7 +173,7 @@
     LoadManifest(manifest, out_extension);
 
     // Ensure that the startup url seen in the prefs is same as |startup_url|.
-    const base::ListValue* url_list =
+    const base::Value* url_list =
         GetPrefs()->GetList(prefs::kURLsToRestoreOnStartup);
     ASSERT_EQ(url_list->GetList().size(), 1U);
     ASSERT_TRUE(url_list->GetList()[0].is_string());
diff --git a/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_unittest.cc b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_unittest.cc
index 4bec3a8..964671b 100644
--- a/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_unittest.cc
+++ b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_unittest.cc
@@ -53,8 +53,8 @@
 const char kStartupUrl2[] = "http://start2.com";
 const char kStartupUrl3[] = "http://start3.com";
 
-bool ListValueContainsUrl(const base::ListValue* list, const GURL& url) {
-  if (!list)
+bool ListValueContainsUrl(const base::Value* list, const GURL& url) {
+  if (!list || !list->is_list())
     return false;
 
   for (const base::Value& i : list->GetList()) {
diff --git a/chrome/browser/search/background/ntp_custom_background_service.cc b/chrome/browser/search/background/ntp_custom_background_service.cc
index 49a7dd6..6d9b19b3 100644
--- a/chrome/browser/search/background/ntp_custom_background_service.cc
+++ b/chrome/browser/search/background/ntp_custom_background_service.cc
@@ -255,7 +255,7 @@
 
 void NtpCustomBackgroundService::RefreshBackgroundIfNeeded() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  const base::DictionaryValue* background_info =
+  const base::Value* background_info =
       profile_->GetPrefs()->GetDictionary(prefs::kNtpCustomBackgroundDict);
   int64_t refresh_timestamp = 0;
   const base::Value* timestamp_value =
@@ -295,7 +295,7 @@
   // Attempt to get custom background URL from preferences.
   if (IsCustomBackgroundPrefValid()) {
     auto custom_background = absl::make_optional<CustomBackground>();
-    const base::DictionaryValue* background_info =
+    const base::Value* background_info =
         pref_service_->GetDictionary(prefs::kNtpCustomBackgroundDict);
     GURL custom_background_url(
         background_info->FindKey(kNtpCustomBackgroundURL)->GetString());
@@ -400,7 +400,7 @@
 
 bool NtpCustomBackgroundService::IsCustomBackgroundPrefValid() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  const base::DictionaryValue* background_info =
+  const base::Value* background_info =
       profile_->GetPrefs()->GetDictionary(prefs::kNtpCustomBackgroundDict);
   if (!background_info)
     return false;
diff --git a/chrome/browser/sessions/session_service_log.cc b/chrome/browser/sessions/session_service_log.cc
index 0fb58e1f..39851a8 100644
--- a/chrome/browser/sessions/session_service_log.cc
+++ b/chrome/browser/sessions/session_service_log.cc
@@ -176,7 +176,7 @@
 }  // namespace
 
 std::list<SessionServiceEvent> GetSessionServiceEvents(Profile* profile) {
-  const base::ListValue* serialized_events =
+  const base::Value* serialized_events =
       profile->GetPrefs()->GetList(kEventPrefKey);
   if (!serialized_events)
     return {};
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
index d132589..34928c2 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
@@ -233,11 +233,7 @@
      */
     void updateShareSheetForLinkToggle(LinkToggleMetricsDetails linkToggleMetricsDetails,
             @LinkGeneration int linkGenerationState) {
-        if (mLinkToTextCoordinator == null
-                && (!(ChromeFeatureList.isEnabled(ChromeFeatureList.SHARING_HUB_LINK_TOGGLE)
-                            || ChromeFeatureList.isEnabled(
-                                    ChromeFeatureList.UPCOMING_SHARING_FEATURES))
-                        || mShareSheetLinkToggleCoordinator == null)) {
+        if (mLinkToTextCoordinator == null && mShareSheetLinkToggleCoordinator == null) {
             return;
         }
 
@@ -572,11 +568,8 @@
             recordSharedHighlightingUsage();
         }
 
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.SHARING_HUB_LINK_TOGGLE)
-                || ChromeFeatureList.isEnabled(ChromeFeatureList.UPCOMING_SHARING_FEATURES)) {
-            ShareSheetLinkToggleMetricsHelper.recordLinkToggleSharedStateMetric(
-                    linkToggleMetricsDetails);
-        }
+        ShareSheetLinkToggleMetricsHelper.recordLinkToggleSharedStateMetric(
+                linkToggleMetricsDetails);
     }
 
     private static void recordTimeToShare(long shareStartTime) {
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetLinkToggleCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetLinkToggleCoordinator.java
index 1ebc1d4c..cf09f2c 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetLinkToggleCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetLinkToggleCoordinator.java
@@ -27,10 +27,6 @@
         int COUNT = 2;
     }
 
-    // SHARING_HUB_LINK_TOGGLE params
-    static final String IMAGE_ENABLED_BY_DEFAULT = "image_enabled";
-    static final String SCREENSHOT_ENABLED_BY_DEFAULT = "screenshot_enabled";
-
     private final LinkToTextCoordinator mLinkToTextCoordinator;
 
     private ShareParams mShareParams;
@@ -65,9 +61,7 @@
                 && chromeShareExtras.getDetailedContentType()
                         == DetailedContentType.HIGHLIGHTED_TEXT;
         mShouldEnableGenericToggle =
-                (ChromeFeatureList.isEnabled(ChromeFeatureList.SHARING_HUB_LINK_TOGGLE)
-                        || ChromeFeatureList.isEnabled(ChromeFeatureList.UPCOMING_SHARING_FEATURES))
-                && mChromeShareExtras.getDetailedContentType() != DetailedContentType.NOT_SPECIFIED
+                mChromeShareExtras.getDetailedContentType() != DetailedContentType.NOT_SPECIFIED
                 && mUrl != null && !mUrl.isEmpty();
     }
 
@@ -102,21 +96,8 @@
 
     boolean shouldEnableToggleByDefault() {
         int detailedContentType = mChromeShareExtras.getDetailedContentType();
-        if (detailedContentType == DetailedContentType.IMAGE
-                && ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
-                        ChromeFeatureList.SHARING_HUB_LINK_TOGGLE, IMAGE_ENABLED_BY_DEFAULT,
-                        false)) {
-            return true;
-        } else if (detailedContentType == DetailedContentType.SCREENSHOT
-                && ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
-                        ChromeFeatureList.SHARING_HUB_LINK_TOGGLE, SCREENSHOT_ENABLED_BY_DEFAULT,
-                        false)) {
-            return true;
-        } else if (detailedContentType == DetailedContentType.HIGHLIGHTED_TEXT
+        return detailedContentType == DetailedContentType.HIGHLIGHTED_TEXT
                 || detailedContentType == DetailedContentType.WEB_NOTES
-                || detailedContentType == DetailedContentType.LIGHTWEIGHT_REACTION) {
-            return true;
-        }
-        return false;
+                || detailedContentType == DetailedContentType.LIGHTWEIGHT_REACTION;
     }
 }
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
index c5cb8509..7765a6c0 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
@@ -69,7 +69,6 @@
  * Tests {@link ChromeProvidedSharingOptionsProvider}.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@Features.EnableFeatures({ChromeFeatureList.SHARING_HUB_LINK_TOGGLE})
 public class ChromeProvidedSharingOptionsProviderTest {
     @Rule
     public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
@@ -138,6 +137,7 @@
     @Test
     @MediumTest
     @Features.EnableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT})
+    @Features.DisableFeatures({ChromeFeatureList.LIGHTWEIGHT_REACTIONS})
     @DisabledTest(message = "https://crbug.com/1233184")
     public void getPropertyModels_screenshotEnabled() {
         setUpChromeProvidedSharingOptionsProviderTest(
@@ -163,9 +163,37 @@
 
     @Test
     @MediumTest
+    @Features.EnableFeatures({ChromeFeatureList.LIGHTWEIGHT_REACTIONS})
     @Features.DisableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT})
+    public void getPropertyModels_lightweightReactionsEnabled() {
+        setUpChromeProvidedSharingOptionsProviderTest(
+                /*printingEnabled=*/false, LinkGeneration.MAX);
+        List<PropertyModel> propertyModels =
+                mChromeProvidedSharingOptionsProvider.getPropertyModels(
+                        ShareSheetPropertyModelBuilder.ALL_CONTENT_TYPES_FOR_TEST,
+                        DetailedContentType.NOT_SPECIFIED,
+                        /*isMultiWindow=*/false);
+
+        assertCorrectModelsAreInTheRightOrder(propertyModels,
+                ImmutableList.of(
+                        mActivity.getResources().getString(R.string.sharing_webnotes_create_card),
+                        mActivity.getResources().getString(R.string.sharing_screenshot),
+                        mActivity.getResources().getString(R.string.sharing_lightweight_reactions),
+                        mActivity.getResources().getString(R.string.sharing_copy_image),
+                        mActivity.getResources().getString(R.string.sharing_copy),
+                        mActivity.getResources().getString(
+                                R.string.send_tab_to_self_share_activity_title),
+                        mActivity.getResources().getString(R.string.qr_code_share_icon_label),
+                        mActivity.getResources().getString(R.string.sharing_save_image)));
+    }
+
+    @Test
+    @MediumTest
+    @Features.DisableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT,
+            ChromeFeatureList.LIGHTWEIGHT_REACTIONS})
     @DisabledTest(message = "https://crbug.com/1233184")
-    public void getPropertyModels_printingEnabled_includesPrinting() {
+    public void
+    getPropertyModels_printingEnabled_includesPrinting() {
         setUpChromeProvidedSharingOptionsProviderTest(
                 /*printingEnabled=*/true, LinkGeneration.MAX);
         List<PropertyModel> propertyModels =
@@ -189,8 +217,10 @@
 
     @Test
     @MediumTest
-    @Features.DisableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT})
-    public void getPropertyModels_sharingHub15Enabled_includesCopyText() {
+    @Features.DisableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT,
+            ChromeFeatureList.LIGHTWEIGHT_REACTIONS})
+    public void
+    getPropertyModels_sharingHub15Enabled_includesCopyText() {
         setUpChromeProvidedSharingOptionsProviderTest(
                 /*printingEnabled=*/false, LinkGeneration.MAX);
         List<PropertyModel> propertyModels =
@@ -205,8 +235,10 @@
 
     @Test
     @MediumTest
-    @Features.DisableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT})
-    public void getPropertyModels_linkAndTextShare() {
+    @Features.DisableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT,
+            ChromeFeatureList.LIGHTWEIGHT_REACTIONS})
+    public void
+    getPropertyModels_linkAndTextShare() {
         setUpChromeProvidedSharingOptionsProviderTest(
                 /*printingEnabled=*/false, LinkGeneration.MAX);
 
@@ -226,8 +258,10 @@
 
     @Test
     @MediumTest
-    @Features.DisableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT})
-    public void getPropertyModels_linkShare() {
+    @Features.DisableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT,
+            ChromeFeatureList.LIGHTWEIGHT_REACTIONS})
+    public void
+    getPropertyModels_linkShare() {
         setUpChromeProvidedSharingOptionsProviderTest(
                 /*printingEnabled=*/false, LinkGeneration.MAX);
 
@@ -246,8 +280,10 @@
 
     @Test
     @MediumTest
-    @Features.DisableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT})
-    public void getPropertyModels_textShare() {
+    @Features.DisableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT,
+            ChromeFeatureList.LIGHTWEIGHT_REACTIONS})
+    public void
+    getPropertyModels_textShare() {
         setUpChromeProvidedSharingOptionsProviderTest(
                 /*printingEnabled=*/false, LinkGeneration.MAX);
 
@@ -287,8 +323,10 @@
 
     @Test
     @MediumTest
-    @Features.EnableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT})
-    public void getPropertyModels_filtersByContentType() {
+    @Features.EnableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT,
+            ChromeFeatureList.LIGHTWEIGHT_REACTIONS})
+    public void
+    getPropertyModels_filtersByContentType() {
         setUpChromeProvidedSharingOptionsProviderTest(
                 /*printingEnabled=*/true, LinkGeneration.MAX);
         List<PropertyModel> propertyModels =
@@ -306,8 +344,10 @@
 
     @Test
     @MediumTest
-    @Features.EnableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT})
-    public void getPropertyModels_multipleTypes_filtersByContentType() {
+    @Features.EnableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT,
+            ChromeFeatureList.LIGHTWEIGHT_REACTIONS})
+    public void
+    getPropertyModels_multipleTypes_filtersByContentType() {
         setUpChromeProvidedSharingOptionsProviderTest(
                 /*printingEnabled=*/true, LinkGeneration.MAX);
         List<PropertyModel> propertyModels =
@@ -324,6 +364,7 @@
                     mActivity.getResources().getString(R.string.sharing_long_screenshot));
         }
         expectedModels.addAll(ImmutableList.of(
+                mActivity.getResources().getString(R.string.sharing_lightweight_reactions),
                 mActivity.getResources().getString(R.string.sharing_copy_url),
                 mActivity.getResources().getString(R.string.sharing_copy_image),
                 mActivity.getResources().getString(R.string.send_tab_to_self_share_activity_title),
@@ -335,7 +376,8 @@
 
     @Test
     @MediumTest
-    public void getPropertyModels_nonLightweightReaction_doesNotFilterByDetailedContentType() {
+    @Features.DisableFeatures({ChromeFeatureList.LIGHTWEIGHT_REACTIONS})
+    public void getPropertyModels_nonLightweightReactions_doesNotFilterByDetailedContentType() {
         setUpChromeProvidedSharingOptionsProviderTest(
                 /*printingEnabled=*/true, LinkGeneration.MAX);
         List<PropertyModel> propertyModels =
@@ -360,7 +402,8 @@
 
     @Test
     @MediumTest
-    public void getPropertyModels_lightweightReaction_filtersByDetailedContentType() {
+    @Features.EnableFeatures({ChromeFeatureList.LIGHTWEIGHT_REACTIONS})
+    public void getPropertyModels_lightweightReactions_filtersByDetailedContentType() {
         setUpChromeProvidedSharingOptionsProviderTest(
                 /*printingEnabled=*/true, LinkGeneration.MAX);
         List<PropertyModel> propertyModels =
@@ -378,8 +421,10 @@
     @Test
     @MediumTest
     @Features.EnableFeatures({ChromeFeatureList.PREEMPTIVE_LINK_TO_TEXT_GENERATION})
-    @Features.DisableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT})
-    public void getShareDetailsMetrics_LinkGeneration() {
+    @Features.DisableFeatures({ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT,
+            ChromeFeatureList.LIGHTWEIGHT_REACTIONS})
+    public void
+    getShareDetailsMetrics_LinkGeneration() {
         @LinkGeneration
         int linkGenerationStatus = LinkGeneration.LINK;
 
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java
index f8310980..056fc2f 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java
@@ -60,8 +60,7 @@
  * Tests {@link ShareSheetCoordinator}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Features.EnableFeatures({ChromeFeatureList.PREEMPTIVE_LINK_TO_TEXT_GENERATION,
-        ChromeFeatureList.SHARING_HUB_LINK_TOGGLE})
+@Features.EnableFeatures({ChromeFeatureList.PREEMPTIVE_LINK_TO_TEXT_GENERATION})
 public final class ShareSheetCoordinatorTest {
     private static final String MOCK_URL = JUnitTestGURLs.EXAMPLE_URL;
 
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetLinkToggleCoordinatorTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetLinkToggleCoordinatorTest.java
index 7343aee64..2ea913d 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetLinkToggleCoordinatorTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetLinkToggleCoordinatorTest.java
@@ -69,11 +69,6 @@
         FeatureList.TestValues testValues = new FeatureList.TestValues();
         testValues.addFeatureFlagOverride(
                 ChromeFeatureList.PREEMPTIVE_LINK_TO_TEXT_GENERATION, true);
-        testValues.addFeatureFlagOverride(ChromeFeatureList.SHARING_HUB_LINK_TOGGLE, true);
-        testValues.addFieldTrialParamOverride(ChromeFeatureList.SHARING_HUB_LINK_TOGGLE,
-                ShareSheetLinkToggleCoordinator.IMAGE_ENABLED_BY_DEFAULT, "true");
-        testValues.addFieldTrialParamOverride(ChromeFeatureList.SHARING_HUB_LINK_TOGGLE,
-                ShareSheetLinkToggleCoordinator.SCREENSHOT_ENABLED_BY_DEFAULT, "true");
         FeatureList.setTestValues(testValues);
     }
 
@@ -243,6 +238,20 @@
                         .build());
         assertFalse("Should not enable toggle by default.",
                 shareSheetLinkToggleCoordinator.shouldEnableToggleByDefault());
+
+        shareSheetLinkToggleCoordinator.setShareParamsAndExtras(shareParams,
+                new ChromeShareExtras.Builder()
+                        .setDetailedContentType(DetailedContentType.SCREENSHOT)
+                        .build());
+        assertFalse("Should not enable toggle by default.",
+                shareSheetLinkToggleCoordinator.shouldEnableToggleByDefault());
+
+        shareSheetLinkToggleCoordinator.setShareParamsAndExtras(shareParams,
+                new ChromeShareExtras.Builder()
+                        .setDetailedContentType(DetailedContentType.IMAGE)
+                        .build());
+        assertFalse("Should not enable toggle by default.",
+                shareSheetLinkToggleCoordinator.shouldEnableToggleByDefault());
     }
 
     @Test
@@ -251,7 +260,7 @@
                 new ShareParams.Builder(/*window=*/null, /*title=*/"", /*url=*/"").build();
         ChromeShareExtras chromeShareExtras =
                 new ChromeShareExtras.Builder()
-                        .setDetailedContentType(DetailedContentType.IMAGE)
+                        .setDetailedContentType(DetailedContentType.HIGHLIGHTED_TEXT)
                         .build();
         ShareSheetLinkToggleCoordinator shareSheetLinkToggleCoordinator =
                 new ShareSheetLinkToggleCoordinator(
@@ -262,20 +271,6 @@
 
         shareSheetLinkToggleCoordinator.setShareParamsAndExtras(shareParams,
                 new ChromeShareExtras.Builder()
-                        .setDetailedContentType(DetailedContentType.HIGHLIGHTED_TEXT)
-                        .build());
-        assertTrue("Should enable toggle by default.",
-                shareSheetLinkToggleCoordinator.shouldEnableToggleByDefault());
-
-        shareSheetLinkToggleCoordinator.setShareParamsAndExtras(shareParams,
-                new ChromeShareExtras.Builder()
-                        .setDetailedContentType(DetailedContentType.SCREENSHOT)
-                        .build());
-        assertTrue("Should enable toggle by default.",
-                shareSheetLinkToggleCoordinator.shouldEnableToggleByDefault());
-
-        shareSheetLinkToggleCoordinator.setShareParamsAndExtras(shareParams,
-                new ChromeShareExtras.Builder()
                         .setDetailedContentType(DetailedContentType.WEB_NOTES)
                         .build());
         assertTrue("Should enable toggle by default.",
diff --git a/chrome/browser/shared_highlighting/shared_highlighting_browsertest.cc b/chrome/browser/shared_highlighting/shared_highlighting_browsertest.cc
index 084c4a45c..daf7c78e 100644
--- a/chrome/browser/shared_highlighting/shared_highlighting_browsertest.cc
+++ b/chrome/browser/shared_highlighting/shared_highlighting_browsertest.cc
@@ -248,10 +248,10 @@
   base::RunLoop run_loop_;
 };
 
-#if defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if defined(OS_MAC) || defined(OS_WIN) || BUILDFLAG(IS_CHROMEOS_LACROS)
 // Disabled because it fails for mac specific context menu:
-// https://crbug.com/1275253
-// TODO(1276463): Flakily crashes under lacros.
+// TODO(crbug.com/1275253): Flakily crashes under Windows and Mac.
+// TODO(crbug.com/1276463): Flakily crashes under lacros.
 #define MAYBE_LinkGenerationTest DISABLED_LinkGenerationTest
 #else
 #define MAYBE_LinkGenerationTest LinkGenerationTest
diff --git a/chrome/browser/sharing/sharing_sync_preference_unittest.cc b/chrome/browser/sharing/sharing_sync_preference_unittest.cc
index 52f27b4..717b903 100644
--- a/chrome/browser/sharing/sharing_sync_preference_unittest.cc
+++ b/chrome/browser/sharing/sharing_sync_preference_unittest.cc
@@ -57,7 +57,7 @@
   }
 
   void AddEnabledFeature(int feature) {
-    const base::DictionaryValue* registration =
+    const base::Value* registration =
         prefs_.GetDictionary(prefs::kSharingLocalSharingInfo);
     base::Value enabled_features =
         registration->FindListKey(kSharingInfoEnabledFeatures)->Clone();
diff --git a/chrome/browser/signin/dice_browsertest.cc b/chrome/browser/signin/dice_browsertest.cc
index 150647b..e8d5c51 100644
--- a/chrome/browser/signin/dice_browsertest.cc
+++ b/chrome/browser/signin/dice_browsertest.cc
@@ -1202,7 +1202,7 @@
   // Ensure that there are not deleted profiles before running this test.
   PrefService* local_state = g_browser_process->local_state();
   DCHECK(local_state);
-  const base::ListValue* deleted_profiles =
+  const base::Value* deleted_profiles =
       local_state->GetList(prefs::kProfilesDeleted);
   ASSERT_TRUE(deleted_profiles);
   ASSERT_TRUE(deleted_profiles->GetList().empty());
@@ -1221,7 +1221,7 @@
   // longer allowed.
   PrefService* local_state = g_browser_process->local_state();
   DCHECK(local_state);
-  const base::ListValue* deleted_profiles =
+  const base::Value* deleted_profiles =
       local_state->GetList(prefs::kProfilesDeleted);
   EXPECT_TRUE(deleted_profiles);
   EXPECT_EQ(1U, deleted_profiles->GetList().size());
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.cc b/chrome/browser/signin/dice_web_signin_interceptor.cc
index 751c3a5..dc2955c 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor.cc
@@ -792,7 +792,7 @@
 
 bool DiceWebSigninInterceptor::HasUserDeclinedProfileCreation(
     const std::string& email) const {
-  const base::DictionaryValue* pref_data = profile_->GetPrefs()->GetDictionary(
+  const base::Value* pref_data = profile_->GetPrefs()->GetDictionary(
       kProfileCreationInterceptionDeclinedPref);
   absl::optional<int> declined_count =
       pref_data->FindIntKey(GetPersistentEmailHash(email));
diff --git a/chrome/browser/speech/tts_controller_delegate_impl.cc b/chrome/browser/speech/tts_controller_delegate_impl.cc
index 7c789b5e..61c8b9be 100644
--- a/chrome/browser/speech/tts_controller_delegate_impl.cc
+++ b/chrome/browser/speech/tts_controller_delegate_impl.cc
@@ -117,5 +117,6 @@
   const PrefService* prefs = GetPrefService(utterance);
   return prefs == nullptr
              ? nullptr
-             : prefs->GetDictionary(prefs::kTextToSpeechLangToVoiceName);
+             : &base::Value::AsDictionaryValue(
+                   *prefs->GetDictionary(prefs::kTextToSpeechLangToVoiceName));
 }
diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc
index 7e6846c..087ea1e 100644
--- a/chrome/browser/spellchecker/spellcheck_service.cc
+++ b/chrome/browser/spellchecker/spellcheck_service.cc
@@ -423,13 +423,13 @@
   PrefService* prefs = user_prefs::UserPrefs::Get(context_);
   DCHECK(prefs);
 
-  const base::ListValue* user_dictionaries =
+  const base::Value* user_dictionaries =
       prefs->GetList(spellcheck::prefs::kSpellCheckDictionaries);
-  const base::ListValue* forced_dictionaries =
+  const base::Value* forced_dictionaries =
       prefs->GetList(spellcheck::prefs::kSpellCheckForcedDictionaries);
 
   // Build a lookup of blocked dictionaries to skip loading them.
-  const base::ListValue* blocked_dictionaries =
+  const base::Value* blocked_dictionaries =
       prefs->GetList(spellcheck::prefs::kSpellCheckBlocklistedDictionaries);
   std::unordered_set<std::string> blocked_dictionaries_lookup;
   for (const auto& blocked_dict : blocked_dictionaries->GetList()) {
diff --git a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
index 4c03e6e9..5ec37b0 100644
--- a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
+++ b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
@@ -184,7 +184,7 @@
   }
 
   std::string GetMultilingualDictionaries() {
-    const base::ListValue* list_value =
+    const base::Value* list_value =
         prefs_->GetList(spellcheck::prefs::kSpellCheckDictionaries);
     std::vector<base::StringPiece> dictionaries;
     for (const auto& item_value : list_value->GetList()) {
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 7f7d7023..1f02839 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -1735,7 +1735,7 @@
 
     EXPECT_NO_FATAL_FAILURE(UpdateChromePolicy(policy_map));
 
-    const base::ListValue* pref_value = pref_service->GetList(pref_name);
+    const base::Value* pref_value = pref_service->GetList(pref_name);
     ASSERT_TRUE(pref_value);
     std::vector<std::string> pref_values;
     for (const auto& value : pref_value->GetList()) {
diff --git a/chrome/browser/ssl/ssl_config_service_manager_pref.cc b/chrome/browser/ssl/ssl_config_service_manager_pref.cc
index 213860d..521f9a7c6 100644
--- a/chrome/browser/ssl/ssl_config_service_manager_pref.cc
+++ b/chrome/browser/ssl/ssl_config_service_manager_pref.cc
@@ -311,8 +311,8 @@
 
 void SSLConfigServiceManagerPref::OnDisabledCipherSuitesChange(
     PrefService* local_state) {
-  const base::ListValue* value =
-      local_state->GetList(prefs::kCipherSuiteBlacklist);
+  const base::ListValue* value = &base::Value::AsListValue(
+      *local_state->GetList(prefs::kCipherSuiteBlacklist));
   disabled_cipher_suites_ = ParseCipherSuites(ListValueToStringVector(value));
 }
 
diff --git a/chrome/browser/supervised_user/supervised_user_extension_unittest.cc b/chrome/browser/supervised_user/supervised_user_extension_unittest.cc
index 21de3de..6ecf9de 100644
--- a/chrome/browser/supervised_user/supervised_user_extension_unittest.cc
+++ b/chrome/browser/supervised_user/supervised_user_extension_unittest.cc
@@ -446,7 +446,7 @@
   // Prefs are updated via sync.
   PrefService* pref_service = profile()->GetPrefs();
   ASSERT_TRUE(pref_service);
-  const base::DictionaryValue* approved_extensions =
+  const base::Value* approved_extensions =
       pref_service->GetDictionary(prefs::kSupervisedUserApprovedExtensions);
   EXPECT_TRUE(approved_extensions->FindStringKey(id));
 }
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index c0bc73f..5a3b3ae 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -724,7 +724,7 @@
 }
 
 void SupervisedUserService::UpdateManualHosts() {
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       profile_->GetPrefs()->GetDictionary(prefs::kSupervisedUserManualHosts);
   std::map<std::string, bool> host_map;
   for (auto it : dict->DictItems()) {
@@ -743,7 +743,7 @@
 }
 
 void SupervisedUserService::UpdateManualURLs() {
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       profile_->GetPrefs()->GetDictionary(prefs::kSupervisedUserManualURLs);
   std::map<GURL, bool> url_map;
   for (auto it : dict->DictItems()) {
@@ -981,7 +981,7 @@
   // version information stored in the values is unnecessary. It is only there
   // for backwards compatibility. Remove the version information once sufficient
   // users have migrated away from M83.
-  const base::DictionaryValue* dict = profile_->GetPrefs()->GetDictionary(
+  const base::Value* dict = profile_->GetPrefs()->GetDictionary(
       prefs::kSupervisedUserApprovedExtensions);
   for (auto it : dict->DictItems()) {
     approved_extensions_set_.insert(it.first);
diff --git a/chrome/browser/support_tool/ash/ui_hierarchy_data_collector.cc b/chrome/browser/support_tool/ash/ui_hierarchy_data_collector.cc
index 797dea5..a733720 100644
--- a/chrome/browser/support_tool/ash/ui_hierarchy_data_collector.cc
+++ b/chrome/browser/support_tool/ash/ui_hierarchy_data_collector.cc
@@ -18,6 +18,7 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "chrome/browser/support_tool/data_collector.h"
+#include "components/feedback/pii_types.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 UiHierarchyDataCollector::UiHierarchyDataCollector() = default;
@@ -79,7 +80,7 @@
 }
 
 void UiHierarchyDataCollector::ExportCollectedDataWithPII(
-    std::set<PIIType> pii_types_to_keep,
+    std::set<feedback::PIIType> pii_types_to_keep,
     base::FilePath target_directory,
     DataCollectorDoneCallback on_exported_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -105,6 +106,8 @@
 
 void UiHierarchyDataCollector::InsertIntoPIIMap(
     const std::vector<std::string>& window_titles) {
+  std::set<std::string>& pii_window_titles =
+      pii_map_[feedback::PIIType::kUIHierarchyWindowTitles];
   for (auto const& title : window_titles)
-    pii_map_.emplace(PIIType::kUIHierarchyWindowTitles, title);
+    pii_window_titles.insert(title);
 }
diff --git a/chrome/browser/support_tool/ash/ui_hierarchy_data_collector.h b/chrome/browser/support_tool/ash/ui_hierarchy_data_collector.h
index 5199680..9297fe3 100644
--- a/chrome/browser/support_tool/ash/ui_hierarchy_data_collector.h
+++ b/chrome/browser/support_tool/ash/ui_hierarchy_data_collector.h
@@ -11,6 +11,7 @@
 #include "base/files/file_path.h"
 #include "base/sequence_checker.h"
 #include "chrome/browser/support_tool/data_collector.h"
+#include "components/feedback/pii_types.h"
 
 struct UIHierarchyData {
   UIHierarchyData(std::vector<std::string> window_titles, std::string data);
@@ -45,7 +46,7 @@
       DataCollectorDoneCallback on_data_collected_callback) override;
 
   void ExportCollectedDataWithPII(
-      std::set<PIIType> pii_types_to_keep,
+      std::set<feedback::PIIType> pii_types_to_keep,
       base::FilePath target_directory,
       DataCollectorDoneCallback on_exported_callback) override;
 
diff --git a/chrome/browser/support_tool/data_collector.h b/chrome/browser/support_tool/data_collector.h
index f19a7f4..b4681b9 100644
--- a/chrome/browser/support_tool/data_collector.h
+++ b/chrome/browser/support_tool/data_collector.h
@@ -12,15 +12,9 @@
 
 #include "base/callback.h"
 #include "base/files/file_path.h"
+#include "components/feedback/pii_types.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-// PII (Personally Identifiable Information) types that can exist in the debug
-// data and logs DataCollector collects.
-enum class PIIType {
-  // Window titles that appear in UI hierarchy.
-  kUIHierarchyWindowTitles,
-};
-
 // The error code that a Support Tool component can return.
 enum class SupportToolError {
   kUIHierarchyDataCollectorError,
@@ -30,7 +24,7 @@
   kDataExportCreateArchiveFailed,
 };
 
-using PIIMap = std::multimap<PIIType, std::string>;
+using PIIMap = std::map<feedback::PIIType, std::set<std::string>>;
 
 // Returns a SupportToolError if an error occurs to the callback.
 using DataCollectorDoneCallback =
@@ -63,7 +57,7 @@
   // `on_exported_callback` when done. `on_exported_callback` won't be called
   // if the DataCollector instance is deleted.
   virtual void ExportCollectedDataWithPII(
-      std::set<PIIType> pii_types_to_keep,
+      std::set<feedback::PIIType> pii_types_to_keep,
       base::FilePath target_directory,
       DataCollectorDoneCallback on_exported_callback) = 0;
 };
diff --git a/chrome/browser/support_tool/support_tool_handler.cc b/chrome/browser/support_tool/support_tool_handler.cc
index 7bde6a5..2099fbb 100644
--- a/chrome/browser/support_tool/support_tool_handler.cc
+++ b/chrome/browser/support_tool/support_tool_handler.cc
@@ -26,6 +26,7 @@
 #include "base/task/thread_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/support_tool/data_collector.h"
+#include "components/feedback/pii_types.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/zlib/google/zip.h"
 
@@ -114,8 +115,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   for (auto& data_collector : data_collectors_) {
     const PIIMap& collected = data_collector->GetDetectedPII();
-    // Use std::multipmap.merge() function after migration to C++17.
-    detected_pii_.insert(collected.begin(), collected.end());
+    for (auto& pii_data : collected) {
+      detected_pii_[pii_data.first].insert(pii_data.second.begin(),
+                                           pii_data.second.end());
+    }
   }
 
   std::move(on_data_collection_done_callback_)
@@ -123,7 +126,7 @@
 }
 
 void SupportToolHandler::ExportCollectedData(
-    std::set<PIIType> pii_types_to_keep,
+    std::set<feedback::PIIType> pii_types_to_keep,
     base::FilePath target_path,
     SupportToolDataExportedCallback on_data_exported_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -140,9 +143,10 @@
                      target_path));
 }
 
-void SupportToolHandler::ExportIntoTempDir(std::set<PIIType> pii_types_to_keep,
-                                           base::FilePath target_path,
-                                           base::FilePath tmp_path) {
+void SupportToolHandler::ExportIntoTempDir(
+    std::set<feedback::PIIType> pii_types_to_keep,
+    base::FilePath target_path,
+    base::FilePath tmp_path) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (tmp_path.empty()) {
diff --git a/chrome/browser/support_tool/support_tool_handler.h b/chrome/browser/support_tool/support_tool_handler.h
index f8c3835..b5907594 100644
--- a/chrome/browser/support_tool/support_tool_handler.h
+++ b/chrome/browser/support_tool/support_tool_handler.h
@@ -15,6 +15,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "chrome/browser/support_tool/data_collector.h"
+#include "components/feedback/pii_types.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 using SupportToolDataCollectedCallback =
@@ -79,7 +80,7 @@
   // Exports collected data to the `target_path` and archives the file. This
   // function should be called only once on an instance of SupportToolHandler.
   void ExportCollectedData(
-      std::set<PIIType> pii_types_to_keep,
+      std::set<feedback::PIIType> pii_types_to_keep,
       base::FilePath target_path,
       SupportToolDataExportedCallback on_data_exported_callback);
 
@@ -99,7 +100,7 @@
   // DataCollector with their name. The DataCollectors will export their output
   // to that path then the contents of the `tmp_path` will be put inside a zip
   // archive on `target_path`.
-  void ExportIntoTempDir(std::set<PIIType> pii_types_to_keep,
+  void ExportIntoTempDir(std::set<feedback::PIIType> pii_types_to_keep,
                          base::FilePath target_path,
                          base::FilePath tmp_path);
 
diff --git a/chrome/browser/support_tool/support_tool_handler_unittest.cc b/chrome/browser/support_tool/support_tool_handler_unittest.cc
index cee0c37..76188ad 100644
--- a/chrome/browser/support_tool/support_tool_handler_unittest.cc
+++ b/chrome/browser/support_tool/support_tool_handler_unittest.cc
@@ -25,6 +25,7 @@
 #include "base/test/test_future.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/support_tool/data_collector.h"
+#include "components/feedback/pii_types.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -58,7 +59,7 @@
   }
 
   void ExportCollectedDataWithPII(
-      std::set<PIIType> pii_types_to_keep,
+      std::set<feedback::PIIType> pii_types_to_keep,
       base::FilePath target_directory,
       DataCollectorDoneCallback on_exported_callback) override {
     on_exported_callback = base::BindPostTask(
@@ -78,8 +79,7 @@
     if (error_) {
       std::move(callback).Run(SupportToolError::kTestDataCollectorError);
     } else {
-      pii_map_.insert(std::pair<PIIType, std::string>(
-          PIIType::kUIHierarchyWindowTitles, name_));
+      pii_map_[feedback::PIIType::kUIHierarchyWindowTitles].insert(name_);
       std::move(callback).Run(absl::nullopt);
     }
   }
@@ -208,7 +208,8 @@
   base::FilePath target_path = GetPathForOutput().Append(
       FILE_PATH_LITERAL("support-tool-export-success"));
   base::test::TestFuture<std::set<SupportToolError>> test_future;
-  std::set<PIIType> pii_types{PIIType::kUIHierarchyWindowTitles};
+  std::set<feedback::PIIType> pii_types{
+      feedback::PIIType::kUIHierarchyWindowTitles};
   handler->ExportCollectedData(pii_types, target_path,
                                test_future.GetCallback());
   std::set<SupportToolError> errors = test_future.Get();
@@ -275,7 +276,8 @@
 
   // Export collected data into the target temporary directory.
   base::test::TestFuture<std::set<SupportToolError>> test_future;
-  std::set<PIIType> pii_types{PIIType::kUIHierarchyWindowTitles};
+  std::set<feedback::PIIType> pii_types{
+      feedback::PIIType::kUIHierarchyWindowTitles};
   handler->ExportCollectedData(pii_types, target_path,
                                test_future.GetCallback());
 
diff --git a/chrome/browser/sync/test/integration/preferences_helper.cc b/chrome/browser/sync/test/integration/preferences_helper.cc
index 2a1d9d8..53811d8 100644
--- a/chrome/browser/sync/test/integration/preferences_helper.cc
+++ b/chrome/browser/sync/test/integration/preferences_helper.cc
@@ -130,7 +130,7 @@
 }
 
 bool ListPrefMatches(const char* pref_name) {
-  const base::ListValue* reference_value = GetPrefs(0)->GetList(pref_name);
+  const base::Value* reference_value = GetPrefs(0)->GetList(pref_name);
   for (int i = 1; i < test()->num_clients(); ++i) {
     if (*reference_value != *GetPrefs(i)->GetList(pref_name)) {
       DVLOG(1) << "List preference " << pref_name << " mismatched in"
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
index 6c03652..c24c0f1 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
@@ -77,7 +77,7 @@
             new ObserverList<CriticalPersistedTabDataObserver>();
     private boolean mShouldSaveForTesting;
     /** Tab level Request Desktop Site setting. */
-    private @Nullable @TabUserAgent Integer mUserAgent;
+    private @TabUserAgent int mUserAgent;
 
     @VisibleForTesting
     protected CriticalPersistedTabData(Tab tab) {
@@ -355,7 +355,7 @@
     }
 
     @VisibleForTesting
-    static @Nullable @TabUserAgent Integer getUserAgentType(
+    static @TabUserAgent int getUserAgentType(
             CriticalPersistedTabDataProto.UserAgentType protoUserAgent) {
         switch (protoUserAgent) {
             case DEFAULT:
@@ -371,18 +371,13 @@
             default:
                 assert false : "Unexpected deserialization of UserAgentType: " + protoUserAgent;
                 // shouldn't happen
-                return null;
+                return TabUserAgent.DEFAULT;
         }
     }
 
     @VisibleForTesting
     static CriticalPersistedTabDataProto.UserAgentType getUserAgentType(
-            @Nullable @TabUserAgent Integer protoUserAgent) {
-        if (protoUserAgent == null) {
-            assert false : "TabUserAgent should never be null.";
-            // shouldn't happen
-            return CriticalPersistedTabDataProto.UserAgentType.USER_AGENT_UNKNOWN;
-        }
+            @TabUserAgent int protoUserAgent) {
         switch (protoUserAgent) {
             case TabUserAgent.DEFAULT:
                 return CriticalPersistedTabDataProto.UserAgentType.DEFAULT;
@@ -397,7 +392,7 @@
             default:
                 assert false : "Unexpected serialization of UserAgentType: " + protoUserAgent;
                 // shouldn't happen
-                return CriticalPersistedTabDataProto.UserAgentType.USER_AGENT_UNKNOWN;
+                return CriticalPersistedTabDataProto.UserAgentType.DEFAULT;
         }
     }
 
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/proto/critical_persisted_tab_data.proto b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/proto/critical_persisted_tab_data.proto
index b4e5c24..128824e 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/proto/critical_persisted_tab_data.proto
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/proto/critical_persisted_tab_data.proto
@@ -62,7 +62,6 @@
     DESKTOP = 2;
     UNSET = 3;
     USER_AGENT_SIZE = 4;
-    USER_AGENT_UNKNOWN = 5;
   }
   optional UserAgentType user_agent = 9;
 }
diff --git a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManager.java b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManager.java
index 1201160..acefc46 100644
--- a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManager.java
+++ b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManager.java
@@ -57,6 +57,8 @@
      * @return TabState that has been restored, or null if it failed.
      */
     public static TabState restoreTabState(File stateFolder, int id) {
+        // Confirm we entered this method and log the tab id.
+        Log.i(TAG, "Starting restoreTabState with id " + id);
         // First try finding an unencrypted file.
         boolean encrypted = false;
         File file = getTabStateFile(stateFolder, id, encrypted);
@@ -71,12 +73,16 @@
         if (!file.exists()) return null;
 
         // If one of them passed, open the file input stream and read the state contents.
+        // Confirm we find the TabStateFile and log if encrypted.
+        Log.i(TAG, "restoreTabState with id " + id + " and encrypted is " + encrypted);
         long startTime = SystemClock.elapsedRealtime();
         TabState tabState = restoreTabState(file, encrypted);
         if (tabState != null) {
             RecordHistogram.recordTimesHistogram(
                     "Tabs.TabState.LoadTime", SystemClock.elapsedRealtime() - startTime);
         }
+        // Confirm we get the tabState and log if it is null.
+        Log.i(TAG, "restoreTabState with id " + id + " and tabState is " + tabState);
         return tabState;
     }
 
diff --git a/chrome/browser/translate/translate_manager_browsertest.cc b/chrome/browser/translate/translate_manager_browsertest.cc
index b4ac8d2..9fec519 100644
--- a/chrome/browser/translate/translate_manager_browsertest.cc
+++ b/chrome/browser/translate/translate_manager_browsertest.cc
@@ -504,7 +504,7 @@
 // Test that hrefTranslate is propagating properly.
 // The test is flaky. See https://crbug.com/1280123
 IN_PROC_BROWSER_TEST_F(TranslateManagerBrowserTest,
-                       DISABLE_HrefTranslateSuccess) {
+                       DISABLED_HrefTranslateSuccess) {
   base::HistogramTester histograms;
   GetChromeTranslateClient()
       ->GetTranslateManager()
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index d717f628..7cd4b47b 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -4070,6 +4070,8 @@
       "views/device_chooser_content_view.h",
       "views/devtools_process_observer.cc",
       "views/devtools_process_observer.h",
+      "views/download/bubble/download_toolbar_button_view.cc",
+      "views/download/bubble/download_toolbar_button_view.h",
       "views/download/download_danger_prompt_views.cc",
       "views/download/download_in_progress_dialog_view.cc",
       "views/download/download_in_progress_dialog_view.h",
diff --git a/chrome/browser/ui/android/management/BUILD.gn b/chrome/browser/ui/android/management/BUILD.gn
index 73bcb88..38906b1 100644
--- a/chrome/browser/ui/android/management/BUILD.gn
+++ b/chrome/browser/ui/android/management/BUILD.gn
@@ -7,6 +7,7 @@
 android_library("java") {
   sources = [
     "java/src/org/chromium/chrome/browser/management/ManagementCoordinator.java",
+    "java/src/org/chromium/chrome/browser/management/ManagementMediator.java",
     "java/src/org/chromium/chrome/browser/management/ManagementPage.java",
     "java/src/org/chromium/chrome/browser/management/ManagementProperties.java",
     "java/src/org/chromium/chrome/browser/management/ManagementView.java",
@@ -20,6 +21,7 @@
     "//chrome/browser/tab:java",
     "//chrome/browser/ui/android/native_page:java",
     "//components/embedder_support/android:util_java",
+    "//content/public/android:content_full_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//ui/android:ui_no_recycler_view_java",
   ]
diff --git a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementCoordinator.java b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementCoordinator.java
index bdbf1ea..d3165d20 100644
--- a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementCoordinator.java
+++ b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementCoordinator.java
@@ -4,19 +4,18 @@
 
 package org.chromium.chrome.browser.management;
 
-import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.View;
 
-import org.chromium.chrome.browser.enterprise.util.ManagedBrowserUtils;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.chrome.browser.ui.native_page.NativePageHost;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * The class responsible for setting up ManagementPage.
  */
 class ManagementCoordinator {
+    private final ManagementMediator mMediator;
     private final ManagementView mView;
 
     /**
@@ -24,18 +23,12 @@
      * @param context Environment Context.
      * @param profile The current Profile.
      */
-    public ManagementCoordinator(Context context, Profile profile) {
-        PropertyModel model = new PropertyModel.Builder(ManagementProperties.ALL_KEYS)
-                                      .with(ManagementProperties.BROWSER_IS_MANAGED,
-                                              ManagedBrowserUtils.isBrowserManaged(profile))
-                                      .with(ManagementProperties.ACCOUNT_MANAGER_NAME,
-                                              ManagedBrowserUtils.getAccountManagerName(profile))
-                                      .build();
-
-        mView = (ManagementView) LayoutInflater.from(context).inflate(
-                R.layout.enterprise_management, null);
-
-        PropertyModelChangeProcessor.create(model, mView, ManagementViewBinder::bind);
+    public ManagementCoordinator(NativePageHost host, Profile profile) {
+        mMediator = new ManagementMediator(host, profile);
+        mView = (ManagementView) LayoutInflater.from(host.getContext())
+                        .inflate(R.layout.enterprise_management, null);
+        PropertyModelChangeProcessor.create(
+                mMediator.getModel(), mView, ManagementViewBinder::bind);
     }
 
     /** Returns the intended view for ManagementPage tab. */
diff --git a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementMediator.java b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementMediator.java
new file mode 100644
index 0000000..b48a8ad9
--- /dev/null
+++ b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementMediator.java
@@ -0,0 +1,54 @@
+// 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.
+
+package org.chromium.chrome.browser.management;
+
+import android.content.Context;
+import android.text.SpannableString;
+
+import org.chromium.chrome.browser.enterprise.util.ManagedBrowserUtils;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.ui.native_page.NativePageHost;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.text.NoUnderlineClickableSpan;
+import org.chromium.ui.text.SpanApplier;
+
+/**
+ * A mediator for the {@link ManagementCoordinator} responsible for handling business logic.
+ */
+public class ManagementMediator {
+    private static final String LEARN_MORE_URL =
+            "https://support.google.com/chrome/?p=is_chrome_managed";
+
+    private final NativePageHost mHost;
+    private final PropertyModel mModel;
+
+    public ManagementMediator(NativePageHost host, Profile profile) {
+        mHost = host;
+        mModel = new PropertyModel.Builder(ManagementProperties.ALL_KEYS)
+                         .with(ManagementProperties.BROWSER_IS_MANAGED,
+                                 ManagedBrowserUtils.isBrowserManaged(profile))
+                         .with(ManagementProperties.ACCOUNT_MANAGER_NAME,
+                                 ManagedBrowserUtils.getAccountManagerName(profile))
+                         .with(ManagementProperties.LEARN_MORE_TEXT, getLearnMoreClickableText())
+                         .build();
+    }
+
+    public PropertyModel getModel() {
+        return mModel;
+    }
+
+    private SpannableString getLearnMoreClickableText() {
+        final Context context = mHost.getContext();
+        final NoUnderlineClickableSpan clickableLearnMoreSpan = new NoUnderlineClickableSpan(
+                context.getResources(), (v) -> { showHelpCenterArticle(); });
+        return SpanApplier.applySpans(context.getString(R.string.management_learn_more),
+                new SpanApplier.SpanInfo("<LINK>", "</LINK>", clickableLearnMoreSpan));
+    }
+
+    private void showHelpCenterArticle() {
+        mHost.loadUrl(new LoadUrlParams(LEARN_MORE_URL), /*incognito=*/false);
+    }
+}
diff --git a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementPage.java b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementPage.java
index e70da6a..1341fcda 100644
--- a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementPage.java
+++ b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementPage.java
@@ -25,7 +25,7 @@
         super(host);
 
         mTitle = host.getContext().getResources().getString(R.string.management);
-        mManagementCoordinator = new ManagementCoordinator(host.getContext(), profile);
+        mManagementCoordinator = new ManagementCoordinator(host, profile);
 
         initWithView(mManagementCoordinator.getView());
     }
diff --git a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementProperties.java b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementProperties.java
index a6005d1..21ce3309 100644
--- a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementProperties.java
+++ b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementProperties.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.management;
 
+import android.text.SpannableString;
+
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -11,11 +13,15 @@
  * Associated properties for ManagementPage's view.
  */
 class ManagementProperties {
-    public static final PropertyModel.WritableBooleanPropertyKey BROWSER_IS_MANAGED =
-            new PropertyModel.WritableBooleanPropertyKey();
-
     public static final PropertyModel.WritableObjectPropertyKey<String> ACCOUNT_MANAGER_NAME =
             new PropertyModel.WritableObjectPropertyKey<>();
 
-    public static final PropertyKey[] ALL_KEYS = {BROWSER_IS_MANAGED, ACCOUNT_MANAGER_NAME};
+    public static final PropertyModel.WritableBooleanPropertyKey BROWSER_IS_MANAGED =
+            new PropertyModel.WritableBooleanPropertyKey();
+
+    public static final PropertyModel.WritableObjectPropertyKey<SpannableString> LEARN_MORE_TEXT =
+            new PropertyModel.WritableObjectPropertyKey<>();
+
+    public static final PropertyKey[] ALL_KEYS = {
+            ACCOUNT_MANAGER_NAME, BROWSER_IS_MANAGED, LEARN_MORE_TEXT};
 }
diff --git a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementView.java b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementView.java
index 7964aef..bf62a33 100644
--- a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementView.java
+++ b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementView.java
@@ -5,7 +5,7 @@
 package org.chromium.chrome.browser.management;
 
 import android.content.Context;
-import android.text.Html;
+import android.text.SpannableString;
 import android.text.TextUtils;
 import android.text.method.LinkMovementMethod;
 import android.util.AttributeSet;
@@ -26,9 +26,6 @@
     private TextView mDescription;
     private TextView mLearnMore;
 
-    private static final String LEARN_MORE_URL =
-            "https://support.google.com/chrome/answer/9281740?p=is_chrome_managed&visit_id=637678488620233541-4078225067&rd=1";
-
     /** Constructor for inflating from XML. */
     public ManagementView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -42,13 +39,6 @@
         mDescription = (TextView) findViewById(R.id.description_text);
         mLearnMore = (TextView) findViewById(R.id.learn_more);
 
-        // Enable learn more link.
-        String learnMoreText =
-                getResources().getString(R.string.management_learn_more, LEARN_MORE_URL);
-
-        mLearnMore.setText(Html.fromHtml(learnMoreText));
-        mLearnMore.setMovementMethod(LinkMovementMethod.getInstance());
-
         // Set default management status
         mIsManaged = false;
         mManagerName = null;
@@ -87,6 +77,11 @@
         return mManagerName;
     }
 
+    public void setLearnMoreText(SpannableString learnMoreText) {
+        mLearnMore.setText(learnMoreText);
+        mLearnMore.setMovementMethod(LinkMovementMethod.getInstance());
+    }
+
     /**
      * Adjusts Title, Description, and Learn More link based on management status.
      */
diff --git a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementViewBinder.java b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementViewBinder.java
index c401511..ad1bf6e 100644
--- a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementViewBinder.java
+++ b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementViewBinder.java
@@ -18,10 +18,12 @@
      * @param propertyKey Specific model attribute that changed on this event.
      */
     public static void bind(PropertyModel model, ManagementView view, PropertyKey propertyKey) {
-        if (ManagementProperties.BROWSER_IS_MANAGED == propertyKey) {
+        if (propertyKey == ManagementProperties.BROWSER_IS_MANAGED) {
             view.setManaged(model.get(ManagementProperties.BROWSER_IS_MANAGED));
-        } else if (ManagementProperties.ACCOUNT_MANAGER_NAME == propertyKey) {
+        } else if (propertyKey == ManagementProperties.ACCOUNT_MANAGER_NAME) {
             view.setManagerName(model.get(ManagementProperties.ACCOUNT_MANAGER_NAME));
+        } else if (propertyKey == ManagementProperties.LEARN_MORE_TEXT) {
+            view.setLearnMoreText(model.get(ManagementProperties.LEARN_MORE_TEXT));
         }
     }
 }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java
index 4b7a5558..0a5a7f1 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java
@@ -1405,4 +1405,14 @@
         assertVerifierCallCounts(2, 3);
         mInOrder.verifyNoMoreInteractions();
     }
+
+    // crbug.com/759876
+    @Test
+    @EnableFeatures(ChromeFeatureList.SPANNABLE_INLINE_AUTOCOMPLETE)
+    public void testEndBatchEditCanReturnFalse() {
+        assertTrue(mInputConnection.beginBatchEdit());
+        assertLastBatchEdit(mInputConnection.endBatchEdit());
+        // Additional endBatchEdit() must continue returning false.
+        assertFalse(mInputConnection.endBatchEdit());
+    }
 }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModel.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModel.java
index 2ee179108..5163a47 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModel.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModel.java
@@ -564,15 +564,14 @@
         public boolean beginBatchEdit() {
             if (DEBUG) Log.i(TAG, "beginBatchEdit");
             onBeginImeCommand();
-            boolean retVal = incrementBatchEditCount();
-            onEndImeCommand();
-            return retVal;
+            incrementBatchEditCount();
+            return onEndImeCommand();
         }
 
         /**
          * Always call this at the beginning of any IME command. Compare this with beginBatchEdit()
          * which is by itself an IME command.
-         * @return Whether the call was successful.
+         * @return {@code true} if the batch edit is still in progress. {@code false} otherwise.
          */
         public boolean onBeginImeCommand() {
             if (DEBUG) Log.i(TAG, "onBeginImeCommand: " + mBatchEditNestCount);
@@ -625,15 +624,14 @@
         public boolean endBatchEdit() {
             if (DEBUG) Log.i(TAG, "endBatchEdit");
             onBeginImeCommand();
-            boolean retVal = decrementBatchEditCount();
-            onEndImeCommand();
-            return retVal;
+            decrementBatchEditCount();
+            return onEndImeCommand();
         }
 
         /**
          * Always call this at the end of an IME command. Compare this with endBatchEdit()
          * which is by itself an IME command.
-         * @return Whether the call was successful.
+         * @return {@code true} if the batch edit is still in progress. {@code false} otherwise.
          */
         public boolean onEndImeCommand() {
             if (DEBUG) Log.i(TAG, "onEndImeCommand: " + (mBatchEditNestCount - 1));
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/java/res/drawable-nodpi/quick_action_search_widget_dino_preview.png b/chrome/browser/ui/android/quickactionsearchwidget/java/res/drawable-nodpi/quick_action_search_widget_dino_preview.png
index 392ddf6..fe13f3d 100644
--- a/chrome/browser/ui/android/quickactionsearchwidget/java/res/drawable-nodpi/quick_action_search_widget_dino_preview.png
+++ b/chrome/browser/ui/android/quickactionsearchwidget/java/res/drawable-nodpi/quick_action_search_widget_dino_preview.png
Binary files differ
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_medium_layout.xml b/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_medium_layout.xml
index f0040cf..6496c3c3 100644
--- a/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_medium_layout.xml
+++ b/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_medium_layout.xml
@@ -21,6 +21,7 @@
         android:layout_marginBottom="@dimen/quick_action_search_widget_medium_row_vertical_margin"
         android:background="@drawable/quick_action_search_widget_medium_search_bar_background"
         android:gravity="center_vertical"
+        android:contentDescription="@string/quick_action_search_widget_search_bar_hint"
         android:orientation="horizontal">
 
         <ImageView
@@ -31,7 +32,7 @@
             android:layout_marginBottom="@dimen/quick_action_search_widget_medium_search_bar_icon_margin"
             android:layout_marginStart="@dimen/quick_action_search_widget_medium_search_bar_icon_margin"
             android:layout_marginEnd="@dimen/quick_action_search_widget_medium_search_bar_icon_margin_text"
-            android:contentDescription="@string/accessibility_quick_action_search_widget_icon"
+            android:importantForAccessibility="no"
             android:src="@mipmap/app_icon" />
 
         <TextView
@@ -41,6 +42,7 @@
             android:layout_height="match_parent"
             android:gravity="center_vertical"
             android:hint="@string/quick_action_search_widget_search_bar_hint"
+            android:importantForAccessibility="no"
             android:singleLine="true"
             android:textAlignment="viewStart" />
     </LinearLayout>
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_small_layout.xml b/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_small_layout.xml
index f2f97a0..95e47d0 100644
--- a/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_small_layout.xml
+++ b/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_small_layout.xml
@@ -23,6 +23,7 @@
         android:layout_weight="1"
         android:background="@drawable/quick_action_search_widget_small_search_bar_background"
         android:gravity="center_vertical"
+        android:contentDescription="@string/quick_action_search_widget_search_bar_hint"
         android:orientation="horizontal">
 
         <ImageView
@@ -33,7 +34,7 @@
             android:layout_marginBottom="@dimen/quick_action_search_widget_small_search_bar_icon_margin"
             android:layout_marginStart="@dimen/quick_action_search_widget_small_search_bar_icon_margin"
             android:layout_marginEnd="@dimen/quick_action_search_widget_small_search_bar_icon_margin_text"
-            android:contentDescription="@string/accessibility_quick_action_search_widget_icon"
+            android:importantForAccessibility="no"
             android:src="@mipmap/app_icon" />
 
         <TextView
@@ -43,6 +44,7 @@
             android:layout_height="match_parent"
             android:gravity="center_vertical"
             android:hint="@string/search_widget_default"
+            android:importantForAccessibility="no"
             android:singleLine="true"
             android:textAlignment="viewStart" />
 
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_xsmall_layout.xml b/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_xsmall_layout.xml
index 62e9599..aed4ff25 100644
--- a/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_xsmall_layout.xml
+++ b/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_xsmall_layout.xml
@@ -21,6 +21,7 @@
         android:layout_gravity="center"
         android:layout_marginHorizontal="@dimen/quick_action_search_widget_xsmall_search_bar_horizontal_margin"
         android:layout_weight="1"
+        android:contentDescription="@string/quick_action_search_widget_search_bar_hint"
         android:background="@drawable/quick_action_search_widget_xsmall_search_bar_background"
         android:gravity="center_vertical"
         android:orientation="horizontal">
@@ -33,7 +34,7 @@
             android:layout_marginBottom="@dimen/quick_action_search_widget_xsmall_search_bar_icon_margin"
             android:layout_marginStart="@dimen/quick_action_search_widget_xsmall_search_bar_icon_margin"
             android:layout_marginEnd="@dimen/quick_action_search_widget_xsmall_search_bar_icon_margin_text"
-            android:contentDescription="@string/accessibility_quick_action_search_widget_icon"
+            android:importantForAccessibility="no"
             android:src="@mipmap/app_icon" />
 
         <TextView
@@ -44,6 +45,7 @@
             android:gravity="center_vertical"
             android:hint="@string/search_widget_default"
             android:singleLine="true"
+            android:importantForAccessibility="no"
             android:textAlignment="viewStart" />
 
     </LinearLayout>
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/java/src/org/chromium/chrome/browser/ui/quickactionsearchwidget/QuickActionSearchWidgetProviderDelegateTest.java b/chrome/browser/ui/android/quickactionsearchwidget/java/src/org/chromium/chrome/browser/ui/quickactionsearchwidget/QuickActionSearchWidgetProviderDelegateTest.java
index 185e6a8..028eb7ba 100644
--- a/chrome/browser/ui/android/quickactionsearchwidget/java/src/org/chromium/chrome/browser/ui/quickactionsearchwidget/QuickActionSearchWidgetProviderDelegateTest.java
+++ b/chrome/browser/ui/android/quickactionsearchwidget/java/src/org/chromium/chrome/browser/ui/quickactionsearchwidget/QuickActionSearchWidgetProviderDelegateTest.java
@@ -38,6 +38,7 @@
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.browserservices.intents.WebappConstants;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
@@ -605,6 +606,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "https://crbug.com/1281655")
     public void resizeDinoWidgetToFillTargetCellArea_repositionContentRTL() {
         final Configuration c = new Configuration(mContext.getResources().getConfiguration());
         c.setLayoutDirection(Locale.forLanguageTag("ar")); // arabic
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 1d7f1101..c285737 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -601,13 +601,17 @@
         Keep your passwords safe from data breaches and other security issues
       </message>
       <!-- TODO(crbug.com/1202088): Remove "translateable=false" once definitive string is available, and update description. -->
-      <message translateable="false" name="IDS_ANDROID_TRUSTED_VAULT_OPT_IN_LABEL" desc="Title for the settings button to opt in to trusted vault encryption">
+      <message translateable="false" name="IDS_ANDROID_TRUSTED_VAULT_BANNER_LABEL" desc="Title for the trusted vault banner in password settings.">
         Trusted vault
       </message>
       <!-- TODO(crbug.com/1202088): Remove "translateable=false" once definitive string is available, and update description. -->
-      <message translateable="false" name="IDS_ANDROID_TRUSTED_VAULT_OPT_IN_SUB_LABEL" desc="Sub-label for the settings button to opt in to trusted vault encryption">
+      <message translateable="false" name="IDS_ANDROID_TRUSTED_VAULT_BANNER_SUB_LABEL_OFFER_OPT_IN" desc="Sub-label for the trusted vault banner when the user is offered to opt in to trusted vault encryption.">
         Opt in to trusted vault
       </message>
+      <!-- TODO(crbug.com/1202088): Remove "translateable=false" once definitive string is available, and update description. -->
+      <message translateable="false" name="IDS_ANDROID_TRUSTED_VAULT_BANNER_SUB_LABEL_OPTED_IN" desc="Sub-label for the trusted vault banner when the user is already opted in to trusted vault encryption.">
+        You are opted in to trusted vault
+      </message>
       <message name="IDS_SECTION_SAVED_PASSWORDS_EXCEPTIONS" desc="Header for the list of websites for which user selected to never save passwords. [CHAR_LIMIT=32]">
         Never saved
       </message>
@@ -2326,9 +2330,6 @@
       <message name="IDS_CONTEXTMENU_COPY_IMAGE" desc="Context sensitive menu item for copying the selected image to the system clipboard. [CHAR_LIMIT=30]">
         Copy image
       </message>
-      <message name="IDS_CONTEXTMENU_SEARCH_WITH_GOOGLE_LENS" desc="Context sensitive menu item for deep linking into google lens. [CHAR_LIMIT=30]">
-        Search with Google Lens <ph name="BEGIN_NEW">&lt;new&gt;</ph>New<ph name="END_NEW">&lt;/new&gt;</ph>
-      </message>
       <message name="IDS_CONTEXTMENU_SEARCH_IMAGE_WITH_GOOGLE_LENS" desc="Context sensitive menu item for deep linking into google lens. [CHAR_LIMIT=30]">
         Search image with Google Lens <ph name="BEGIN_NEW">&lt;new&gt;</ph>New<ph name="END_NEW">&lt;/new&gt;</ph>
       </message>
@@ -4460,9 +4461,6 @@
       <message name="IDS_ACCESSIBILITY_QUICK_ACTION_SEARCH_WIDGET_START_DINO_GAME" desc="Content description for the Dino button on the Quick Action Search Widget.">
         Start Dino Game
       </message>
-      <message name="IDS_ACCESSIBILITY_QUICK_ACTION_SEARCH_WIDGET_ICON" desc="Content description for the for the icon on the Quick Action Search Widget">
-        Quick Action Search Widget Icon
-      </message>
       <message name="IDS_QUICK_ACTION_SEARCH_WIDGET_MESSAGE_NO_INCOGNITO" desc="Toast message shown when the user tries to open Incognito tab which is disabled on the device.">
         Incognito is not available on this device
       </message>
@@ -5434,7 +5432,7 @@
         Your administrator can change your browser setup remotely. Activity on this device may also be managed outside of Chrome.
       </message>
       <message name="IDS_MANAGEMENT_LEARN_MORE" desc="The learn more link on chrome://management page that provides more information about managed devices.">
-        <ph name="BEGIN_LINK">&lt;a href="$1<ex>https://google.com/</ex>"&gt;</ph>Learn More<ph name="END_LINK">&lt;/a&gt;</ph>
+        <ph name="BEGIN_LINK">&lt;LINK&gt;</ph>Learn More<ph name="END_LINK">&lt;/LINK&gt;</ph>
       </message>
     </messages>
   </release>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ACCESSIBILITY_QUICK_ACTION_SEARCH_WIDGET_ICON.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ACCESSIBILITY_QUICK_ACTION_SEARCH_WIDGET_ICON.png.sha1
deleted file mode 100644
index cb90c965c..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ACCESSIBILITY_QUICK_ACTION_SEARCH_WIDGET_ICON.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-747da3f9af01f47173353a7a91a50f7de18b592f
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTMENU_SEARCH_WITH_GOOGLE_LENS.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTMENU_SEARCH_WITH_GOOGLE_LENS.png.sha1
deleted file mode 100644
index 2894ad2..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTMENU_SEARCH_WITH_GOOGLE_LENS.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-7c86e47f3459f791bd5be58e1dd2b16f8054a7e3
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MANAGEMENT_LEARN_MORE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MANAGEMENT_LEARN_MORE.png.sha1
index 4f34ffd..c6687ec 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MANAGEMENT_LEARN_MORE.png.sha1
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MANAGEMENT_LEARN_MORE.png.sha1
@@ -1 +1 @@
-b02f038ccd57988410ab3f87c9a2f4d3100cacd5
\ No newline at end of file
+66dd9d8051a49106f928cc6b1e3ea842f82a94b1
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_lo.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_lo.xtb
index bfb8817..e6229974 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_lo.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_lo.xtb
@@ -1082,6 +1082,7 @@
 <translation id="7177466738963138057">ທ່ານສາມາດປ່ຽນສິ່ງນີ້ໄດ້ພາຍຫຼັງໃນການຕັ້ງຄ່າ</translation>
 <translation id="7177873915659574692">ບໍ່ສາມາດສ້າງລະຫັດ QR ໄດ້. URL ຫຼາຍກວ່າ <ph name="CHARACTER_LIMIT" /> ຕົວອັກສອນ.</translation>
 <translation id="7180611975245234373">ຣີເຟຣຊ</translation>
+<translation id="718226107353899806">ທ່ານສາມາດເພີ່ມຣີແອັກຊັນໄດ້ສູງສຸດ <ph name="MAX_NUM_REACTIONS" /> ອັນ</translation>
 <translation id="7187993566681480880">ຮັກສາໃຫ້ທ່ານປອດໄພໃນ Chrome ແລະ ອາດຈະຖືກໃຊ້ເພື່ອປັບປຸງຄວາມປອດໄພໃນແອັບອື່ນຂອງ Google ເມື່ອທ່ານເຂົ້າສູ່ລະບົບ.</translation>
 <translation id="718926126787620637">ລາຍຊື່ໂຟນເດີທີ່ເປີດຢູ່ທີ່ຄວາມສູງເຕັມຈໍ</translation>
 <translation id="7191430249889272776">ແຖບທີ່ເປີດຢູ່ໃນພື້ນຫຼັງ.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sw.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sw.xtb
index d408620..580f286 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sw.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sw.xtb
@@ -1082,6 +1082,7 @@
 <translation id="7177466738963138057">Unaweza kubadilisha hii baadaye katika Mipangilio</translation>
 <translation id="7177873915659574692">Imeshindwa kutunga Msimbo wa QR. URL imepitisha herufi <ph name="CHARACTER_LIMIT" />.</translation>
 <translation id="7180611975245234373">Onyesha upya</translation>
+<translation id="718226107353899806">Unaweza kuweka hadi maoni <ph name="MAX_NUM_REACTIONS" /></translation>
 <translation id="7187993566681480880">Hulinda usalama wako kwenye Chrome na inaweza kutumiwa kuboresha usalama wako kwenye programu nyingine za Google ukiwa umeingia katika akaunti.</translation>
 <translation id="718926126787620637">Orodha ya folda za alamisho imefunguliwa kwenye skrini nzima</translation>
 <translation id="7191430249889272776">Kichupo kimefunguliwa chini chini.</translation>
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarMediator.java
index fe786f99..0f9b0ae 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarMediator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarMediator.java
@@ -82,7 +82,6 @@
     private boolean mDefaultSearchEngineHasLogo;
     private boolean mShouldShowStartSurfaceAsHomepage;
     private boolean mHomepageEnabled;
-    private boolean mHasIncognitoTabs;
 
     private CallbackController mCallbackController = new CallbackController();
     private float mNonIncognitoHomepageTranslationY;
@@ -162,13 +161,11 @@
         mIncognitoTabModelObserver = new IncognitoTabModelObserver() {
             @Override
             public void wasFirstTabCreated() {
-                mHasIncognitoTabs = true;
                 updateIncognitoToggleTabVisibility();
             }
 
             @Override
             public void didBecomeEmpty() {
-                mHasIncognitoTabs = false;
                 updateIncognitoToggleTabVisibility();
             }
         };
@@ -292,14 +289,16 @@
             return;
         }
 
-        if (mHideIncognitoSwitchWhenNoTabs) {
-            mPropertyModel.set(INCOGNITO_SWITCHER_VISIBLE, mHasIncognitoTabs);
-        } else {
-            mPropertyModel.set(INCOGNITO_SWITCHER_VISIBLE, true);
-        }
+        mPropertyModel.set(
+                INCOGNITO_SWITCHER_VISIBLE, !mHideIncognitoSwitchWhenNoTabs || hasIncognitoTabs());
         updateNewTabViewTextVisibility();
     }
 
+    private boolean hasIncognitoTabs() {
+        if (mTabModelSelector == null) return false;
+        return mTabModelSelector.getModel(true).getCount() != 0;
+    }
+
     void setStartSurfaceToolbarVisibility(boolean shouldShowStartSurfaceToolbar) {
         mPropertyModel.set(IS_VISIBLE, shouldShowStartSurfaceToolbar);
     }
diff --git a/chrome/browser/ui/app_list/app_list_sort_unittest.cc b/chrome/browser/ui/app_list/app_list_sort_unittest.cc
index 8947136..c5974ff2 100644
--- a/chrome/browser/ui/app_list/app_list_sort_unittest.cc
+++ b/chrome/browser/ui/app_list/app_list_sort_unittest.cc
@@ -378,7 +378,8 @@
 
 // Verifies that merging two items to form a folder resets the nominal app list
 // sort order (if the app list is sorted at the time).
-TEST_F(TemporaryAppListSortTest, MergingItemsResetsSortOrder) {
+// Disabled due to flakiness (https://crbug.com/1281828).
+TEST_F(TemporaryAppListSortTest, DISABLED_MergingItemsResetsSortOrder) {
   RemoveAllExistingItems();
 
   std::vector<scoped_refptr<extensions::Extension>> apps;
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc
index dd7e21b..7f41ba9 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -437,27 +437,26 @@
   DCHECK(!IsInitialized());
 
   // Restore initial state from local storage.
-  const base::DictionaryValue* local_items =
+  const base::Value* local_items =
       profile_->GetPrefs()->GetDictionary(prefs::kAppListLocalState);
   DCHECK(local_items);
 
-  for (base::DictionaryValue::Iterator item(*local_items); !item.IsAtEnd();
-       item.Advance()) {
+  for (const auto item : local_items->DictItems()) {
     const base::DictionaryValue* dict_item;
-    if (!item.value().GetAsDictionary(&dict_item)) {
-      LOG(ERROR) << "Dictionary not found for " << item.key() + ".";
+    if (!item.second.GetAsDictionary(&dict_item)) {
+      LOG(ERROR) << "Dictionary not found for " << item.first + ".";
       continue;
     }
 
     absl::optional<int> type = dict_item->FindIntKey(kTypeKey);
     if (!type) {
-      LOG(ERROR) << "Item type is not set in local storage for " << item.key()
+      LOG(ERROR) << "Item type is not set in local storage for " << item.second
                  << ".";
       continue;
     }
 
     SyncItem* sync_item = CreateSyncItem(
-        item.key(),
+        item.first,
         static_cast<sync_pb::AppListSpecifics::AppListItemType>(*type));
 
     dict_item->GetString(kNameKey, &sync_item->item_name);
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index fe0b19fa..5384a675 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -115,7 +115,7 @@
   }
 
   bool Get(const std::string& app_id) {
-    const base::DictionaryValue* dict =
+    const base::Value* dict =
         prefs_->GetDictionary(arc::prefs::kArcSetNotificationsEnabledDeferred);
     return dict->FindBoolKey(app_id).value_or(false);
   }
@@ -339,8 +339,7 @@
 
 std::string ArcAppListPrefs::GetAppIdByPackageName(
     const std::string& package_name) const {
-  const base::DictionaryValue* apps =
-      prefs_->GetDictionary(arc::prefs::kArcApps);
+  const base::Value* apps = prefs_->GetDictionary(arc::prefs::kArcApps);
   if (!apps)
     return std::string();
 
@@ -655,8 +654,8 @@
     return nullptr;
 
   const base::DictionaryValue* package = nullptr;
-  const base::DictionaryValue* packages =
-      prefs_->GetDictionary(arc::prefs::kArcPackages);
+  const base::DictionaryValue* packages = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(arc::prefs::kArcPackages));
   if (!packages ||
       !packages->GetDictionaryWithoutPathExpansion(package_name, &package))
     return nullptr;
@@ -740,8 +739,8 @@
 
 std::vector<std::string> ArcAppListPrefs::GetAppIdsNoArcEnabledCheck() const {
   std::vector<std::string> ids;
-  const base::DictionaryValue* apps =
-      prefs_->GetDictionary(arc::prefs::kArcApps);
+  const base::DictionaryValue* apps = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(arc::prefs::kArcApps));
   DCHECK(apps);
 
   // crx_file::id_util is de-facto utility for id generation.
@@ -770,8 +769,8 @@
 std::unique_ptr<ArcAppListPrefs::AppInfo> ArcAppListPrefs::GetAppFromPrefs(
     const std::string& app_id) const {
   const base::DictionaryValue* app = nullptr;
-  const base::DictionaryValue* apps =
-      prefs_->GetDictionary(arc::prefs::kArcApps);
+  const base::DictionaryValue* apps = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(arc::prefs::kArcApps));
   if (!apps || !apps->GetDictionaryWithoutPathExpansion(app_id, &app))
     return nullptr;
 
@@ -826,8 +825,8 @@
     return false;
 
   const base::DictionaryValue* app = nullptr;
-  const base::DictionaryValue* apps =
-      prefs_->GetDictionary(arc::prefs::kArcApps);
+  const base::DictionaryValue* apps = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(arc::prefs::kArcApps));
   return apps && apps->GetDictionaryWithoutPathExpansion(app_id, &app);
 }
 
@@ -1692,8 +1691,8 @@
 void ArcAppListPrefs::OnUninstallShortcut(const std::string& package_name,
                                           const std::string& intent_uri) {
   std::vector<std::string> shortcuts_to_remove;
-  const base::DictionaryValue* apps =
-      prefs_->GetDictionary(arc::prefs::kArcApps);
+  const base::DictionaryValue* apps = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(arc::prefs::kArcApps));
   for (base::DictionaryValue::Iterator app_it(*apps); !app_it.IsAtEnd();
        app_it.Advance()) {
     const base::Value* value = &app_it.value();
@@ -1731,8 +1730,8 @@
     bool include_only_launchable_apps,
     bool include_shortcuts) const {
   std::unordered_set<std::string> app_set;
-  const base::DictionaryValue* apps =
-      prefs_->GetDictionary(arc::prefs::kArcApps);
+  const base::DictionaryValue* apps = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(arc::prefs::kArcApps));
   for (base::DictionaryValue::Iterator app_it(*apps); !app_it.IsAtEnd();
        app_it.Advance()) {
     if (!crx_file::id_util::IdIsValid(app_it.key()))
@@ -1879,8 +1878,8 @@
 void ArcAppListPrefs::OnNotificationsEnabledChanged(
     const std::string& package_name,
     bool enabled) {
-  const base::DictionaryValue* apps =
-      prefs_->GetDictionary(arc::prefs::kArcApps);
+  const base::DictionaryValue* apps = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(arc::prefs::kArcApps));
   for (base::DictionaryValue::Iterator app(*apps); !app.IsAtEnd();
        app.Advance()) {
     const base::DictionaryValue* app_dict;
@@ -1978,8 +1977,8 @@
     return packages;
   }
 
-  const base::DictionaryValue* package_prefs =
-      prefs_->GetDictionary(arc::prefs::kArcPackages);
+  const base::DictionaryValue* package_prefs = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(arc::prefs::kArcPackages));
   for (base::DictionaryValue::Iterator package(*package_prefs);
        !package.IsAtEnd(); package.Advance()) {
     const base::DictionaryValue* package_info;
@@ -2001,8 +2000,8 @@
 
 base::Time ArcAppListPrefs::GetInstallTime(const std::string& app_id) const {
   const base::DictionaryValue* app = nullptr;
-  const base::DictionaryValue* apps =
-      prefs_->GetDictionary(arc::prefs::kArcApps);
+  const base::DictionaryValue* apps = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(arc::prefs::kArcApps));
   if (!apps || !apps->GetDictionaryWithoutPathExpansion(app_id, &app))
     return base::Time();
 
diff --git a/chrome/browser/ui/app_list/arc/arc_app_utils.cc b/chrome/browser/ui/app_list/arc/arc_app_utils.cc
index 8af50bf6..5f558b3f 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_utils.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_utils.cc
@@ -482,7 +482,7 @@
   const Profile* const profile = Profile::FromBrowserContext(context);
   const PrefService* prefs = profile->GetPrefs();
 
-  const base::ListValue* selected_package_prefs =
+  const base::Value* selected_package_prefs =
       prefs->GetList(arc::prefs::kArcFastAppReinstallPackages);
   for (const base::Value& item : selected_package_prefs->GetList()) {
     std::string item_str = item.is_string() ? item.GetString() : std::string();
diff --git a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
index e65cb650..d6885fa 100644
--- a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
+++ b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
@@ -123,7 +123,8 @@
 
 // Returns true if default app |app_id| is marked as hidden in the prefs.
 bool IsAppHidden(const PrefService* prefs, const std::string& app_id) {
-  const base::DictionaryValue* apps_dict = prefs->GetDictionary(kDefaultApps);
+  const base::DictionaryValue* apps_dict =
+      &base::Value::AsDictionaryValue(*prefs->GetDictionary(kDefaultApps));
   const base::DictionaryValue* app_dict;
   if (!apps_dict || !apps_dict->GetDictionary(app_id, &app_dict))
     return false;
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger.cc b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger.cc
index 226d59e6..8840f561 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger.cc
@@ -223,8 +223,10 @@
 
   PrefService* pref_service = profile->GetPrefs();
   if (pref_service) {
-    arc_apps_ = pref_service->GetDictionary(arc::prefs::kArcApps);
-    arc_packages_ = pref_service->GetDictionary(arc::prefs::kArcPackages);
+    arc_apps_ = &base::Value::AsDictionaryValue(
+        *pref_service->GetDictionary(arc::prefs::kArcApps));
+    arc_packages_ = &base::Value::AsDictionaryValue(
+        *pref_service->GetDictionary(arc::prefs::kArcPackages));
   }
 }
 
diff --git a/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc b/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
index 91d9919..50066df 100644
--- a/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
+++ b/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
@@ -25,6 +25,8 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
+#include "chrome/browser/ash/app_restore/app_restore_arc_test_helper.h"
+#include "chrome/browser/ash/app_restore/app_restore_test_util.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/ui/user_adding_screen.h"
@@ -49,6 +51,7 @@
 #include "chromeos/ui/base/window_state_type.h"
 #include "components/app_restore/app_launch_info.h"
 #include "components/app_restore/features.h"
+#include "components/app_restore/full_restore_save_handler.h"
 #include "components/app_restore/full_restore_utils.h"
 #include "components/app_restore/restore_data.h"
 #include "components/app_restore/window_properties.h"
@@ -236,13 +239,10 @@
                               ash::features::kDesksTemplates},
         /*disabled_features=*/{});
   }
+  DesksTemplatesClientTest(const DesksTemplatesClientTest&) = delete;
+  DesksTemplatesClientTest& operator=(const DesksTemplatesClientTest&) = delete;
   ~DesksTemplatesClientTest() override = default;
 
-  void SetUpOnMainThread() override {
-    ::full_restore::SetActiveProfilePath(profile()->GetPath());
-    extensions::PlatformAppBrowserTest::SetUpOnMainThread();
-  }
-
   void SetTemplate(std::unique_ptr<ash::DeskTemplate> launch_template) {
     DesksTemplatesClient::Get()->launch_template_for_test_ =
         std::move(launch_template);
@@ -296,6 +296,12 @@
                : web_app::LaunchWebAppBrowserAndWait(profile(), app_id);
   }
 
+  // extensions::PlatformAppBrowserTest:
+  void SetUpOnMainThread() override {
+    ::full_restore::SetActiveProfilePath(profile()->GetPath());
+    extensions::PlatformAppBrowserTest::SetUpOnMainThread();
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -1126,10 +1132,122 @@
             settings_window->parent());
 }
 
+class DesksTemplatesClientArcTest : public InProcessBrowserTest {
+ public:
+  DesksTemplatesClientArcTest() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{full_restore::features::kFullRestore,
+                              ash::features::kDesksTemplates},
+        /*disabled_features=*/{});
+  }
+  DesksTemplatesClientArcTest(const DesksTemplatesClientArcTest&) = delete;
+  DesksTemplatesClientArcTest& operator=(const DesksTemplatesClientArcTest&) =
+      delete;
+  ~DesksTemplatesClientArcTest() override = default;
+
+  ash::AppRestoreArcTestHelper* arc_helper() { return &arc_helper_; }
+
+  // InProcessBrowserTest:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    arc_helper_.SetUpCommandLine(command_line);
+    InProcessBrowserTest::SetUpCommandLine(command_line);
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    arc_helper_.SetUpInProcessBrowserTestFixture();
+    InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
+  }
+
+  void SetUpOnMainThread() override {
+    arc_helper_.SetUpOnMainThread(browser()->profile());
+    InProcessBrowserTest::SetUpOnMainThread();
+  }
+
+ private:
+  ash::AppRestoreArcTestHelper arc_helper_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Tests that launching a template that contains an ARC app works as expected.
+IN_PROC_BROWSER_TEST_F(DesksTemplatesClientArcTest,
+                       NativeUILaunchTemplateWithArcApp) {
+  auto* desk_model = DesksTemplatesClient::Get()->GetDeskModel();
+  ASSERT_EQ(0, desk_model->GetEntryCount());
+
+  constexpr char kTestAppPackage[] = "test.arc.app.package";
+  arc_helper()->InstallTestApps(kTestAppPackage, /*multi_app=*/false);
+  const std::string app_id = ash::GetTestApp1Id(kTestAppPackage);
+
+  int32_t session_id1 =
+      full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId();
+
+  // Create the window for app1. The task id needs to match the
+  // `window_app_id` arg of `CreateExoWindow`.
+  const int32_t kTaskId1 = 100;
+  views::Widget* widget = ash::CreateExoWindow("org.chromium.arc.100");
+  widget->SetBounds(gfx::Rect(500, 500));
+  full_restore::SaveAppLaunchInfo(browser()->profile()->GetPath(),
+                                  std::make_unique<app_restore::AppLaunchInfo>(
+                                      app_id, ui::EventFlags::EF_NONE,
+                                      session_id1, display::kDefaultDisplayId));
+
+  // Simulate creating the task.
+  arc_helper()->CreateTask(app_id, kTaskId1, session_id1);
+
+  // Enter overview and save the current desk as a template.
+  ash::ToggleOverview();
+  ash::WaitForOverviewEnterAnimation();
+  views::Button* save_desk_as_template_button =
+      ash::GetSaveDeskAsTemplateButton();
+  ASSERT_TRUE(save_desk_as_template_button);
+  ClickButton(save_desk_as_template_button);
+  ash::WaitForDesksTemplatesUI();
+  ASSERT_EQ(1, desk_model->GetEntryCount());
+
+  // Exit overview and close the Arc window. We'll need to verify if it
+  // reopens later.
+  ash::ToggleOverview();
+  ash::WaitForOverviewExitAnimation();
+  widget->CloseNow();
+  arc_helper()->GetAppHost()->OnTaskDestroyed(kTaskId1);
+
+  // Enter overview, head over to the desks templates grid and launch the
+  // template.
+  ash::ToggleOverview();
+  ash::WaitForOverviewEnterAnimation();
+  views::Button* zero_state_templates_button =
+      ash::GetZeroStateDesksTemplatesButton();
+  ASSERT_TRUE(zero_state_templates_button);
+  ClickButton(zero_state_templates_button);
+
+  ash::WaitForDesksTemplatesUI();
+  views::Button* template_item = ash::GetTemplateItemButton(/*index=*/0);
+  ASSERT_TRUE(template_item);
+  ClickButton(template_item);
+
+  ash::WaitForDesksTemplatesUI();
+  ash::ToggleOverview();
+  ash::WaitForOverviewExitAnimation();
+
+  // Create the window to simulate launching the ARC app.
+  const int32_t kTaskId2 = 200;
+  auto* widget1 = ash::CreateExoWindow("org.chromium.arc.200");
+  auto* window1 = widget1->GetNativeWindow();
+  arc_helper()->CreateTask(app_id, kTaskId2, session_id1);
+
+  // Tests that the ARC app is launched on desk 2.
+  EXPECT_EQ(ash::Shell::GetContainer(window1->GetRootWindow(),
+                                     ash::kShellWindowId_DeskContainerB),
+            window1->parent());
+
+  widget1->CloseNow();
+  arc_helper()->GetAppHost()->OnTaskDestroyed(kTaskId2);
+  arc_helper()->StopInstance();
+}
+
 // TODO(crbug.com/1273532): Add more tests:
 // - Deleting templates.
 // - Launching templates with uninstalled apps.
-// - Launching ARC apps.
 // - Launching ARC apps which already have an instance open.
 // - Test for spoken feedback.
 // - Port tests that use `DesksTemplatesClient` directly. These were meant to
diff --git a/chrome/browser/ui/ash/in_session_auth_dialog_client.cc b/chrome/browser/ui/ash/in_session_auth_dialog_client.cc
index f9b9df70..503a152 100644
--- a/chrome/browser/ui/ash/in_session_auth_dialog_client.cc
+++ b/chrome/browser/ui/ash/in_session_auth_dialog_client.cc
@@ -25,10 +25,10 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 
-using chromeos::AuthStatusConsumer;
-using chromeos::ExtendedAuthenticator;
-using chromeos::Key;
-using chromeos::UserContext;
+using ::ash::AuthStatusConsumer;
+using ::ash::ExtendedAuthenticator;
+using ::ash::Key;
+using ::ash::UserContext;
 
 namespace {
 
@@ -109,7 +109,7 @@
   DCHECK(user);
   UserContext user_context(*user);
   user_context.SetKey(
-      Key(chromeos::Key::KEY_TYPE_PASSWORD_PLAIN, std::string(), password));
+      Key(Key::KEY_TYPE_PASSWORD_PLAIN, std::string(), password));
   user_context.SetIsUsingPin(authenticated_by_pin);
   user_context.SetSyncPasswordData(password_manager::PasswordHashData(
       user->GetAccountId().GetUserEmail(), base::UTF8ToUTF16(password),
@@ -234,8 +234,7 @@
 }
 
 // AuthStatusConsumer:
-void InSessionAuthDialogClient::OnAuthFailure(
-    const chromeos::AuthFailure& error) {
+void InSessionAuthDialogClient::OnAuthFailure(const ash::AuthFailure& error) {
   if (pending_auth_state_) {
     std::move(pending_auth_state_->callback).Run(false);
     pending_auth_state_.reset();
diff --git a/chrome/browser/ui/ash/in_session_auth_dialog_client.h b/chrome/browser/ui/ash/in_session_auth_dialog_client.h
index 512023c..8c0b108 100644
--- a/chrome/browser/ui/ash/in_session_auth_dialog_client.h
+++ b/chrome/browser/ui/ash/in_session_auth_dialog_client.h
@@ -22,7 +22,7 @@
 
 // Handles method calls sent from Ash to ChromeOS.
 class InSessionAuthDialogClient : public ash::InSessionAuthDialogClient,
-                                  public chromeos::AuthStatusConsumer {
+                                  public ash::AuthStatusConsumer {
  public:
   using AuthenticateCallback = base::OnceCallback<void(bool)>;
 
@@ -53,12 +53,12 @@
   aura::Window* OpenInSessionAuthHelpPage() const override;
 
   // AuthStatusConsumer:
-  void OnAuthFailure(const chromeos::AuthFailure& error) override;
-  void OnAuthSuccess(const chromeos::UserContext& user_context) override;
+  void OnAuthFailure(const ash::AuthFailure& error) override;
+  void OnAuthSuccess(const ash::UserContext& user_context) override;
 
   // For testing:
   void SetExtendedAuthenticator(
-      scoped_refptr<chromeos::ExtendedAuthenticator> extended_authenticator) {
+      scoped_refptr<ash::ExtendedAuthenticator> extended_authenticator) {
     extended_authenticator_ = std::move(extended_authenticator);
   }
 
@@ -76,21 +76,20 @@
 
   // Returns a pointer to the ExtendedAuthenticator instance if there is one.
   // Otherwise creates one.
-  chromeos::ExtendedAuthenticator* GetExtendedAuthenticator();
+  ash::ExtendedAuthenticator* GetExtendedAuthenticator();
 
-  void AuthenticateWithPassword(const chromeos::UserContext& user_context);
+  void AuthenticateWithPassword(const ash::UserContext& user_context);
 
-  void OnPinAttemptDone(const chromeos::UserContext& user_context,
-                        bool success);
+  void OnPinAttemptDone(const ash::UserContext& user_context, bool success);
 
-  void OnPasswordAuthSuccess(const chromeos::UserContext& user_context);
+  void OnPasswordAuthSuccess(const ash::UserContext& user_context);
 
   void OnFingerprintAuthDone(
       base::OnceCallback<void(bool, ash::FingerprintState)> callback,
       user_data_auth::CryptohomeErrorCode error);
 
   // Used to authenticate the user to unlock supervised users.
-  scoped_refptr<chromeos::ExtendedAuthenticator> extended_authenticator_;
+  scoped_refptr<ash::ExtendedAuthenticator> extended_authenticator_;
 
   // State associated with a pending authentication attempt.
   absl::optional<AuthState> pending_auth_state_;
diff --git a/chrome/browser/ui/ash/in_session_auth_dialog_client_unittest.cc b/chrome/browser/ui/ash/in_session_auth_dialog_client_unittest.cc
index 2f9d7783..e79ba95 100644
--- a/chrome/browser/ui/ash/in_session_auth_dialog_client_unittest.cc
+++ b/chrome/browser/ui/ash/in_session_auth_dialog_client_unittest.cc
@@ -18,9 +18,9 @@
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using chromeos::FakeExtendedAuthenticator;
-using chromeos::Key;
-using chromeos::UserContext;
+using ::ash::FakeExtendedAuthenticator;
+using ::ash::Key;
+using ::ash::UserContext;
 
 namespace {
 
@@ -104,7 +104,7 @@
       user_manager::UserManager::Get()->GetActiveUser();
   UserContext expected_user_context(*user);
   expected_user_context.SetKey(
-      Key(chromeos::Key::KEY_TYPE_PASSWORD_PLAIN, std::string(), kPassword));
+      Key(Key::KEY_TYPE_PASSWORD_PLAIN, std::string(), kPassword));
 
   SetExpectedContext(expected_user_context);
 
@@ -127,7 +127,7 @@
       user_manager::UserManager::Get()->GetActiveUser();
   UserContext expected_user_context(*user);
   expected_user_context.SetKey(
-      Key(chromeos::Key::KEY_TYPE_PASSWORD_PLAIN, std::string(), kPassword));
+      Key(Key::KEY_TYPE_PASSWORD_PLAIN, std::string(), kPassword));
 
   SetExpectedContext(expected_user_context);
 
diff --git a/chrome/browser/ui/ash/login_screen_client_impl.cc b/chrome/browser/ui/ash/login_screen_client_impl.cc
index d188b29..e6128f5d 100644
--- a/chrome/browser/ui/ash/login_screen_client_impl.cc
+++ b/chrome/browser/ui/ash/login_screen_client_impl.cc
@@ -298,8 +298,8 @@
   DCHECK(!ash::ScreenLocker::default_screen_locker());
   if (ash::LoginDisplayHost::default_host()) {
     ash::LoginDisplayHost::default_host()->GetExistingUserController()->Login(
-        chromeos::UserContext(user_manager::USER_TYPE_GUEST,
-                              user_manager::GuestAccountId()),
+        ash::UserContext(user_manager::USER_TYPE_GUEST,
+                         user_manager::GuestAccountId()),
         ash::SigninSpecifics());
   }
 }
diff --git a/chrome/browser/ui/ash/multi_user/multi_profile_support.cc b/chrome/browser/ui/ash/multi_user/multi_profile_support.cc
index df70a57..9b84a0129 100644
--- a/chrome/browser/ui/ash/multi_user/multi_profile_support.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_profile_support.cc
@@ -141,8 +141,7 @@
         account_id_to_app_observer_.find(account_id);
     if (app_observer_iterator != account_id_to_app_observer_.end()) {
       extensions::AppWindowRegistry::Get(*it)->RemoveObserver(
-          app_observer_iterator->second);
-      delete app_observer_iterator->second;
+          app_observer_iterator->second.get());
       account_id_to_app_observer_.erase(app_observer_iterator);
     }
   }
@@ -173,9 +172,9 @@
     return;
 
   account_id_to_app_observer_[account_id] =
-      new AppObserver(account_id.GetUserEmail());
+      std::make_unique<AppObserver>(account_id.GetUserEmail());
   extensions::AppWindowRegistry::Get(profile)->AddObserver(
-      account_id_to_app_observer_[account_id]);
+      account_id_to_app_observer_[account_id].get());
 
   // Account all existing application windows of this user accordingly.
   const extensions::AppWindowRegistry::AppWindowList& app_windows =
diff --git a/chrome/browser/ui/ash/multi_user/multi_profile_support.h b/chrome/browser/ui/ash/multi_user/multi_profile_support.h
index 269ba8fc..d69abe5 100644
--- a/chrome/browser/ui/ash/multi_user/multi_profile_support.h
+++ b/chrome/browser/ui/ash/multi_user/multi_profile_support.h
@@ -74,7 +74,8 @@
   // tests.
   static MultiProfileSupport* instance_;
 
-  using AccountIdToAppWindowObserver = std::map<AccountId, AppObserver*>;
+  using AccountIdToAppWindowObserver =
+      std::map<AccountId, std::unique_ptr<AppObserver>>;
 
   // A list of all known users and their app window observers.
   AccountIdToAppWindowObserver account_id_to_app_observer_;
diff --git a/chrome/browser/ui/ash/shelf/browser_app_shelf_controller.cc b/chrome/browser/ui/ash/shelf/browser_app_shelf_controller.cc
index 965bc4e..8fd7c75 100644
--- a/chrome/browser/ui/ash/shelf/browser_app_shelf_controller.cc
+++ b/chrome/browser/ui/ash/shelf/browser_app_shelf_controller.cc
@@ -214,6 +214,7 @@
   } else {
     // No active app for that window: it's mapped to the browser's shelf item,
     // which must be present.
+    app_id = browser_app_id;
     shelf_id = ash::ShelfID(browser_app_id);
     DCHECK(model_.ItemByID(shelf_id));
     BrowserWindowMustBeValid(browser_window);
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc
index acfaa229..da5f9e67 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc
@@ -119,7 +119,7 @@
     return AppListControllerDelegate::PIN_EDITABLE;
   }
 
-  const base::ListValue* policy_apps =
+  const base::Value* policy_apps =
       profile->GetPrefs()->GetList(prefs::kPolicyPinnedLauncherApps);
   if (!policy_apps)
     return AppListControllerDelegate::PIN_EDITABLE;
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 73f14a8..35b575a 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -829,7 +829,6 @@
       password_manager::ContentPasswordManagerDriver::GetForRenderFrameHost(
           rfh);
   if (driver) {
-    driver->GetPasswordGenerationHelper()->ProcessPasswordRequirements(forms);
     driver->GetPasswordManager()->ProcessAutofillPredictions(driver, forms);
   }
 }
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index 353747e..51d8425 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -743,8 +743,13 @@
 }
 
 // Tests that the tapping gesture with cntl/cmd key on a link open the
-// backgournd tab.
-IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, TapGestureWithCtrlKey) {
+// background tab.
+#if defined(OS_WIN)
+#define MAYBE_TapGestureWithCtrlKey DISABLED_TapGestureWithCtrlKey
+#else
+#define MAYBE_TapGestureWithCtrlKey TapGestureWithCtrlKey
+#endif
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, MAYBE_TapGestureWithCtrlKey) {
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
 
   GURL url(embedded_test_server()->GetURL(
diff --git a/chrome/browser/ui/browser_window_state.cc b/chrome/browser/ui/browser_window_state.cc
index 264ece3..9ce79d2 100644
--- a/chrome/browser/ui/browser_window_state.cc
+++ b/chrome/browser/ui/browser_window_state.cc
@@ -112,10 +112,10 @@
     PrefService* prefs) {
   DCHECK(!window_name.empty());
   if (prefs->FindPreference(window_name))
-    return prefs->GetDictionary(window_name);
+    return &base::Value::AsDictionaryValue(*prefs->GetDictionary(window_name));
 
-  const base::DictionaryValue* app_windows =
-      prefs->GetDictionary(prefs::kAppWindowPlacement);
+  const base::DictionaryValue* app_windows = &base::Value::AsDictionaryValue(
+      *prefs->GetDictionary(prefs::kAppWindowPlacement));
   if (!app_windows)
     return nullptr;
   const base::DictionaryValue* to_return = nullptr;
diff --git a/chrome/browser/ui/cocoa/window_size_autosaver.mm b/chrome/browser/ui/cocoa/window_size_autosaver.mm
index a0fe83e..536484f 100644
--- a/chrome/browser/ui/cocoa/window_size_autosaver.mm
+++ b/chrome/browser/ui/cocoa/window_size_autosaver.mm
@@ -74,7 +74,8 @@
 
 - (void)restore {
   // Get the positioning information.
-  const base::DictionaryValue* windowPrefs = _prefService->GetDictionary(_path);
+  const base::DictionaryValue* windowPrefs =
+      &base::Value::AsDictionaryValue(*_prefService->GetDictionary(_path));
   if ([_window styleMask] & NSResizableWindowMask) {
     int x1, x2, y1, y2;
     if (!windowPrefs->GetInteger("left", &x1) ||
diff --git a/chrome/browser/ui/cocoa/window_size_autosaver_unittest.mm b/chrome/browser/ui/cocoa/window_size_autosaver_unittest.mm
index a64897c3..c247d81 100644
--- a/chrome/browser/ui/cocoa/window_size_autosaver_unittest.mm
+++ b/chrome/browser/ui/cocoa/window_size_autosaver_unittest.mm
@@ -47,7 +47,7 @@
   ASSERT_TRUE(pref);
 
   // Check to make sure there is no existing pref for window placement.
-  const base::DictionaryValue* placement = pref->GetDictionary(path_);
+  const base::Value* placement = pref->GetDictionary(path_);
   ASSERT_TRUE(placement);
   EXPECT_TRUE(placement->DictEmpty());
 
@@ -98,7 +98,8 @@
   // ...and it should be in the profile, too.
   EXPECT_TRUE(pref->GetDictionary(path_));
   int x, y;
-  const base::DictionaryValue* windowPref = pref->GetDictionary(path_);
+  const base::DictionaryValue* windowPref =
+      &base::Value::AsDictionaryValue(*pref->GetDictionary(path_));
   EXPECT_FALSE(windowPref->GetInteger("left", &x));
   EXPECT_FALSE(windowPref->GetInteger("right", &x));
   EXPECT_FALSE(windowPref->GetInteger("top", &x));
@@ -114,7 +115,7 @@
   ASSERT_TRUE(pref);
 
   // Check to make sure there is no existing pref for window placement.
-  const base::DictionaryValue* placement = pref->GetDictionary(path_);
+  const base::Value* placement = pref->GetDictionary(path_);
   ASSERT_TRUE(placement);
   EXPECT_TRUE(placement->DictEmpty());
 
@@ -157,7 +158,8 @@
   // ...and it should be in the profile, too.
   EXPECT_TRUE(pref->GetDictionary(path_));
   int x1, y1, x2, y2;
-  const base::DictionaryValue* windowPref = pref->GetDictionary(path_);
+  const base::DictionaryValue* windowPref =
+      &base::Value::AsDictionaryValue(*pref->GetDictionary(path_));
   EXPECT_FALSE(windowPref->GetInteger("x", &x1));
   EXPECT_FALSE(windowPref->GetInteger("y", &x1));
   ASSERT_TRUE(windowPref->GetInteger("left", &x1));
diff --git a/chrome/browser/ui/hats/hats_service.cc b/chrome/browser/ui/hats/hats_service.cc
index e0bc852c..c401c8b 100644
--- a/chrome/browser/ui/hats/hats_service.cc
+++ b/chrome/browser/ui/hats/hats_service.cc
@@ -586,7 +586,7 @@
     return false;
   }
 
-  const base::DictionaryValue* pref_data =
+  const base::Value* pref_data =
       profile_->GetPrefs()->GetDictionary(prefs::kHatsSurveyMetadata);
   absl::optional<int> last_major_version =
       pref_data->FindIntPath(GetMajorVersionPath(trigger));
@@ -655,7 +655,7 @@
   // confrontational manner than the standard HaTS prompt). The bar for whether
   // a user is eligible is thus lower for these types of surveys.
   if (!user_prompted) {
-    const base::DictionaryValue* pref_data =
+    const base::Value* pref_data =
         profile_->GetPrefs()->GetDictionary(prefs::kHatsSurveyMetadata);
 
     // If the profile is too new, measured as the age of the profile directory,
@@ -710,7 +710,7 @@
   // Check the survey status in profile first.
   // We record the survey's over capacity information in user profile to avoid
   // duplicated checks since the survey won't change once it is full.
-  const base::DictionaryValue* pref_data =
+  const base::Value* pref_data =
       profile_->GetPrefs()->GetDictionary(prefs::kHatsSurveyMetadata);
   absl::optional<int> is_full =
       pref_data->FindBoolPath(GetIsSurveyFull(trigger));
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
index b30922b7..c8a90e8 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
@@ -122,7 +122,7 @@
 }
 
 bool ChromeOmniboxClient::IsDefaultSearchProviderEnabled() const {
-  const base::DictionaryValue* url_dict = profile_->GetPrefs()->GetDictionary(
+  const base::Value* url_dict = profile_->GetPrefs()->GetDictionary(
       DefaultSearchManager::kDefaultSearchProviderDataPrefName);
   return !url_dict->FindBoolPath(DefaultSearchManager::kDisabledByPolicy)
               .value_or(false);
diff --git a/chrome/browser/ui/search/omnibox_utils.cc b/chrome/browser/ui/search/omnibox_utils.cc
index c85e51a4..e217cc7d 100644
--- a/chrome/browser/ui/search/omnibox_utils.cc
+++ b/chrome/browser/ui/search/omnibox_utils.cc
@@ -9,10 +9,9 @@
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/omnibox/clipboard_utils.h"
 #include "components/omnibox/browser/omnibox_edit_model.h"
-#include "components/omnibox/browser/omnibox_view.h"
 #include "content/public/browser/web_contents.h"
 
-namespace {
+namespace search {
 
 OmniboxView* GetOmniboxView(content::WebContents* web_contents) {
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
@@ -21,10 +20,6 @@
   return browser->window()->GetLocationBar()->GetOmniboxView();
 }
 
-}  // namespace
-
-namespace search {
-
 void FocusOmnibox(bool focus, content::WebContents* web_contents) {
   OmniboxView* omnibox_view = GetOmniboxView(web_contents);
   if (!omnibox_view)
diff --git a/chrome/browser/ui/search/omnibox_utils.h b/chrome/browser/ui/search/omnibox_utils.h
index 7dca840..47f5c6a 100644
--- a/chrome/browser/ui/search/omnibox_utils.h
+++ b/chrome/browser/ui/search/omnibox_utils.h
@@ -5,12 +5,16 @@
 #ifndef CHROME_BROWSER_UI_SEARCH_OMNIBOX_UTILS_H_
 #define CHROME_BROWSER_UI_SEARCH_OMNIBOX_UTILS_H_
 
+class OmniboxView;
 namespace content {
 class WebContents;
 }  // namespace content
 
 namespace search {
 
+// Returns the omnibox view from the browser instance associated with
+// `web_contents`, if any, or nullptr otherwise.
+OmniboxView* GetOmniboxView(content::WebContents* web_contents);
 // Focus or unfocus the omnibox if |focus| is true or false respectively.
 void FocusOmnibox(bool focus, content::WebContents* web_contents);
 // Returns whether input is in progress, i.e. if the omnibox has focus and the
diff --git a/chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller_chromeos_browsertest.cc b/chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller_chromeos_browsertest.cc
deleted file mode 100644
index ade0e5a..0000000
--- a/chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller_chromeos_browsertest.cc
+++ /dev/null
@@ -1,47 +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 "chrome/browser/sharesheet/sharesheet_service.h"
-#include "chrome/browser/sharesheet/sharesheet_service_factory.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/page_action/page_action_icon_type.h"
-#include "chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "content/public/test/browser_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-class SharingHubBubbleControllerChromeOsBrowserTest
-    : public InProcessBrowserTest {
- public:
-  SharingHubBubbleControllerChromeOsBrowserTest() = default;
-  ~SharingHubBubbleControllerChromeOsBrowserTest() override = default;
-};
-
-IN_PROC_BROWSER_TEST_F(SharingHubBubbleControllerChromeOsBrowserTest,
-                       OpenSharesheet) {
-  sharesheet::SharesheetService* sharesheet_service =
-      sharesheet::SharesheetServiceFactory::GetForProfile(browser()->profile());
-  gfx::NativeWindow web_contents_containing_window_ =
-      browser()
-          ->tab_strip_model()
-          ->GetActiveWebContents()
-          ->GetTopLevelNativeWindow();
-
-  // Open the sharesheet using the sharing hub controller.
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  sharing_hub::SharingHubBubbleController::CreateOrGetFromWebContents(
-      web_contents)
-      ->ShowBubble();
-
-  // Verify that the sharesheet is open.
-  sharesheet::SharesheetController* controller =
-      sharesheet_service->GetSharesheetController(
-          web_contents_containing_window_);
-  ASSERT_TRUE(controller->IsBubbleVisible());
-}
-
-}  // namespace
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index 7a92f1a..f349895 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -1626,7 +1626,14 @@
   return web_app::test::InstallWebApp(profile, std::move(web_app_info));
 }
 
-IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest, ListAppsForAllProfiles) {
+// TODO(crbug.com/1281009): Fix flakes and re-enable.
+#if defined(OS_WIN)
+#define MAYBE_ListAppsForAllProfiles DISABLED_ListAppsForAllProfiles
+#else
+#define MAYBE_ListAppsForAllProfiles ListAppsForAllProfiles
+#endif
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest,
+                       MAYBE_ListAppsForAllProfiles) {
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   base::FilePath user_data_dir = profile_manager->user_data_dir();
   Profile* profile1 = browser()->profile();
@@ -1733,7 +1740,14 @@
   CloseBrowserSynchronously(app_browser2);
 }
 
-IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest, ListAppsForGivenProfile) {
+// TODO(crbug.com/1281009): Fix flakes and re-enable.
+#if defined(OS_WIN)
+#define MAYBE_ListAppsForGivenProfile DISABLED_ListAppsForGivenProfile
+#else
+#define MAYBE_ListAppsForGivenProfile ListAppsForGivenProfile
+#endif
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest,
+                       MAYBE_ListAppsForGivenProfile) {
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   base::FilePath user_data_dir = profile_manager->user_data_dir();
   Profile* profile1 = browser()->profile();
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 51874c3e..a4b2207 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/breadcrumbs/breadcrumb_manager_tab_helper.h"
 #include "chrome/browser/breadcrumbs/breadcrumbs_status.h"
+#include "chrome/browser/browser_features.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/buildflags.h"
 #include "chrome/browser/captive_portal/captive_portal_service_factory.h"
@@ -148,6 +149,7 @@
 #else
 #include "chrome/browser/accuracy_tips/accuracy_service_factory.h"
 #include "chrome/browser/banners/app_banner_manager_desktop.h"
+#include "chrome/browser/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper.h"
 #include "chrome/browser/tab_contents/form_interaction_tab_helper.h"
 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
 #include "chrome/browser/ui/intent_picker_tab_helper.h"
@@ -157,6 +159,7 @@
 #include "chrome/browser/ui/sync/browser_synced_tab_delegate.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "components/accuracy_tips/accuracy_web_contents_observer.h"
+#include "components/omnibox/common/omnibox_features.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "components/zoom/zoom_controller.h"
 #endif  // defined(OS_ANDROID)
@@ -186,6 +189,10 @@
 #include "chrome/browser/ui/cocoa/screentime/tab_helper.h"
 #endif
 
+#if defined(OS_WIN)
+#include "chrome/browser/font_prewarmer_tab_helper.h"
+#endif
+
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
 #include "components/captive_portal/content/captive_portal_tab_helper.h"
 #endif
@@ -455,6 +462,9 @@
     ThumbnailTabHelper::CreateForWebContents(web_contents);
   }
   web_modal::WebContentsModalDialogManager::CreateForWebContents(web_contents);
+  if (base::FeatureList::IsEnabled(omnibox::kZeroSuggestPrefetching)) {
+    ZeroSuggestPrefetchTabHelper::CreateForWebContents(web_contents);
+  }
 #endif
 
 #if defined(OS_MAC)
@@ -502,6 +512,11 @@
   }
 #endif
 
+#if defined(OS_WIN)
+  if (base::FeatureList::IsEnabled(features::kPrewarmSearchResultsPageFonts))
+    FontPrewarmerTabHelper::CreateForWebContents(web_contents);
+#endif
+
   // --- Section 3: Feature tab helpers behind BUILDFLAGs ---
   // NOT for "if enabled"; put those in section 1.
 
diff --git a/chrome/browser/ui/task_manager/task_manager_table_model.cc b/chrome/browser/ui/task_manager/task_manager_table_model.cc
index b8a8055..a726747 100644
--- a/chrome/browser/ui/task_manager/task_manager_table_model.cc
+++ b/chrome/browser/ui/task_manager/task_manager_table_model.cc
@@ -817,9 +817,9 @@
   if (!g_browser_process->local_state())
     return;
 
-  const base::DictionaryValue* dictionary =
-      g_browser_process->local_state()->GetDictionary(
-          prefs::kTaskManagerColumnVisibility);
+  const base::DictionaryValue* dictionary = &base::Value::AsDictionaryValue(
+      *g_browser_process->local_state()->GetDictionary(
+          prefs::kTaskManagerColumnVisibility));
   if (!dictionary)
     return;
 
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 5046983..b6497da 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -48,6 +48,10 @@
     "ChromeWhatsNewInMainMenuNewBadge", base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
+// Whether to use download bubble instead of download shelf.
+const base::Feature kDownloadBubble{"DownloadBubble",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+
 #if !defined(ANDROID)
 // Enables "Access Code Cast" UI.
 const base::Feature kAccessCodeCastUI{"AccessCodeCastUI",
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index de3c934c..65001db 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -37,6 +37,8 @@
 
 extern const base::Feature kCommander;
 
+extern const base::Feature kDownloadBubble;
+
 #if !defined(ANDROID)
 extern const base::Feature kAccessCodeCastUI;
 #endif
diff --git a/chrome/browser/ui/user_education/feature_promo_snooze_service.cc b/chrome/browser/ui/user_education/feature_promo_snooze_service.cc
index e4214fa..21fe71b 100644
--- a/chrome/browser/ui/user_education/feature_promo_snooze_service.cc
+++ b/chrome/browser/ui/user_education/feature_promo_snooze_service.cc
@@ -189,7 +189,7 @@
 FeaturePromoSnoozeService::ReadSnoozeData(const base::Feature& iph_feature) {
   std::string path_prefix = std::string(iph_feature.name) + ".";
 
-  const base::DictionaryValue* pref_data =
+  const base::Value* pref_data =
       profile_->GetPrefs()->GetDictionary(kIPHSnoozeDataPath);
   absl::optional<bool> is_dismissed =
       pref_data->FindBoolPath(path_prefix + kIPHIsDismissedPath);
diff --git a/chrome/browser/ui/views/chrome_views_delegate.cc b/chrome/browser/ui/views/chrome_views_delegate.cc
index 83a9c755..5484dad 100644
--- a/chrome/browser/ui/views/chrome_views_delegate.cc
+++ b/chrome/browser/ui/views/chrome_views_delegate.cc
@@ -117,7 +117,7 @@
     return false;
 
   DCHECK(prefs->FindPreference(window_name));
-  const base::DictionaryValue* dictionary = prefs->GetDictionary(window_name);
+  const base::Value* dictionary = prefs->GetDictionary(window_name);
   if (!dictionary)
     return false;
   absl::optional<int> left = dictionary->FindIntKey("left");
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
new file mode 100644
index 0000000..94af522
--- /dev/null
+++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
@@ -0,0 +1,103 @@
+// 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 "chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h"
+
+#include "base/bind.h"
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/download/bubble/download_display_controller.h"
+#include "chrome/browser/themes/theme_properties.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/view_ids.h"
+#include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/controls/button/button_controller.h"
+
+DownloadToolbarButtonView::DownloadToolbarButtonView(BrowserView* browser_view)
+    : ToolbarButton(
+          base::BindRepeating(&DownloadToolbarButtonView::ButtonPressed,
+                              base::Unretained(this))),
+      browser_(browser_view->browser()) {
+  button_controller()->set_notify_action(
+      views::ButtonController::NotifyAction::kOnPress);
+  SetVectorIcons(kDownloadToolbarButtonIcon, kDownloadToolbarButtonIcon);
+  GetViewAccessibility().OverrideHasPopup(ax::mojom::HasPopup::kDialog);
+  SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_DOWNLOAD_ICON));
+  Profile* profile = browser_->profile();
+  content::DownloadManager* manager = profile->GetDownloadManager();
+  // The display starts hidden and isn't shown until a download is initiated.
+  // TODO(anise): Use pref service to determine what the initial state
+  // should be.
+  SetVisible(false);
+  controller_ = std::make_unique<DownloadDisplayController>(this, manager);
+}
+
+DownloadToolbarButtonView::~DownloadToolbarButtonView() {
+  controller_.reset();
+}
+
+void DownloadToolbarButtonView::Show() {
+  SetVisible(true);
+  PreferredSizeChanged();
+}
+
+void DownloadToolbarButtonView::Hide() {
+  SetVisible(false);
+  PreferredSizeChanged();
+}
+
+bool DownloadToolbarButtonView::IsShowing() {
+  return GetVisible();
+}
+
+void DownloadToolbarButtonView::Enable() {
+  SetEnabled(true);
+}
+
+void DownloadToolbarButtonView::Disable() {
+  SetEnabled(false);
+}
+
+void DownloadToolbarButtonView::UpdateDownloadIcon(
+    download::DownloadIconState state) {
+  icon_state_ = state;
+  UpdateIcon();
+}
+
+void DownloadToolbarButtonView::UpdateIcon() {
+  if (!GetWidget())
+    return;
+
+  const gfx::VectorIcon* new_icon;
+  SkColor icon_color;
+  if (icon_state_ == download::DownloadIconState::kProgress) {
+    icon_color = GetThemeProvider()->GetColor(
+        ThemeProperties::COLOR_TAB_THROBBER_SPINNING);
+    new_icon = &kDownloadInProgressIcon;
+  } else {
+    icon_color = GetThemeProvider()->GetColor(
+        ThemeProperties::COLOR_TOOLBAR_VERTICAL_SEPARATOR);
+    new_icon = &kDownloadToolbarButtonIcon;
+  }
+
+  if (icon_color != gfx::kPlaceholderColor) {
+    for (auto state : kButtonStates) {
+      SetImageModel(state,
+                    ui::ImageModel::FromVectorIcon(*new_icon, icon_color));
+    }
+  }
+
+  // TODO(anise): Add progress ring animation.
+}
+
+// TODO(anise): Implement opening of full view.
+void DownloadToolbarButtonView::ButtonPressed() {}
+
+BEGIN_METADATA(DownloadToolbarButtonView, ToolbarButton)
+END_METADATA
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
new file mode 100644
index 0000000..006e33d
--- /dev/null
+++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
@@ -0,0 +1,47 @@
+// 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 CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_TOOLBAR_BUTTON_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_TOOLBAR_BUTTON_VIEW_H_
+
+#include "chrome/browser/download/bubble/download_display.h"
+#include "chrome/browser/download/bubble/download_icon_state.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_button.h"
+#include "ui/base/metadata/metadata_header_macros.h"
+
+class Browser;
+class BrowserView;
+class DownloadDisplayController;
+
+// Download icon shown in the trusted area of the toolbar. Its lifetime is tied
+// to that of its parent ToolbarView. The icon is made visible when downloads
+// are in progress or when a download was initiated in the past 24 hours.
+class DownloadToolbarButtonView : public ToolbarButton, public DownloadDisplay {
+ public:
+  METADATA_HEADER(DownloadToolbarButtonView);
+  explicit DownloadToolbarButtonView(BrowserView* browser_view);
+  DownloadToolbarButtonView(const DownloadToolbarButtonView&) = delete;
+  DownloadToolbarButtonView& operator=(const DownloadToolbarButtonView&) =
+      delete;
+  ~DownloadToolbarButtonView() override;
+
+  // DownloadsDisplay implementation.
+  void Show() override;
+  void Hide() override;
+  bool IsShowing() override;
+  void Enable() override;
+  void Disable() override;
+  void UpdateDownloadIcon(download::DownloadIconState state) override;
+
+  // ToolbarButton:
+  void UpdateIcon() override;
+
+ private:
+  void ButtonPressed();
+  const Browser* const browser_;
+  std::unique_ptr<DownloadDisplayController> controller_;
+  download::DownloadIconState icon_state_;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_TOOLBAR_BUTTON_VIEW_H_
diff --git a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc
index ed660cc..025c05e 100644
--- a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/ui/views/extensions/extensions_menu_item_view.h"
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
 #include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/views/accessibility/view_accessibility.h"
@@ -28,6 +29,7 @@
 #include "ui/views/controls/tabbed_pane/tabbed_pane.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/layout_provider.h"
+#include "ui/views/view.h"
 #include "ui/views/view_utils.h"
 
 namespace {
@@ -64,6 +66,19 @@
   return static_cast<ExtensionsMenuItemView*>(view);
 }
 
+// Returns the menu item view of `action_id` if it is a children of
+// `parent_view`.
+ExtensionsMenuItemView* GetMenuItemView(
+    views::View* parent_view,
+    const ToolbarActionsModel::ActionId& action_id) {
+  for (auto* view : parent_view->children()) {
+    auto* item_view = GetAsMenuItemView(view);
+    if (item_view->view_controller()->GetId() == action_id)
+      return item_view;
+  }
+  return nullptr;
+}
+
 // Returns the current index or insert position of `extension_name` in
 // `parent_view`, based on alphabetical order.
 int FindIndex(const std::u16string extension_name, views::View* parent_view) {
@@ -234,44 +249,60 @@
   Update();
 }
 
-// TODO(crbug.com/1263310): Update site access items for toolbar changes.
-
 void ExtensionsTabbedMenuView::OnToolbarActionAdded(
     const ToolbarActionsModel::ActionId& action_id) {
   auto extension_name = toolbar_model_->GetExtensionName(action_id);
   auto index = FindIndex(extension_name, installed_items_);
   CreateAndInsertInstalledExtension(action_id, index);
 
+  CreateAndInsertSiteAccessItem(action_id);
+  UpdateSiteAccessSectionsVisibility();
+
   ConsistencyCheck();
 }
 
 void ExtensionsTabbedMenuView::OnToolbarActionRemoved(
     const ToolbarActionsModel::ActionId& action_id) {
-  for (views::View* view : installed_items_->children()) {
-    if (GetAsMenuItemView(view)->view_controller()->GetId() == action_id) {
-      installed_items_->RemoveChildViewT(view);
-      break;
-    }
-  }
+  auto remove_item = [](views::View* parent_view,
+                        const ToolbarActionsModel::ActionId& action_id) {
+    auto* item_view = GetMenuItemView(parent_view, action_id);
+    if (item_view)
+      parent_view->RemoveChildViewT(item_view);
+  };
+
+  remove_item(installed_items_, action_id);
+  remove_item(requests_access_.items, action_id);
+  remove_item(has_access_.items, action_id);
+
+  UpdateSiteAccessSectionsVisibility();
 
   ConsistencyCheck();
 }
 
 void ExtensionsTabbedMenuView::OnToolbarActionUpdated(
     const ToolbarActionsModel::ActionId& action_id) {
-  for (views::View* view : installed_items_->children()) {
-    auto* item_view = GetAsMenuItemView(view);
-    if (item_view->view_controller()->GetId() == action_id) {
-      UpdateMenuItemView(item_view, installed_items_);
-      break;
-    }
-  }
+  auto update_item = [](views::View* parent_view,
+                        const ToolbarActionsModel::ActionId& action_id) {
+    auto* item_view = GetMenuItemView(parent_view, action_id);
+    if (item_view)
+      UpdateMenuItemView(item_view, parent_view);
+  };
+
+  update_item(installed_items_, action_id);
+  update_item(requests_access_.items, action_id);
+  update_item(has_access_.items, action_id);
+
+  MoveItemsBetweenSectionsIfNecessary();
+
+  UpdateSiteAccessSectionsVisibility();
 
   ConsistencyCheck();
 }
 
 void ExtensionsTabbedMenuView::OnToolbarModelInitialized() {
   DCHECK(installed_items_->children().empty());
+  DCHECK(requests_access_.items->children().empty());
+  DCHECK(has_access_.items->children().empty());
   Populate();
 }
 
@@ -322,10 +353,20 @@
 }
 
 void ExtensionsTabbedMenuView::Update() {
-  for (views::View* view : installed_items_->children()) {
-    auto* item_view = GetAsMenuItemView(view);
-    UpdateMenuItemView(item_view, installed_items_);
-  }
+  auto update_items = [](views::View* parent_view) {
+    for (views::View* view : parent_view->children()) {
+      auto* item_view = GetAsMenuItemView(view);
+      UpdateMenuItemView(item_view, parent_view);
+    }
+  };
+
+  update_items(installed_items_);
+  update_items(requests_access_.items);
+  update_items(has_access_.items);
+
+  MoveItemsBetweenSectionsIfNecessary();
+
+  UpdateSiteAccessSectionsVisibility();
 }
 
 std::unique_ptr<views::View>
@@ -404,6 +445,11 @@
       ExtensionsMenuItemView::MenuItemType::kSiteAccess, browser_,
       std::move(controller), allow_pinning_);
 
+  InsertSiteAccessItem(std::move(item));
+}
+
+void ExtensionsTabbedMenuView::InsertSiteAccessItem(
+    std::unique_ptr<ExtensionsMenuItemView> item) {
   const ToolbarActionViewController::PageInteractionStatus status =
       item->view_controller()->GetPageInteractionStatus(
           browser_->tab_strip_model()->GetActiveWebContents());
@@ -411,15 +457,49 @@
   switch (status) {
     case ToolbarActionViewController::PageInteractionStatus::kNone:
       break;
-    case ToolbarActionViewController::PageInteractionStatus::kPending:
-      requests_access_.items->AddChildView(std::move(item));
+    case ToolbarActionViewController::PageInteractionStatus::kPending: {
+      int index = FindIndex(item->view_controller()->GetActionName(),
+                            requests_access_.items);
+      requests_access_.items->AddChildViewAt(std::move(item), index);
       break;
-    case ToolbarActionViewController::PageInteractionStatus::kActive:
-      has_access_.items->AddChildView(std::move(item));
+    }
+    case ToolbarActionViewController::PageInteractionStatus::kActive: {
+      int index = FindIndex(item->view_controller()->GetActionName(),
+                            has_access_.items);
+      has_access_.items->AddChildViewAt(std::move(item), index);
       break;
+    }
   }
 }
 
+void ExtensionsTabbedMenuView::MoveItemsBetweenSectionsIfNecessary() {
+  content::WebContents* const web_contents =
+      browser_->tab_strip_model()->GetActiveWebContents();
+
+  auto move_items_between_sections_if_Necessary =
+      [web_contents, this](SiteAccessSection* section) {
+        // Collect the views to move separately, so that we don't change the
+        // children of the view during iteration.
+        std::vector<ExtensionsMenuItemView*> items_to_move;
+        for (views::View* view : section->items->children()) {
+          auto* item_view = GetAsMenuItemView(view);
+          auto item_page_status =
+              item_view->view_controller()->GetPageInteractionStatus(
+                  web_contents);
+          if (item_page_status != section->page_status)
+            items_to_move.push_back(item_view);
+        }
+
+        for (ExtensionsMenuItemView* item_view : items_to_move) {
+          auto item_view_to_move = section->items->RemoveChildViewT(item_view);
+          InsertSiteAccessItem(std::move(item_view_to_move));
+        }
+      };
+
+  move_items_between_sections_if_Necessary(&requests_access_);
+  move_items_between_sections_if_Necessary(&has_access_);
+}
+
 void ExtensionsTabbedMenuView::UpdateSiteAccessSectionsVisibility() {
   auto update_section = [](SiteAccessSection* section) {
     bool should_be_visible = !section->items->children().empty();
diff --git a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.h b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.h
index a976861..a5f6e9e 100644
--- a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.h
+++ b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.h
@@ -122,14 +122,23 @@
   // Creates and returns the site access container with empty sections.
   std::unique_ptr<views::View> CreateSiteAccessContainer();
 
-  // Adds a menu item in the installed extensions for a newly-added extension.
+  // Creates and adds a menu item for `id` in the installed extensions for a
+  // newly-added extension.
   void CreateAndInsertInstalledExtension(
       const ToolbarActionsModel::ActionId& id,
       int index);
 
-  // Adds a menu item in the corresponding site access section.
+  // Creates and adds a menu item for `id` in its corresponding site access
+  // section.
   void CreateAndInsertSiteAccessItem(const ToolbarActionsModel::ActionId& id);
 
+  // Adds `item` in its corresponding site access section.
+  void InsertSiteAccessItem(std::unique_ptr<ExtensionsMenuItemView> item);
+
+  // Moves items between site access sections if their site access status
+  // changed. Called when one or more items are updated.
+  void MoveItemsBetweenSectionsIfNecessary();
+
   // Updates the visibility of the site access sections. A given section should
   // be visible if there are any extensions displayed in it.
   void UpdateSiteAccessSectionsVisibility();
diff --git a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc
index 81e9185..11dafda 100644
--- a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc
@@ -43,12 +43,12 @@
   BrowserView* browser_view_;
 };
 
-std::vector<std::string> GetNamesFromInstalledItems(
-    std::vector<ExtensionsMenuItemView*> installed_items) {
+std::vector<std::string> GetNamesFromMenuItems(
+    std::vector<ExtensionsMenuItemView*> item_views) {
   std::vector<std::string> names;
-  names.resize(installed_items.size());
+  names.resize(item_views.size());
   std::transform(
-      installed_items.begin(), installed_items.end(), names.begin(),
+      item_views.begin(), item_views.end(), names.begin(),
       [](ExtensionsMenuItemView* item) {
         return base::UTF16ToUTF8(item->primary_action_button_for_testing()
                                      ->label_text_for_testing());
@@ -287,7 +287,7 @@
   // Basic std::sort would do A,C,Z,b however we want A,b,C,Z
   std::vector<std::string> expected_items{kExtensionAName, kExtensionBName,
                                           kExtensionCName, kExtensionZName};
-  EXPECT_EQ(GetNamesFromInstalledItems(items), expected_items);
+  EXPECT_EQ(GetNamesFromMenuItems(items), expected_items);
 }
 
 TEST_F(ExtensionsTabbedMenuViewUnitTest,
@@ -333,7 +333,7 @@
     EXPECT_EQ(items.size(), 3u);
     std::vector<std::string> expected_items{kExtensionA, kExtensionB,
                                             kExtensionC};
-    EXPECT_EQ(GetNamesFromInstalledItems(items), expected_items);
+    EXPECT_EQ(GetNamesFromMenuItems(items), expected_items);
   }
 
   // Pinning an extension should add it to the toolbar.
@@ -420,7 +420,7 @@
     std::vector<ExtensionsMenuItemView*> items = installed_items();
     ASSERT_EQ(items.size(), 2u);
     std::vector<std::string> expected_names{kExtensionA, kExtensionC};
-    EXPECT_EQ(GetNamesFromInstalledItems(items), expected_names);
+    EXPECT_EQ(GetNamesFromMenuItems(items), expected_names);
   }
 
   // Add a new extension while the menu is open.
@@ -435,7 +435,7 @@
     ASSERT_EQ(items.size(), 3u);
     std::vector<std::string> expected_names{kExtensionA, kExtensionB,
                                             kExtensionC};
-    EXPECT_EQ(GetNamesFromInstalledItems(items), expected_names);
+    EXPECT_EQ(GetNamesFromMenuItems(items), expected_names);
   }
 
   // Remove a extension while the menu is open
@@ -447,7 +447,7 @@
     std::vector<ExtensionsMenuItemView*> items = installed_items();
     ASSERT_EQ(items.size(), 2u);
     std::vector<std::string> expected_names{kExtensionA, kExtensionC};
-    EXPECT_EQ(GetNamesFromInstalledItems(items), expected_names);
+    EXPECT_EQ(GetNamesFromMenuItems(items), expected_names);
   }
 }
 
@@ -583,6 +583,55 @@
   EXPECT_EQ(requests_access_items().size(), 0u);
 }
 
+TEST_F(ExtensionsTabbedMenuViewUnitTest,
+       SiteAccessTab_AddAndRemoveExtensionWhenMenuIsOpen) {
+  constexpr char kExtensionA[] = "A Extension";
+  constexpr char kExtensionC[] = "C Extension";
+  InstallExtensionWithHostPermissions(kExtensionA, {"<all_urls>"});
+  InstallExtensionWithHostPermissions(kExtensionC, {"<all_urls>"});
+
+  const GURL url_a("http://www.a.com");
+  web_contents_tester()->NavigateAndCommit(url_a);
+  ShowSiteAccessTabInMenu();
+
+  // Verify the order of the extensions is A,C under the has access section.
+  // Note that extensions installed with all urls permissions have access by
+  // default.
+  {
+    std::vector<ExtensionsMenuItemView*> has_acess_items = has_access_items();
+    ASSERT_EQ(has_acess_items.size(), 2u);
+    std::vector<std::string> expected_names{kExtensionA, kExtensionC};
+    EXPECT_EQ(GetNamesFromMenuItems(has_acess_items), expected_names);
+  }
+
+  // Add a new extension while the menu is open.
+  constexpr char kExtensionB[] = "B Extension";
+  auto extensionB =
+      InstallExtensionWithHostPermissions(kExtensionB, {"<all_urls>"});
+  LayoutMenuIfNecessary();
+
+  // Verify the new order is A,B,C under the has access section
+  {
+    std::vector<ExtensionsMenuItemView*> has_acess_items = has_access_items();
+    ASSERT_EQ(has_acess_items.size(), 3u);
+    std::vector<std::string> expected_names{kExtensionA, kExtensionB,
+                                            kExtensionC};
+    EXPECT_EQ(GetNamesFromMenuItems(has_acess_items), expected_names);
+  }
+
+  // Remove a extension while the menu is open
+  UninstallExtension(extensionB->id());
+  LayoutMenuIfNecessary();
+
+  // Verify the new order is A,C.
+  {
+    std::vector<ExtensionsMenuItemView*> has_acess_items = has_access_items();
+    ASSERT_EQ(has_acess_items.size(), 2u);
+    std::vector<std::string> expected_names{kExtensionA, kExtensionC};
+    EXPECT_EQ(GetNamesFromMenuItems(has_acess_items), expected_names);
+  }
+}
+
 // TODO(crbug.com/1263310): Verify menu gets updated after permission changes
 // using the menu item dropdown.
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc
index 2265184..c5188f3 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc
@@ -473,6 +473,7 @@
 }
 
 void BrowserNonClientFrameViewChromeOS::OnThemeChanged() {
+  OnUpdateBackgroundColor();
   OnUpdateFrameColor();
   BrowserNonClientFrameView::OnThemeChanged();
 }
@@ -911,6 +912,34 @@
   return immersive_controller->ShouldHideTopViews();
 }
 
+void BrowserNonClientFrameViewChromeOS::OnUpdateBackgroundColor() {
+  auto* browser_view = this->browser_view();
+  if (!browser_view)
+    return;
+
+  auto* contents_web_view = browser_view->contents_web_view();
+  if (!contents_web_view)
+    return;
+
+  absl::optional<SkColor> background_color;
+  if (browser_view->GetIsWebAppType()) {
+    auto* browser = browser_view->browser();
+    if (browser && web_app::IsSystemWebApp(browser)) {
+      // The personalization app manages its own background color override to
+      // facilitate previewing of wallpaper selection which requires background
+      // transparency. That being the case, background color override for the
+      // personalization app should not be set here.
+      if (web_app::IsBrowserForSystemWebApp(
+              browser, web_app::SystemAppType::PERSONALIZATION)) {
+        return;
+      }
+      background_color = browser->app_controller()->GetBackgroundColor();
+    }
+  }
+
+  contents_web_view->SetBackgroundColorOverride(background_color);
+}
+
 void BrowserNonClientFrameViewChromeOS::OnUpdateFrameColor() {
   aura::Window* window = frame()->GetNativeWindow();
   window->SetProperty(chromeos::kFrameActiveColorKey,
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h
index f9afef3..c9c97dbd 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h
@@ -224,6 +224,9 @@
   // fullscreen.
   bool GetHideCaptionButtonsForFullscreen() const;
 
+  // Called any time the background color may have changed.
+  void OnUpdateBackgroundColor();
+
   // Called any time the frame color may have changed.
   void OnUpdateFrameColor();
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc
index 71d35f3..a1dbba6 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc
@@ -82,6 +82,7 @@
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.h"
+#include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h"
@@ -97,6 +98,7 @@
 #include "components/password_manager/core/browser/password_form.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/test/background_color_change_waiter.h"
 #include "content/public/test/content_mock_cert_verifier.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "net/dns/mock_host_resolver.h"
@@ -136,6 +138,127 @@
 using BrowserNonClientFrameViewChromeOSTouchTestWithWebUiTabStrip =
     WebUiTabStripOverrideTest<true, BrowserNonClientFrameViewChromeOSTouchTest>;
 
+// Base class for background color change browser tests parameterized by whether
+// to use a SWA or a non-SWA.
+class BrowserNonClientFrameViewChromeOSTestBackgroundColorChange
+    : public InProcessBrowserTest,
+      public testing::WithParamInterface</*use_swa=*/bool> {
+ public:
+  // Returns whether to use a SWA given test parameterization.
+  bool UseSwa() const { return GetParam(); }
+
+  // Installs an SWA or a non-SWA depending on test parameterization, returning
+  // the `AppId` of the installed app. Note that this method may only be invoked
+  // once per test.
+  web_app::AppId InstallWebApp() {
+    DCHECK(!app_id_.has_value());
+    app_id_ = UseSwa() ? InstallSWA() : InstallNonSWA();
+    return app_id_.value();
+  }
+
+  // Toggles the color mode, triggering propagation of theme change events.
+  void ToggleColorMode() {
+    auto* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
+    auto* native_theme_web = ui::NativeTheme::GetInstanceForWeb();
+
+    const bool is_dark_mode_enabled = native_theme->ShouldUseDarkColors();
+
+    native_theme->set_use_dark_colors(!is_dark_mode_enabled);
+    native_theme_web->set_preferred_color_scheme(
+        is_dark_mode_enabled ? ui::NativeTheme::PreferredColorScheme::kLight
+                             : ui::NativeTheme::PreferredColorScheme::kDark);
+
+    native_theme->NotifyOnNativeThemeUpdated();
+    native_theme_web->NotifyOnNativeThemeUpdated();
+  }
+
+  // Returns the profile associated with the test.
+  Profile* profile() { return browser()->profile(); }
+
+ private:
+  web_app::AppId InstallSWA() {
+    web_app::WebAppProvider::GetForSystemWebApps(profile())
+        ->system_web_app_manager()
+        .InstallSystemAppsForTesting();
+    return *web_app::GetAppIdForSystemWebApp(profile(),
+                                             web_app::SystemAppType::SETTINGS);
+  }
+
+  web_app::AppId InstallNonSWA() {
+    if (!test_server_) {
+      test_server_ = std::make_unique<net::EmbeddedTestServer>(
+          net::EmbeddedTestServer::TYPE_HTTPS);
+      test_server_->AddDefaultHandlers(GetChromeTestDataDir());
+      CHECK(test_server_->Start());
+    }
+    const GURL app_url = test_server_->GetURL("app.com", "/ssl/google.html");
+    auto web_app_info = std::make_unique<WebApplicationInfo>();
+    web_app_info->start_url = app_url;
+    web_app_info->scope = app_url.GetWithoutFilename();
+    web_app_info->theme_color = SK_ColorBLUE;
+    web_app_info->background_color = SK_ColorBLUE;
+    web_app_info->dark_mode_theme_color = SK_ColorRED;
+    web_app_info->dark_mode_background_color = SK_ColorRED;
+    return web_app::test::InstallWebApp(profile(), std::move(web_app_info));
+  }
+
+  absl::optional<web_app::AppId> app_id_;
+  std::unique_ptr<net::EmbeddedTestServer> test_server_;
+};
+
+IN_PROC_BROWSER_TEST_P(
+    BrowserNonClientFrameViewChromeOSTestBackgroundColorChange,
+    BackgroundColorChange) {
+  const web_app::AppId app_id = InstallWebApp();
+  Browser* const app_browser = web_app::LaunchWebAppBrowser(profile(), app_id);
+  ContentsWebView* const contents_web_view =
+      BrowserView::GetBrowserViewForBrowser(app_browser)->contents_web_view();
+  content::WebContents* const web_contents =
+      app_browser->tab_strip_model()->GetActiveWebContents();
+
+  // Verify background color is immediately resolved from the app controller
+  // despite the fact that the web contents background color hasn't loaded yet.
+  EXPECT_EQ(contents_web_view->GetBackground()->get_color(),
+            app_browser->app_controller()->GetBackgroundColor().value());
+  EXPECT_FALSE(web_contents->GetBackgroundColor().has_value());
+
+  // Wait for the web contents background color to load and verify that the
+  // background color still matches that resolved from the app controller.
+  {
+    content::BackgroundColorChangeWaiter waiter(web_contents);
+    waiter.Wait();
+    EXPECT_EQ(contents_web_view->GetBackground()->get_color(),
+              app_browser->app_controller()->GetBackgroundColor().value());
+    EXPECT_EQ(contents_web_view->GetBackground()->get_color(),
+              web_contents->GetBackgroundColor().value());
+  }
+
+  content::AwaitDocumentOnLoadCompleted(web_contents);
+
+  // Toggle color mode and verify background color is immediately resolved from
+  // the app controller. In the case of SWAs, there may be a temporary mismatch
+  // between the contents background color and the web contents background color
+  // due to the fact that the web contents background color update is async.
+  ToggleColorMode();
+  EXPECT_EQ(contents_web_view->GetBackground()->get_color(),
+            app_browser->app_controller()->GetBackgroundColor().value());
+  if (!UseSwa()) {
+    EXPECT_EQ(contents_web_view->GetBackground()->get_color(),
+              web_contents->GetBackgroundColor().value());
+  }
+
+  // Wait for the web contents background color to update and verify that the
+  // background color still matches that resolved from the app controller.
+  {
+    content::BackgroundColorChangeWaiter waiter(web_contents);
+    waiter.Wait();
+    EXPECT_EQ(contents_web_view->GetBackground()->get_color(),
+              app_browser->app_controller()->GetBackgroundColor().value());
+    EXPECT_EQ(contents_web_view->GetBackground()->get_color(),
+              web_contents->GetBackgroundColor().value());
+  }
+}
+
 // This test does not make sense for the webUI tabstrip, since the window layout
 // is different in that case.
 IN_PROC_BROWSER_TEST_P(BrowserNonClientFrameViewChromeOSTestNoWebUiTabStrip,
@@ -1165,6 +1288,8 @@
 INSTANTIATE_TEST_SUITE(BrowserNonClientFrameViewChromeOSTest);
 INSTANTIATE_TEST_SUITE(BrowserNonClientFrameViewChromeOSTestNoWebUiTabStrip);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+INSTANTIATE_TEST_SUITE(
+    BrowserNonClientFrameViewChromeOSTestBackgroundColorChange);
 INSTANTIATE_TEST_SUITE(BrowserNonClientFrameViewChromeOSTestWithWebUiTabStrip);
 INSTANTIATE_TEST_SUITE(WebAppNonClientFrameViewAshTest);
 INSTANTIATE_TEST_SUITE(HomeLauncherBrowserNonClientFrameViewChromeOSTest);
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 6eea8b4..a74af3b 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -255,7 +255,7 @@
   }
 
   // Accessor for the contents WebView.
-  views::WebView* contents_web_view() { return contents_web_view_; }
+  ContentsWebView* contents_web_view() { return contents_web_view_; }
 
   base::WeakPtr<BrowserView> GetAsWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
diff --git a/chrome/browser/ui/views/hats/hats_browsertest.cc b/chrome/browser/ui/views/hats/hats_browsertest.cc
index e307bef..bf5c2f3 100644
--- a/chrome/browser/ui/views/hats/hats_browsertest.cc
+++ b/chrome/browser/ui/views/hats/hats_browsertest.cc
@@ -150,7 +150,7 @@
       kHatsNextTestSurveyProductSpecificStringData);
 
   // Check that no record of a survey being shown is present.
-  const base::DictionaryValue* pref_data =
+  const base::Value* pref_data =
       browser()->profile()->GetPrefs()->GetDictionary(
           prefs::kHatsSurveyMetadata);
   absl::optional<base::Time> last_survey_started_time =
diff --git a/chrome/browser/ui/views/task_manager_view.cc b/chrome/browser/ui/views/task_manager_view.cc
index eaccb01..d94a61ef 100644
--- a/chrome/browser/ui/views/task_manager_view.cc
+++ b/chrome/browser/ui/views/task_manager_view.cc
@@ -385,7 +385,7 @@
   if (!g_browser_process->local_state())
     return;
 
-  if (const base::DictionaryValue* dictionary =
+  if (const base::Value* dictionary =
           g_browser_process->local_state()->GetDictionary(GetWindowName())) {
     is_always_on_top_ =
         dictionary->FindBoolKey("always_on_top").value_or(false);
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_unittest.cc b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_unittest.cc
index 3028bf1..84cdbfc9 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_unittest.cc
@@ -473,17 +473,17 @@
 // removed from the PrefService when updating new badge prefs.
 TEST_F(ChromeLabsBubbleTest, CleanUpNewBadgePrefsTest) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  const base::DictionaryValue* new_badge_prefs =
+  const base::Value* new_badge_prefs =
       browser_view()->browser()->profile()->GetPrefs()->GetDictionary(
           chrome_labs_prefs::kChromeLabsNewBadgeDictAshChrome);
 #else
-  const base::DictionaryValue* new_badge_prefs =
+  const base::Value* new_badge_prefs =
       g_browser_process->local_state()->GetDictionary(
           chrome_labs_prefs::kChromeLabsNewBadgeDict);
 #endif
 
-  EXPECT_TRUE(new_badge_prefs->HasKey(kFirstTestFeatureId));
-  EXPECT_TRUE(new_badge_prefs->HasKey(kTestFeatureWithVariationId));
+  EXPECT_TRUE(new_badge_prefs->FindKey(kFirstTestFeatureId));
+  EXPECT_TRUE(new_badge_prefs->FindKey(kTestFeatureWithVariationId));
 
   // Remove two experiments.
   std::vector<LabInfo> test_experiments = TestLabInfo();
@@ -498,6 +498,6 @@
 
   UpdateChromeLabsNewBadgePrefs(browser_view()->browser()->profile(),
                                 chrome_labs_model());
-  EXPECT_FALSE(new_badge_prefs->HasKey(kFirstTestFeatureId));
-  EXPECT_FALSE(new_badge_prefs->HasKey(kTestFeatureWithVariationId));
+  EXPECT_FALSE(new_badge_prefs->FindKey(kFirstTestFeatureId));
+  EXPECT_FALSE(new_badge_prefs->FindKey(kTestFeatureWithVariationId));
 }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index d1155447..f0709d5 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -40,6 +40,7 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h"
+#include "chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h"
 #include "chrome/browser/ui/views/extensions/extension_popup.h"
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
@@ -263,6 +264,12 @@
         browser_view_, MediaToolbarButtonContextualMenu::Create(browser_));
   }
 
+  std::unique_ptr<DownloadToolbarButtonView> download_button;
+  if (base::FeatureList::IsEnabled(features::kDownloadBubble)) {
+    download_button =
+        std::make_unique<DownloadToolbarButtonView>(browser_view_);
+  }
+
   std::unique_ptr<send_tab_to_self::SendTabToSelfToolbarIconView>
       send_tab_to_self_button;
   if ((base::FeatureList::IsEnabled(send_tab_to_self::kSendTabToSelfV2) ||
@@ -345,6 +352,9 @@
   if (media_button)
     media_button_ = AddChildView(std::move(media_button));
 
+  if (download_button)
+    download_button_ = AddChildView(std::move(download_button));
+
   if (send_tab_to_self_button)
     send_tab_to_self_button_ = AddChildView(std::move(send_tab_to_self_button));
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.h b/chrome/browser/ui/views/toolbar/toolbar_view.h
index 8b929587..79efc97 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.h
@@ -44,6 +44,7 @@
 class AvatarToolbarButton;
 class BrowserAppMenuButton;
 class Browser;
+class DownloadToolbarButtonView;
 class ExtensionsToolbarButton;
 class ExtensionsToolbarContainer;
 class ChromeLabsButton;
@@ -144,6 +145,9 @@
   ChromeLabsBubbleViewModel* chrome_labs_model() const {
     return chrome_labs_model_.get();
   }
+  DownloadToolbarButtonView* download_button() const {
+    return download_button_;
+  }
   ExtensionsToolbarContainer* extensions_container() const {
     return extensions_container_;
   }
@@ -287,6 +291,7 @@
   raw_ptr<send_tab_to_self::SendTabToSelfToolbarIconView>
       send_tab_to_self_button_ = nullptr;
   raw_ptr<BrowserAppMenuButton> app_menu_button_ = nullptr;
+  raw_ptr<DownloadToolbarButtonView> download_button_ = nullptr;
 
   const raw_ptr<Browser> browser_;
   const raw_ptr<BrowserView> browser_view_;
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
index 99d8e02..0dfc25f 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
@@ -139,7 +139,7 @@
 
 IN_PROC_BROWSER_TEST_F(
     WebAppIntegrationBrowserTest,
-    WebAppIntegration_InstCrtShctWindowedSiteA_WindowCreated_InListWinSiteA_ClosePWA_MnfstUpdateDsplMinimalSiteA_LaunchFromListSiteA_Minimal) {
+    DISABLED_WebAppIntegration_InstCrtShctWindowedSiteA_WindowCreated_InListWinSiteA_ClosePWA_MnfstUpdateDsplMinimalSiteA_LaunchFromListSiteA_Minimal) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
   // Sheriffs: Disabling this test is supported.
@@ -358,7 +358,7 @@
 
 IN_PROC_BROWSER_TEST_F(
     WebAppIntegrationBrowserTest,
-    WebAppIntegration_InstOmniboxSiteA_WindowCreated_InListWinSiteA_ClosePWA_MnfstUpdateDsplMinimalSiteA_LaunchFromListSiteA_Minimal) {
+    DISABLED_WebAppIntegration_InstOmniboxSiteA_WindowCreated_InListWinSiteA_ClosePWA_MnfstUpdateDsplMinimalSiteA_LaunchFromListSiteA_Minimal) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
   // Sheriffs: Disabling this test is supported.
@@ -577,7 +577,7 @@
 
 IN_PROC_BROWSER_TEST_F(
     WebAppIntegrationBrowserTest,
-    WebAppIntegration_InstMenuOptionSiteA_WindowCreated_InListWinSiteA_ClosePWA_MnfstUpdateDsplMinimalSiteA_LaunchFromListSiteA_Minimal) {
+    DISABLED_WebAppIntegration_InstMenuOptionSiteA_WindowCreated_InListWinSiteA_ClosePWA_MnfstUpdateDsplMinimalSiteA_LaunchFromListSiteA_Minimal) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
   // Sheriffs: Disabling this test is supported.
@@ -1533,7 +1533,7 @@
 
 IN_PROC_BROWSER_TEST_F(
     WebAppIntegrationBrowserTest,
-    WebAppIntegration_InstCrtShctWindowedSiteA_WindowCreated_InListWinSiteA_ClosePWA_MnfstUpdateDsplMinimalSiteA_LaunchFromIconSiteA_Minimal) {
+    DISABLED_WebAppIntegration_InstCrtShctWindowedSiteA_WindowCreated_InListWinSiteA_ClosePWA_MnfstUpdateDsplMinimalSiteA_LaunchFromIconSiteA_Minimal) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
   // Sheriffs: Disabling this test is supported.
@@ -1593,7 +1593,7 @@
 
 IN_PROC_BROWSER_TEST_F(
     WebAppIntegrationBrowserTest,
-    WebAppIntegration_InstOmniboxSiteA_WindowCreated_InListWinSiteA_ClosePWA_MnfstUpdateDsplMinimalSiteA_LaunchFromIconSiteA_Minimal) {
+    DISABLED_WebAppIntegration_InstOmniboxSiteA_WindowCreated_InListWinSiteA_ClosePWA_MnfstUpdateDsplMinimalSiteA_LaunchFromIconSiteA_Minimal) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
   // Sheriffs: Disabling this test is supported.
@@ -1653,7 +1653,7 @@
 
 IN_PROC_BROWSER_TEST_F(
     WebAppIntegrationBrowserTest,
-    WebAppIntegration_InstMenuOptionSiteA_WindowCreated_InListWinSiteA_ClosePWA_MnfstUpdateDsplMinimalSiteA_LaunchFromIconSiteA_Minimal) {
+    DISABLED_WebAppIntegration_InstMenuOptionSiteA_WindowCreated_InListWinSiteA_ClosePWA_MnfstUpdateDsplMinimalSiteA_LaunchFromIconSiteA_Minimal) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
   // Sheriffs: Disabling this test is supported.
diff --git a/chrome/browser/ui/web_applications/share_target_utils.cc b/chrome/browser/ui/web_applications/share_target_utils.cc
index 0c3b25a..e33b7aab 100644
--- a/chrome/browser/ui/web_applications/share_target_utils.cc
+++ b/chrome/browser/ui/web_applications/share_target_utils.cc
@@ -67,12 +67,12 @@
 NavigateParams NavigateParamsForShareTarget(
     Browser* browser,
     const apps::ShareTarget& share_target,
-    const apps::mojom::Intent& intent) {
+    const apps::mojom::Intent& intent,
+    const std::vector<base::FilePath>& launch_files) {
   NavigateParams nav_params(browser, share_target.action,
                             ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  constexpr int kBufSize = 16 * 1024;
+#if defined(OS_CHROMEOS)
   std::vector<std::string> names;
   std::vector<std::string> values;
   std::vector<bool> is_value_file_uris;
@@ -82,10 +82,16 @@
       data_pipe_getters;
 
   if (intent.mime_type.has_value() && intent.files.has_value()) {
+    if (!launch_files.empty()) {
+      DCHECK_EQ(launch_files.size(), intent.files->size());
+    }
+
     // Files for Web Share intents are created by the browser in
     // a .WebShare directory, with generated file names and file urls - see
     // //chrome/browser/webshare/chromeos/sharesheet_client.cc
-    for (const auto& file : intent.files.value()) {
+    for (size_t i = 0; i < intent.files->size(); ++i) {
+      const apps::mojom::IntentFilePtr& file = (*intent.files)[i];
+
       const std::string& mime_type = file->mime_type.has_value()
                                          ? file->mime_type.value()
                                          : intent.mime_type.value();
@@ -105,13 +111,16 @@
       if (name.empty())
         continue;
 
+      storage::FileSystemURL file_system_url;
+      mojo::PendingRemote<network::mojom::DataPipeGetter> data_pipe_getter;
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
       storage::FileSystemContext* file_system_context =
           file_manager::util::GetFileManagerFileSystemContext(
               browser->profile());
-      storage::FileSystemURL file_system_url =
+      file_system_url =
           file_system_context->CrackURLInFirstPartyContext(file->url);
 
-      mojo::PendingRemote<network::mojom::DataPipeGetter> data_pipe_getter;
       if (!file_system_url.is_valid()) {
         // TODO(crbug.com/1166982): We could be more intelligent here and
         // decide which cracking method to use based on the scheme.
@@ -124,6 +133,7 @@
           continue;
         }
 
+        constexpr int kBufSize = 16 * 1024;
         FileStreamDataPipeGetter::Create(
             /*receiver=*/data_pipe_getter.InitWithNewPipeAndPassReceiver(),
             /*context=*/file_system_context,
@@ -132,6 +142,7 @@
             /*file_size=*/file->file_size,
             /*buf_size=*/kBufSize);
       }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
       const std::string filename =
           (file->file_name.has_value() && !file->file_name->path().empty())
@@ -139,7 +150,13 @@
               : file_system_url.path().BaseName().AsUTF8Unsafe();
 
       names.push_back(name);
-      values.push_back(file_system_url.path().AsUTF8Unsafe());
+
+      if (launch_files.empty()) {
+        values.push_back(file_system_url.path().AsUTF8Unsafe());
+      } else {
+        values.push_back(launch_files[i].value());
+      }
+
       is_value_file_uris.push_back(true);
       filenames.push_back(filename);
       types.push_back(mime_type);
@@ -186,7 +203,7 @@
   // TODO(crbug.com/1153194): Support Web Share Target on Windows.
   // TODO(crbug.com/1153195): Support Web Share Target on Mac.
   NOTIMPLEMENTED();
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // defined(OS_CHROMEOS)
 
   return nav_params;
 }
diff --git a/chrome/browser/ui/web_applications/share_target_utils.h b/chrome/browser/ui/web_applications/share_target_utils.h
index 3fb695a..408862a1 100644
--- a/chrome/browser/ui/web_applications/share_target_utils.h
+++ b/chrome/browser/ui/web_applications/share_target_utils.h
@@ -34,7 +34,8 @@
 NavigateParams NavigateParamsForShareTarget(
     Browser* browser,
     const apps::ShareTarget& share_target,
-    const apps::mojom::Intent& intent);
+    const apps::mojom::Intent& intent,
+    const std::vector<base::FilePath>& launch_files);
 
 }  // namespace web_app
 
diff --git a/chrome/browser/ui/web_applications/web_app_browser_controller.cc b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
index 51de46d..d67cea6 100644
--- a/chrome/browser/ui/web_applications/web_app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
@@ -240,18 +240,24 @@
 }
 
 absl::optional<SkColor> WebAppBrowserController::GetBackgroundColor() const {
-  if (auto color = AppBrowserController::GetBackgroundColor())
-    return color;
+  auto web_contents_color = AppBrowserController::GetBackgroundColor();
+  auto registrar_color = registrar().GetAppBackgroundColor(app_id());
 
   if (ui::NativeTheme::GetInstanceForNativeUi()->ShouldUseDarkColors()) {
     absl::optional<SkColor> dark_mode_color =
         registrar().GetAppDarkModeBackgroundColor(app_id());
-    if (dark_mode_color) {
-      return dark_mode_color;
-    }
+    if (dark_mode_color)
+      registrar_color = dark_mode_color;
   }
 
-  return registrar().GetAppBackgroundColor(app_id());
+  // SWAs give preference to registrar colors over colors resolved from web
+  // contents to work around the asynchronous nature of web contents' background
+  // color updates on theme change which would otherwise result in out of sync
+  // transitions between the browser's non-client frame header and background.
+  if (system_app_)
+    return registrar_color ? registrar_color : web_contents_color;
+
+  return web_contents_color ? web_contents_color : registrar_color;
 }
 
 GURL WebAppBrowserController::GetAppStartUrl() const {
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 7f2fd62..b62561e 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -47,6 +47,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/app_menu_model.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
+#include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
 #include "chrome/browser/ui/web_applications/web_app_launch_manager.h"
@@ -54,6 +55,8 @@
 #include "chrome/browser/ui/web_applications/web_app_menu_model.h"
 #include "chrome/browser/web_applications/external_install_options.h"
 #include "chrome/browser/web_applications/externally_installed_web_app_prefs.h"
+#include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h"
+#include "chrome/browser/web_applications/system_web_apps/system_web_app_types.h"
 #include "chrome/browser/web_applications/test/web_app_test_observers.h"
 #include "chrome/browser/web_applications/test/web_app_test_utils.h"
 #include "chrome/browser/web_applications/web_app.h"
@@ -301,39 +304,104 @@
   EXPECT_EQ(provider->registrar().GetAppBackgroundColor(app_id), SK_ColorBLUE);
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, BackgroundColorChange) {
-  const GURL app_url = GetSecureAppURL();
-  auto web_app_info = std::make_unique<WebApplicationInfo>();
-  web_app_info->start_url = app_url;
-  web_app_info->scope = app_url.GetWithoutFilename();
-  web_app_info->theme_color = SK_ColorBLUE;
-  const AppId app_id = InstallWebApp(std::move(web_app_info));
+// Base class for background color change browser tests parameterized by whether
+// to use a SWA or a non-SWA.
+class WebAppBackgroundColorChangeBrowserTest
+    : public WebAppBrowserTest,
+      public testing::WithParamInterface</*use_swa=*/bool> {
+ public:
+  // Returns whether to use a SWA given test parameterization.
+  bool UseSwa() const { return GetParam(); }
 
+  // Installs an SWA or a non-SWA depending on test parameterization, returning
+  // the `AppId` of the installed app. Note that this method may only be invoked
+  // once per test.
+  AppId InstallWebApp() {
+    DCHECK(!app_id_.has_value());
+    if (UseSwa()) {
+      web_app::WebAppProvider::GetForSystemWebApps(profile())
+          ->system_web_app_manager()
+          .InstallSystemAppsForTesting();
+      app_id_ = *web_app::GetAppIdForSystemWebApp(profile(),
+                                                  web_app::SystemAppType::HELP);
+    } else {
+      const GURL app_url = GetSecureAppURL();
+      auto web_app_info = std::make_unique<WebApplicationInfo>();
+      web_app_info->start_url = app_url;
+      web_app_info->scope = app_url.GetWithoutFilename();
+      web_app_info->theme_color = SK_ColorBLUE;
+      web_app_info->background_color = SK_ColorBLUE;
+      app_id_ = WebAppBrowserTest::InstallWebApp(std::move(web_app_info));
+    }
+    return app_id_.value();
+  }
+
+  // Returns the background color for the installed SWA or non-SWA depending on
+  // test parameterization. Note that this method may only be invoked after
+  // having called `InstallWebApp()`.
+  absl::optional<SkColor> GetBackgroundColorFromRegistrar() {
+    auto* provider = WebAppProvider::GetForTest(profile());
+    return provider->registrar().GetAppBackgroundColor(app_id_.value());
+  }
+
+ private:
+  absl::optional<AppId> app_id_;
+};
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+INSTANTIATE_TEST_SUITE_P(All,
+                         WebAppBackgroundColorChangeBrowserTest,
+                         /*use_swa=*/testing::Bool());
+#else
+INSTANTIATE_TEST_SUITE_P(All,
+                         WebAppBackgroundColorChangeBrowserTest,
+                         /*use_swa=*/testing::Values(false));
+#endif
+
+IN_PROC_BROWSER_TEST_P(WebAppBackgroundColorChangeBrowserTest,
+                       BackgroundColorChange) {
+  const AppId app_id = WebAppBackgroundColorChangeBrowserTest::InstallWebApp();
   Browser* const app_browser = LaunchWebAppBrowser(app_id);
   content::WebContents* const web_contents =
       app_browser->tab_strip_model()->GetActiveWebContents();
 
-  // Wait for original background color (not cyan) to load.
+  // Verify background color is resolved from the registrar prior to the web
+  // contents background color being loaded.
+  const SkColor registrar_color = GetBackgroundColorFromRegistrar().value();
+  EXPECT_EQ(app_browser->app_controller()->GetBackgroundColor().value(),
+            registrar_color);
+
+  // Wait for the web contents background color to load. For SWAs, the app
+  // controller should still resolve background color from the registrar while
+  // for non-SWAs the app controller should resolve background color from the
+  // web contents.
   {
     content::BackgroundColorChangeWaiter waiter(web_contents);
     waiter.Wait();
-    EXPECT_NE(app_browser->app_controller()->GetBackgroundColor().value(),
-              SK_ColorCYAN);
+    EXPECT_EQ(app_browser->app_controller()->GetBackgroundColor().value(),
+              UseSwa() ? registrar_color
+                       : web_contents->GetBackgroundColor().value());
   }
   content::AwaitDocumentOnLoadCompleted(web_contents);
 
-  // Changing background color should update download shelf theme.
+  // Changing web contents background color should update download shelf theme
+  // for non-SWAs. For SWAs, background color and shelf theme should still be
+  // resolved from the registrar.
   {
     content::BackgroundColorChangeWaiter waiter(web_contents);
-    EXPECT_TRUE(content::ExecJs(
+    EXPECT_TRUE(content::ExecuteScript(
         web_contents, "document.body.style.backgroundColor = 'cyan';"));
     waiter.Wait();
+    const SkColor web_contents_color =
+        web_contents->GetBackgroundColor().value();
+    EXPECT_EQ(web_contents_color, SK_ColorCYAN);
     EXPECT_EQ(app_browser->app_controller()->GetBackgroundColor().value(),
-              SK_ColorCYAN);
+              UseSwa() ? registrar_color : web_contents_color);
     SkColor download_shelf_color;
     app_browser->app_controller()->GetThemeSupplier()->GetColor(
         ThemeProperties::COLOR_DOWNLOAD_SHELF, &download_shelf_color);
-    EXPECT_EQ(download_shelf_color, SK_ColorCYAN);
+    EXPECT_EQ(download_shelf_color,
+              UseSwa() ? registrar_color : web_contents_color);
   }
 }
 
diff --git a/chrome/browser/ui/web_applications/web_app_dark_mode_browsertest.cc b/chrome/browser/ui/web_applications/web_app_dark_mode_browsertest.cc
index 86e1379..894021e7 100644
--- a/chrome/browser/ui/web_applications/web_app_dark_mode_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_dark_mode_browsertest.cc
@@ -2,14 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/test/bind.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/ui/web_applications/web_app_browser_controller.h"
 #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
+#include "chrome/browser/web_applications/manifest_update_task.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
+#include "components/embedder_support/switches.h"
 #include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/url_loader_interceptor.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -109,4 +114,139 @@
       blink::mojom::WebFeature::kWebAppManifestUserPreferences, 0);
 }
 
+class WebAppDarkModeOriginTrialBrowserTest : public InProcessBrowserTest {
+ public:
+  WebAppDarkModeOriginTrialBrowserTest() {
+    feature_list_.InitAndDisableFeature(blink::features::kWebAppEnableDarkMode);
+  }
+  ~WebAppDarkModeOriginTrialBrowserTest() override = default;
+
+  // InProcessBrowserTest:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // Using the test public key from docs/origin_trials_integration.md#Testing.
+    command_line->AppendSwitchASCII(
+        embedder_support::kOriginTrialPublicKey,
+        "dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA=");
+  }
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    web_app::test::WaitUntilReady(
+        web_app::WebAppProvider::GetForTest(browser()->profile()));
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  OsIntegrationManager::ScopedSuppressForTesting os_hooks_suppress_;
+};
+namespace {
+
+// InstallableManager requires https or localhost to load the manifest. Go with
+// localhost to avoid having to set up cert servers.
+constexpr char kTestWebAppUrl[] = "http://127.0.0.1:8000/";
+constexpr char kTestWebAppHeaders[] =
+    "HTTP/1.1 200 OK\nContent-Type: text/html; charset=utf-8\n";
+constexpr char kTestWebAppBody[] = R"(
+  <!DOCTYPE html>
+  <head>
+    <link rel="manifest" href="manifest.webmanifest">
+    <meta http-equiv="origin-trial" content="$1">
+  </head>
+)";
+
+constexpr char kTestIconUrl[] = "http://127.0.0.1:8000/icon.png";
+constexpr char kTestManifestUrl[] =
+    "http://127.0.0.1:8000/manifest.webmanifest";
+constexpr char kTestManifestHeaders[] =
+    "HTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\n";
+constexpr char kTestManifestBody[] = R"({
+  "name": "Test app",
+  "display": "standalone",
+  "start_url": "/",
+  "scope": "/",
+  "icons": [{
+    "src": "icon.png",
+    "sizes": "192x192",
+    "type": "image/png"
+  }],
+  "theme_color": "#0000FF",
+  "background_color": "#0000FF",
+  "user_preferences": {
+    "color_scheme_dark": {
+      "theme_color": "#FF0000",
+      "background_color": "#FF0000"
+    }
+  }
+})";
+
+// Generated from script:
+// $ tools/origin_trials/generate_token.py http://127.0.0.1:8000
+// "WebAppDarkMode" --expire-timestamp=2000000000
+constexpr char kOriginTrialToken[] =
+    "A5gReAn4Vcyi41JIfRKliAfE8tKDVpNCK7Xjo5S2XxgkuXNY+"
+    "gapsR9MqlZWWiBAmUsNcHSjqG+"
+    "phM2z3ZNQQAMAAABWeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1c"
+    "mUiOiAiV2ViQXBwRGFya01vZGUiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=";
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(WebAppDarkModeOriginTrialBrowserTest, OriginTrial) {
+  ManifestUpdateTask::BypassWindowCloseWaitingForTesting() = true;
+
+  bool serve_token = true;
+  content::URLLoaderInterceptor interceptor(base::BindLambdaForTesting(
+      [&serve_token](
+          content::URLLoaderInterceptor::RequestParams* params) -> bool {
+        if (params->url_request.url.spec() == kTestWebAppUrl) {
+          content::URLLoaderInterceptor::WriteResponse(
+              kTestWebAppHeaders,
+              base::ReplaceStringPlaceholders(
+                  kTestWebAppBody, {serve_token ? kOriginTrialToken : ""},
+                  nullptr),
+              params->client.get());
+          return true;
+        }
+        if (params->url_request.url.spec() == kTestManifestUrl) {
+          content::URLLoaderInterceptor::WriteResponse(
+              kTestManifestHeaders, kTestManifestBody, params->client.get());
+          return true;
+        }
+        if (params->url_request.url.spec() == kTestIconUrl) {
+          content::URLLoaderInterceptor::WriteResponse(
+              "chrome/test/data/web_apps/basic-192.png", params->client.get());
+          return true;
+        }
+        return false;
+      }));
+
+  // Install web app with origin trial token.
+  AppId app_id =
+      web_app::InstallWebAppFromPage(browser(), GURL(kTestWebAppUrl));
+
+  // Origin trial should grant the app access.
+  WebAppProvider& provider = *WebAppProvider::GetForTest(browser()->profile());
+  EXPECT_EQ(provider.registrar().GetAppById(app_id)->dark_mode_theme_color(),
+            SK_ColorRED);
+  EXPECT_EQ(
+      provider.registrar().GetAppById(app_id)->dark_mode_background_color(),
+      SK_ColorRED);
+
+  // Open the page again with the token missing.
+  {
+    UpdateAwaiter update_awaiter(provider.registrar());
+
+    serve_token = false;
+    NavigateToURLAndWait(browser(), GURL(kTestWebAppUrl));
+
+    update_awaiter.AwaitUpdate();
+  }
+
+  // The app should update to no longer have dark mode colors defined without
+  // the origin trial.
+  EXPECT_EQ(provider.registrar().GetAppById(app_id)->dark_mode_theme_color(),
+            absl::nullopt);
+  EXPECT_EQ(
+      provider.registrar().GetAppById(app_id)->dark_mode_background_color(),
+      absl::nullopt);
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/ui/web_applications/web_app_launch_process.cc b/chrome/browser/ui/web_applications/web_app_launch_process.cc
index 4bc7996..744f7b6f 100644
--- a/chrome/browser/ui/web_applications/web_app_launch_process.cc
+++ b/chrome/browser/ui/web_applications/web_app_launch_process.cc
@@ -295,8 +295,8 @@
   if (share_target) {
     // TODO(crbug.com/1213776): Expose share target in the LaunchParams and
     // don't navigate if navigate_existing_client: never is in effect.
-    NavigateParams nav_params =
-        NavigateParamsForShareTarget(browser, *share_target, *params_.intent);
+    NavigateParams nav_params = NavigateParamsForShareTarget(
+        browser, *share_target, *params_.intent, params_.launch_files);
     nav_params.disposition = navigation_disposition;
     return {
         .web_contents = NavigateWebAppUsingParams(params_.app_id, nav_params),
diff --git a/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc b/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc
index 7bdf8cc1..008d478 100644
--- a/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc
+++ b/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc
@@ -31,8 +31,6 @@
       content::WebUIDataSource::Create(source_name);
   source->AddResourcePath("autofill_and_password_manager_internals.js",
                           IDR_AUTOFILL_AND_PASSWORD_MANAGER_INTERNALS_JS);
-  source->AddResourcePath("autofill_and_password_manager_internals.css",
-                          IDR_AUTOFILL_AND_PASSWORD_MANAGER_INTERNALS_CSS);
   source->SetDefaultResource(IDR_AUTOFILL_AND_PASSWORD_MANAGER_INTERNALS_HTML);
   // Data strings:
   source->AddString(version_ui::kVersion, version_info::GetVersionNumber());
diff --git a/chrome/browser/ui/webui/chromeos/bluetooth_shared_load_time_data_provider.cc b/chrome/browser/ui/webui/chromeos/bluetooth_shared_load_time_data_provider.cc
index 8b77a804..7885bbd 100644
--- a/chrome/browser/ui/webui/chromeos/bluetooth_shared_load_time_data_provider.cc
+++ b/chrome/browser/ui/webui/chromeos/bluetooth_shared_load_time_data_provider.cc
@@ -46,6 +46,28 @@
       {"bluetoothConfirmCodeMessage",
        IDS_BLUETOOTH_PAIRING_CONFIRM_CODE_MESSAGE},
       {"bluetoothPairingEnterKeys", IDS_BLUETOOTH_PAIRING_ENTER_KEYS},
+      {"bluetoothPairingDeviceItemA11YLabelUnknown",
+       IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_UNKNOWN},
+      {"bluetoothPairingDeviceItemA11YLabelComputer",
+       IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_COMPUTER},
+      {"bluetoothPairingDeviceItemA11YLabelPhone",
+       IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_PHONE},
+      {"bluetoothPairingDeviceItemA11YLabelHeadset",
+       IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_HEADSET},
+      {"bluetoothPairingDeviceItemA11YLabelVideoCamera",
+       IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_VIDEO_CAMERA},
+      {"bluetoothPairingDeviceItemA11YLabelGameContoller",
+       IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_GAME_CONTROLLER},
+      {"bluetoothPairingDeviceItemA11YLabelKeyboard",
+       IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_KEYBOARD},
+      {"bluetoothPairingDeviceItemA11YLabelMouse",
+       IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_MOUSE},
+      {"bluetoothPairingDeviceItemA11YLabelTablet",
+       IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_A11Y_LABEL_TABLET},
+      {"bluetoothPairingDeviceItemSecondaryErrorA11YLabel",
+       IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_SECONDARY_ERROR_A11Y_LABEL},
+      {"bluetoothPairingDeviceItemSecondaryPairingA11YLabel",
+       IDS_BLUETOOTH_PAIRINGS_DEVICE_ITEM_SECONDARY_PAIRING_A11Y_LABEL},
       // Device connecting and pairing.
       // These ids are generated in JS using 'bluetooth_' + a value from
       // bluetoothPrivate.PairingEventType (see bluetooth_private.idl).
diff --git a/chrome/browser/ui/webui/chromeos/login/online_login_helper.h b/chrome/browser/ui/webui/chromeos/login/online_login_helper.h
index 2044646..c436d9c 100644
--- a/chrome/browser/ui/webui/chromeos/login/online_login_helper.h
+++ b/chrome/browser/ui/webui/chromeos/login/online_login_helper.h
@@ -8,6 +8,10 @@
 #include <string>
 
 #include "ash/components/login/auth/cryptohome_authenticator.h"
+// TODO(https://crbug.com/1164001): move to forward declaration
+#include "ash/components/login/auth/sync_trusted_vault_keys.h"
+// TODO(https://crbug.com/1164001): move to forward declaration
+#include "ash/components/login/auth/user_context.h"
 #include "chrome/browser/ash/login/login_client_cert_usage_observer.h"
 #include "chrome/browser/ash/login/signin_partition_manager.h"
 #include "chrome/browser/ash/login/ui/login_display_host.h"
@@ -22,10 +26,6 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
-
-class SyncTrustedVaultKeys;
-class UserContext;
-
 namespace login {
 
 // A class that's used to specify the way how Gaia should be loaded.
diff --git a/chrome/browser/ui/webui/chromeos/login/os_install_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/os_install_screen_handler.cc
index c92463c..9edfb92 100644
--- a/chrome/browser/ui/webui/chromeos/login/os_install_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/os_install_screen_handler.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/notreached.h"
+#include "base/time/time.h"
 #include "chrome/browser/ash/login/screens/os_install_screen.h"
 #include "chrome/browser/ui/webui/chromeos/login/js_calls_container.h"
 #include "chrome/grit/chromium_strings.h"
diff --git a/chrome/browser/ui/webui/chromeos/login/os_install_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/os_install_screen_handler.h
index bd67f74..978e5614 100644
--- a/chrome/browser/ui/webui/chromeos/login/os_install_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/os_install_screen_handler.h
@@ -13,6 +13,10 @@
 class OsInstallScreen;
 }
 
+namespace base {
+class TimeDelta;
+}  // namespace base
+
 namespace login {
 class LocalizedValuesBuilder;
 }  // namespace login
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
index d222d3ed..2cfe42d 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -35,6 +35,7 @@
 
 namespace ash {
 class LoginDisplayHostMojo;
+class UserContext;
 
 namespace mojom {
 enum class TrayActionState;
@@ -45,7 +46,6 @@
 
 class CoreOobeView;
 class GaiaScreenHandler;
-class UserContext;
 
 // An interface for WebUILoginDisplay to call SigninScreenHandler.
 class LoginDisplayWebUIHandler {
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index e3f624c..339ce63 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -651,8 +651,7 @@
 
   dictionary->Set("apps", std::move(installed_extensions));
 
-  const base::ListValue* app_page_names =
-      prefs->GetList(prefs::kNtpAppPageNames);
+  const base::Value* app_page_names = prefs->GetList(prefs::kNtpAppPageNames);
   if (!app_page_names || !app_page_names->GetList().size()) {
     ListPrefUpdate update(prefs, prefs::kNtpAppPageNames);
     base::ListValue* list = update.Get();
diff --git a/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.cc b/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.cc
index bb14b45..fc76f59 100644
--- a/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.cc
+++ b/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.cc
@@ -61,8 +61,8 @@
   quota_manager_->DumpBucketTable(base::BindOnce(
       &QuotaInternalsProxy::DidDumpBucketTable, weak_factory_.GetWeakPtr()));
 
-  std::map<std::string, std::string> stats = quota_manager_->GetStatistics();
-  ReportStatistics(stats);
+  quota_manager_->GetStatistics(base::BindOnce(
+      &QuotaInternalsProxy::DidGetStatistics, weak_factory_.GetWeakPtr()));
 }
 
 void QuotaInternalsProxy::TriggerStoragePressure(
@@ -179,6 +179,15 @@
                  hosts_pending_.begin()->second);
 }
 
+void QuotaInternalsProxy::DidGetStatistics(
+    const base::flat_map<std::string, std::string>& stats) {
+  std::map<std::string, std::string> stats_map;
+  for (const auto& stat : stats) {
+    stats_map[stat.first] = stat.second;
+  }
+  ReportStatistics(stats_map);
+}
+
 void QuotaInternalsProxy::RequestPerOriginInfo(StorageType type) {
   DCHECK(quota_manager_.get());
 
diff --git a/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h b/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h
index daf0343..861d96ba3 100644
--- a/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h
+++ b/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h
@@ -75,6 +75,8 @@
                        blink::mojom::StorageType type,
                        int64_t usage,
                        blink::mojom::UsageBreakdownPtr usage_breakdown);
+  void DidGetStatistics(
+      const base::flat_map<std::string, std::string>& statistics);
 
   // Helper. Called on IO Thread.
   void RequestPerOriginInfo(blink::mojom::StorageType type);
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
index c337a131..c2f27d5b 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
@@ -339,6 +339,12 @@
     phonehub::util::LogFeatureOptInEntryPoint(
         phonehub::util::OptInEntryPoint::kSettings);
   }
+
+  if (enabled &&
+      feature == multidevice_setup::mojom::Feature::kPhoneHubCameraRoll) {
+    phonehub::util::LogCameraRollFeatureOptInEntryPoint(
+        phonehub::util::CameraRollOptInEntryPoint::kSettings);
+  }
 }
 
 void MultideviceHandler::HandleRemoveHostDevice(const base::ListValue* args) {
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index e0de740..f4d5237e 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -295,7 +295,7 @@
 
   html_source->AddBoolean(
       "userCannotManuallyEnterPassword",
-      !chromeos::password_visibility::AccountHasUserFacingPassword(
+      !ash::password_visibility::AccountHasUserFacingPassword(
           chromeos::ProfileHelper::Get()
               ->GetUserByProfile(profile)
               ->GetAccountId()));
diff --git a/chrome/browser/ui/webui/settings/site_settings_helper.cc b/chrome/browser/ui/webui/settings/site_settings_helper.cc
index c908280e..5daeaf8 100644
--- a/chrome/browser/ui/webui/settings/site_settings_helper.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_helper.cc
@@ -813,7 +813,7 @@
 
   Profile* profile = Profile::FromWebUI(web_ui);
   PrefService* prefs = profile->GetPrefs();
-  const base::ListValue* policy_urls =
+  const base::Value* policy_urls =
       prefs->GetList(type == ContentSettingsType::MEDIASTREAM_MIC
                          ? prefs::kAudioCaptureAllowedUrls
                          : prefs::kVideoCaptureAllowedUrls);
diff --git a/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc b/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
index 158dcf6..2d06a2c 100644
--- a/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
+++ b/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
@@ -31,27 +31,25 @@
 #include "content/public/browser/web_ui.h"
 
 using base::DictionaryValue;
-using base::ListValue;
-using base::Value;
 using syncer::SyncInvalidationsService;
 using syncer::SyncService;
 
 namespace {
 
 // Converts the string at |index| in |list| to an int, defaulting to 0 on error.
-int64_t StringAtIndexToInt64(const base::ListValue* list, size_t index) {
-  if (list->GetList().size() > index && list->GetList()[index].is_string()) {
+int64_t StringAtIndexToInt64(base::Value::ConstListView list, size_t index) {
+  if (list.size() > index && list[index].is_string()) {
     int64_t integer = 0;
-    if (base::StringToInt64(list->GetList()[index].GetString(), &integer))
+    if (base::StringToInt64(list[index].GetString(), &integer))
       return integer;
   }
   return 0;
 }
 
 // Returns whether the there is any value at the given |index|.
-bool HasSomethingAtIndex(const base::ListValue* list, size_t index) {
-  if (list->GetList().size() > index && list->GetList()[index].is_string()) {
-    return !list->GetList()[index].GetString().empty();
+bool HasSomethingAtIndex(base::Value::ConstListView list, size_t index) {
+  if (list.size() > index && list[index].is_string()) {
+    return !list[index].GetString().empty();
   }
   return false;
 }
@@ -94,66 +92,66 @@
 void SyncInternalsMessageHandler::RegisterMessages() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kRequestDataAndRegisterForUpdates,
       base::BindRepeating(
           &SyncInternalsMessageHandler::HandleRequestDataAndRegisterForUpdates,
           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kRequestListOfTypes,
       base::BindRepeating(
           &SyncInternalsMessageHandler::HandleRequestListOfTypes,
           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kRequestIncludeSpecificsInitialState,
       base::BindRepeating(&SyncInternalsMessageHandler::
                               HandleRequestIncludeSpecificsInitialState,
                           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kSetIncludeSpecifics,
       base::BindRepeating(
           &SyncInternalsMessageHandler::HandleSetIncludeSpecifics,
           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kWriteUserEvent,
       base::BindRepeating(&SyncInternalsMessageHandler::HandleWriteUserEvent,
                           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kRequestStart,
       base::BindRepeating(&SyncInternalsMessageHandler::HandleRequestStart,
                           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kRequestStopKeepData,
       base::BindRepeating(
           &SyncInternalsMessageHandler::HandleRequestStopKeepData,
           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kRequestStopClearData,
       base::BindRepeating(
           &SyncInternalsMessageHandler::HandleRequestStopClearData,
           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kTriggerRefresh,
       base::BindRepeating(&SyncInternalsMessageHandler::HandleTriggerRefresh,
                           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kGetAllNodes,
       base::BindRepeating(&SyncInternalsMessageHandler::HandleGetAllNodes,
                           base::Unretained(this)));
 }
 
 void SyncInternalsMessageHandler::HandleRequestDataAndRegisterForUpdates(
-    const ListValue* args) {
-  DCHECK(args->GetList().empty());
+    base::Value::ConstListView args) {
+  DCHECK(args.empty());
   AllowJavascript();
 
   // is_registered_ flag protects us from double-registering.  This could
@@ -177,12 +175,12 @@
 }
 
 void SyncInternalsMessageHandler::HandleRequestListOfTypes(
-    const ListValue* args) {
-  DCHECK(args->GetList().empty());
+    base::Value::ConstListView args) {
+  DCHECK(args.empty());
   AllowJavascript();
 
   DictionaryValue event_details;
-  ListValue type_list;
+  base::Value type_list(base::Value::Type::LIST);
   syncer::ModelTypeSet protocol_types = syncer::ProtocolTypes();
   for (syncer::ModelType type : protocol_types) {
     type_list.Append(ModelTypeToString(type));
@@ -193,8 +191,8 @@
 }
 
 void SyncInternalsMessageHandler::HandleRequestIncludeSpecificsInitialState(
-    const ListValue* args) {
-  DCHECK(args->GetList().empty());
+    base::Value::ConstListView args) {
+  DCHECK(args.empty());
   AllowJavascript();
 
   DictionaryValue value;
@@ -205,11 +203,12 @@
       syncer::sync_ui_util::kOnReceivedIncludeSpecificsInitialState, value);
 }
 
-void SyncInternalsMessageHandler::HandleGetAllNodes(const ListValue* args) {
-  DCHECK_EQ(1U, args->GetList().size());
+void SyncInternalsMessageHandler::HandleGetAllNodes(
+    base::Value::ConstListView args) {
+  DCHECK_EQ(1U, args.size());
   AllowJavascript();
 
-  const std::string& callback_id = args->GetList()[0].GetString();
+  const std::string& callback_id = args[0].GetString();
 
   SyncService* service = GetSyncService();
   if (service) {
@@ -224,15 +223,15 @@
 }
 
 void SyncInternalsMessageHandler::HandleSetIncludeSpecifics(
-    const ListValue* args) {
-  DCHECK_EQ(1U, args->GetList().size());
+    base::Value::ConstListView args) {
+  DCHECK_EQ(1U, args.size());
   AllowJavascript();
-  include_specifics_ = args->GetList()[0].GetBool();
+  include_specifics_ = args[0].GetBool();
 }
 
 void SyncInternalsMessageHandler::HandleWriteUserEvent(
-    const base::ListValue* args) {
-  DCHECK_EQ(2U, args->GetList().size());
+    base::Value::ConstListView args) {
+  DCHECK_EQ(2U, args.size());
   AllowJavascript();
 
   Profile* profile = Profile::FromWebUI(web_ui());
@@ -256,8 +255,8 @@
 }
 
 void SyncInternalsMessageHandler::HandleRequestStart(
-    const base::ListValue* args) {
-  DCHECK_EQ(0U, args->GetList().size());
+    base::Value::ConstListView args) {
+  DCHECK_EQ(0U, args.size());
 
   SyncService* service = GetSyncService();
   if (!service)
@@ -272,8 +271,8 @@
 }
 
 void SyncInternalsMessageHandler::HandleRequestStopKeepData(
-    const base::ListValue* args) {
-  DCHECK_EQ(0U, args->GetList().size());
+    base::Value::ConstListView args) {
+  DCHECK_EQ(0U, args.size());
 
   SyncService* service = GetSyncService();
   if (!service)
@@ -283,8 +282,8 @@
 }
 
 void SyncInternalsMessageHandler::HandleRequestStopClearData(
-    const base::ListValue* args) {
-  DCHECK_EQ(0U, args->GetList().size());
+    base::Value::ConstListView args) {
+  DCHECK_EQ(0U, args.size());
 
   SyncService* service = GetSyncService();
   if (!service)
@@ -294,7 +293,7 @@
 }
 
 void SyncInternalsMessageHandler::HandleTriggerRefresh(
-    const base::ListValue* args) {
+    base::Value::ConstListView args) {
   SyncService* service = GetSyncService();
   if (!service)
     return;
@@ -304,7 +303,7 @@
 
 void SyncInternalsMessageHandler::OnReceivedAllNodes(
     const std::string& callback_id,
-    std::unique_ptr<ListValue> nodes) {
+    std::unique_ptr<base::ListValue> nodes) {
   ResolveJavascriptCallback(base::Value(callback_id), *nodes);
 }
 
@@ -325,7 +324,7 @@
     return;
   }
 
-  base::ListValue data_types_list;
+  base::Value data_types_list(base::Value::Type::LIST);
   for (const auto& data_type_invalidation :
        payload_message.data_type_invalidations()) {
     const int field_number = data_type_invalidation.data_type_id();
@@ -357,7 +356,7 @@
 
 void SyncInternalsMessageHandler::OnGotEntityCounts(
     const std::vector<syncer::TypeEntitiesCount>& entity_counts) {
-  ListValue count_list;
+  base::Value count_list(base::Value::Type::LIST);
   for (const syncer::TypeEntitiesCount& count : entity_counts) {
     DictionaryValue count_dictionary;
     count_dictionary.SetStringPath(syncer::sync_ui_util::kModelType,
diff --git a/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h b/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h
index cd49bb0..99bb9ba 100644
--- a/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h
+++ b/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h
@@ -41,35 +41,36 @@
 
   // Fires an event to send updated data to the About page and registers
   // observers to notify the page upon updates.
-  void HandleRequestDataAndRegisterForUpdates(const base::ListValue* args);
+  void HandleRequestDataAndRegisterForUpdates(base::Value::ConstListView args);
 
   // Fires an event to send the list of types back to the page.
-  void HandleRequestListOfTypes(const base::ListValue* args);
+  void HandleRequestListOfTypes(base::Value::ConstListView args);
 
   // Fires an event to send the initial state of the "include specifics" flag.
-  void HandleRequestIncludeSpecificsInitialState(const base::ListValue* args);
+  void HandleRequestIncludeSpecificsInitialState(
+      base::Value::ConstListView args);
 
   // Handler for getAllNodes message.  Needs a |request_id| argument.
-  void HandleGetAllNodes(const base::ListValue* args);
+  void HandleGetAllNodes(base::Value::ConstListView args);
 
   // Handler for setting internal state of if specifics should be included in
   // protocol events when sent to be displayed.
-  void HandleSetIncludeSpecifics(const base::ListValue* args);
+  void HandleSetIncludeSpecifics(base::Value::ConstListView args);
 
   // Handler for writeUserEvent message.
-  void HandleWriteUserEvent(const base::ListValue* args);
+  void HandleWriteUserEvent(base::Value::ConstListView args);
 
   // Handler for requestStart message.
-  void HandleRequestStart(const base::ListValue* args);
+  void HandleRequestStart(base::Value::ConstListView args);
 
   // Handler for requestStopKeepData message.
-  void HandleRequestStopKeepData(const base::ListValue* args);
+  void HandleRequestStopKeepData(base::Value::ConstListView args);
 
   // Handler for requestStopClearData message.
-  void HandleRequestStopClearData(const base::ListValue* args);
+  void HandleRequestStopClearData(base::Value::ConstListView args);
 
   // Handler for triggerRefresh message.
-  void HandleTriggerRefresh(const base::ListValue* args);
+  void HandleTriggerRefresh(base::Value::ConstListView args);
 
   // Callback used in GetAllNodes.
   void OnReceivedAllNodes(const std::string& callback_id,
diff --git a/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler_unittest.cc b/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler_unittest.cc
index d59364e3..6961c21 100644
--- a/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler_unittest.cc
+++ b/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler_unittest.cc
@@ -25,8 +25,6 @@
 #include "content/public/test/test_web_ui.h"
 
 using base::DictionaryValue;
-using base::ListValue;
-using base::Value;
 using sync_pb::UserEventSpecifics;
 using syncer::FakeUserEventService;
 using syncer::SyncService;
@@ -206,10 +204,9 @@
 };
 
 TEST_F(SyncInternalsMessageHandlerTest, AddRemoveObservers) {
-  ListValue empty_list;
-
   EXPECT_EQ(0, test_sync_service()->add_observer_count());
-  handler()->HandleRequestDataAndRegisterForUpdates(&empty_list);
+  handler()->HandleRequestDataAndRegisterForUpdates(
+      base::Value::ConstListView());
   EXPECT_EQ(1, test_sync_service()->add_observer_count());
 
   EXPECT_EQ(0, test_sync_service()->remove_observer_count());
@@ -221,10 +218,9 @@
 }
 
 TEST_F(SyncInternalsMessageHandlerTest, AddRemoveObserversDisallowJavascript) {
-  ListValue empty_list;
-
   EXPECT_EQ(0, test_sync_service()->add_observer_count());
-  handler()->HandleRequestDataAndRegisterForUpdates(&empty_list);
+  handler()->HandleRequestDataAndRegisterForUpdates(
+      base::Value::ConstListView());
   EXPECT_EQ(1, test_sync_service()->add_observer_count());
 
   EXPECT_EQ(0, test_sync_service()->remove_observer_count());
@@ -242,8 +238,8 @@
   SyncServiceFactory::GetInstance()->SetTestingFactory(
       profile(), BrowserContextKeyedServiceFactory::TestingFactory());
 
-  ListValue empty_list;
-  handler()->HandleRequestDataAndRegisterForUpdates(&empty_list);
+  handler()->HandleRequestDataAndRegisterForUpdates(
+      base::Value::ConstListView());
   handler()->DisallowJavascript();
   // Cannot verify observer methods on sync services were not called, because
   // there is no sync service. Rather, we're just making sure the handler hasn't
@@ -251,28 +247,28 @@
 }
 
 TEST_F(SyncInternalsMessageHandlerTest, HandleGetAllNodes) {
-  ListValue args;
+  base::Value args(base::Value::Type::LIST);
   args.Append("getAllNodes_0");
-  handler()->HandleGetAllNodes(&args);
+  handler()->HandleGetAllNodes(args.GetList());
   test_sync_service()->get_all_nodes_callback().Run(
-      std::make_unique<ListValue>());
+      std::make_unique<base::ListValue>());
   EXPECT_EQ(1, CallCountWithName("cr.webUIResponse"));
 
-  ListValue args2;
+  base::Value args2(base::Value::Type::LIST);
   args2.Append("getAllNodes_1");
-  handler()->HandleGetAllNodes(&args2);
+  handler()->HandleGetAllNodes(args2.GetList());
   // This  breaks the weak ref the callback is hanging onto. Which results in
   // the call count not incrementing.
   handler()->DisallowJavascript();
   test_sync_service()->get_all_nodes_callback().Run(
-      std::make_unique<ListValue>());
+      std::make_unique<base::ListValue>());
   EXPECT_EQ(1, CallCountWithName("cr.webUIResponse"));
 
-  ListValue args3;
+  base::Value args3(base::Value::Type::LIST);
   args3.Append("getAllNodes_2");
-  handler()->HandleGetAllNodes(&args3);
+  handler()->HandleGetAllNodes(args3.GetList());
   test_sync_service()->get_all_nodes_callback().Run(
-      std::make_unique<ListValue>());
+      std::make_unique<base::ListValue>());
   EXPECT_EQ(2, CallCountWithName("cr.webUIResponse"));
 }
 
@@ -297,10 +293,10 @@
 }
 
 TEST_F(SyncInternalsMessageHandlerTest, WriteUserEvent) {
-  ListValue args;
+  base::Value args(base::Value::Type::LIST);
   args.Append("1000000000000000000");
   args.Append("-1");
-  handler()->HandleWriteUserEvent(&args);
+  handler()->HandleWriteUserEvent(args.GetList());
 
   ASSERT_EQ(1u, fake_user_event_service()->GetRecordedUserEvents().size());
   const UserEventSpecifics& event =
@@ -311,10 +307,10 @@
 }
 
 TEST_F(SyncInternalsMessageHandlerTest, WriteUserEventBadParse) {
-  ListValue args;
+  base::Value args(base::Value::Type::LIST);
   args.Append("123abc");
   args.Append("abcdefghijklmnopqrstuvwxyz");
-  handler()->HandleWriteUserEvent(&args);
+  handler()->HandleWriteUserEvent(args.GetList());
 
   ASSERT_EQ(1u, fake_user_event_service()->GetRecordedUserEvents().size());
   const UserEventSpecifics& event =
@@ -325,10 +321,10 @@
 }
 
 TEST_F(SyncInternalsMessageHandlerTest, WriteUserEventBlank) {
-  ListValue args;
+  base::Value args(base::Value::Type::LIST);
   args.Append("");
   args.Append("");
-  handler()->HandleWriteUserEvent(&args);
+  handler()->HandleWriteUserEvent(args.GetList());
 
   ASSERT_EQ(1u, fake_user_event_service()->GetRecordedUserEvents().size());
   const UserEventSpecifics& event =
@@ -342,10 +338,10 @@
 }
 
 TEST_F(SyncInternalsMessageHandlerTest, WriteUserEventZero) {
-  ListValue args;
+  base::Value args(base::Value::Type::LIST);
   args.Append("0");
   args.Append("0");
-  handler()->HandleWriteUserEvent(&args);
+  handler()->HandleWriteUserEvent(args.GetList());
 
   ASSERT_EQ(1u, fake_user_event_service()->GetRecordedUserEvents().size());
   const UserEventSpecifics& event =
diff --git a/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc b/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
index 5b4e275..6bdeac0 100644
--- a/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
+++ b/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
@@ -97,7 +97,7 @@
 }
 
 double ChromeZoomLevelPrefs::GetDefaultZoomLevelPref() const {
-  const base::DictionaryValue* default_zoom_level_dictionary =
+  const base::Value* default_zoom_level_dictionary =
       pref_service_->GetDictionary(prefs::kPartitionDefaultZoomLevel);
   return default_zoom_level_dictionary->FindDoubleKey(partition_key_)
       .value_or(0.0);
@@ -228,7 +228,8 @@
   // Initialize the HostZoomMap with per-host zoom levels from the persisted
   // zoom-level preference values.
   const base::DictionaryValue* host_zoom_dictionaries =
-      pref_service_->GetDictionary(prefs::kPartitionPerHostZoomLevels);
+      &base::Value::AsDictionaryValue(
+          *pref_service_->GetDictionary(prefs::kPartitionPerHostZoomLevels));
   const base::DictionaryValue* host_zoom_dictionary = nullptr;
   if (host_zoom_dictionaries->GetDictionary(partition_key_,
                                             &host_zoom_dictionary)) {
diff --git a/chrome/browser/version/BUILD.gn b/chrome/browser/version/BUILD.gn
index e75c23707..2b38f48 100644
--- a/chrome/browser/version/BUILD.gn
+++ b/chrome/browser/version/BUILD.gn
@@ -14,10 +14,7 @@
     "//components/version_info/android:version_constants_java",
   ]
 
-  sources = [
-    "java/src/org/chromium/chrome/browser/version/ChromeVersionInfo.java",
-    _chrome_version_java_file,
-  ]
+  sources = [ _chrome_version_java_file ]
 }
 
 process_version("chrome_version_constants") {
diff --git a/chrome/browser/version/java/src/org/chromium/chrome/browser/version/ChromeVersionInfo.java b/chrome/browser/version/java/src/org/chromium/chrome/browser/version/ChromeVersionInfo.java
deleted file mode 100644
index 027d729..0000000
--- a/chrome/browser/version/java/src/org/chromium/chrome/browser/version/ChromeVersionInfo.java
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2014 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.version;
-
-import org.chromium.components.version_info.VersionInfo;
-
-/**
- * Compatibility shim for three-sided patch.
- */
-public class ChromeVersionInfo extends VersionInfo {}
diff --git a/chrome/browser/web_applications/app_service/web_apps_publisher_host.cc b/chrome/browser/web_applications/app_service/web_apps_publisher_host.cc
index 5458ed1..d92b51e 100644
--- a/chrome/browser/web_applications/app_service/web_apps_publisher_host.cc
+++ b/chrome/browser/web_applications/app_service/web_apps_publisher_host.cc
@@ -248,7 +248,7 @@
   }
 
   auto params = apps::ConvertCrosapiToLaunchParams(launch_params, profile_);
-  if (!params.launch_files.empty()) {
+  if (!params.launch_files.empty() && !launch_params->intent) {
     // File handling may create the WebContents asynchronously.
     // TODO(crbug/1261263): implement.
     NOTIMPLEMENTED_LOG_ONCE();
diff --git a/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc b/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc
index c07817e..09b529e4 100644
--- a/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc
+++ b/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc
@@ -10,17 +10,23 @@
 #include <vector>
 
 #include "base/callback_helpers.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
 #include "base/location.h"
 #include "base/notreached.h"
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/bind.h"
+#include "base/threading/thread_restrictions.h"
 #include "base/values.h"
 #include "chrome/browser/apps/app_service/app_icon/app_icon_factory.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/intent_util.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/notifications/notification_common.h"
@@ -41,10 +47,12 @@
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
 #include "chrome/browser/web_applications/web_application_info.h"
 #include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "chromeos/crosapi/mojom/app_service.mojom.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
+#include "components/services/app_service/public/cpp/intent_util.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -52,6 +60,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/display/types/display_constants.h"
@@ -64,6 +73,8 @@
 
 namespace web_app {
 
+namespace {
+
 class MockAppPublisher : public crosapi::mojom::AppPublisher {
  public:
   MockAppPublisher() { run_loop_ = std::make_unique<base::RunLoop>(); }
@@ -110,6 +121,15 @@
   std::unique_ptr<base::RunLoop> run_loop_;
 };
 
+content::EvalJsResult ReadTextContent(content::WebContents* web_contents,
+                                      const char* id) {
+  const std::string script =
+      base::StringPrintf("document.getElementById('%s').textContent", id);
+  return content::EvalJs(web_contents, script);
+}
+
+}  // namespace
+
 class WebAppsPublisherHostBrowserTest : public WebAppControllerBrowserTest {
  public:
   WebAppsPublisherHostBrowserTest() = default;
@@ -702,4 +722,99 @@
             IconEffects::kRoundCorners | IconEffects::kCrOsStandardIcon);
 }
 
+IN_PROC_BROWSER_TEST_F(WebAppsPublisherHostBrowserTest, GetLink) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  const AppId app_id = InstallWebAppFromManifest(
+      browser(),
+      embedded_test_server()->GetURL("/web_share_target/gatherer.html"));
+  const GURL share_target_url =
+      embedded_test_server()->GetURL("/web_share_target/share.html");
+
+  MockAppPublisher mock_app_publisher;
+  WebAppsPublisherHost web_apps_publisher_host(profile());
+  web_apps_publisher_host.SetPublisherForTesting(&mock_app_publisher);
+  web_apps_publisher_host.Init();
+  mock_app_publisher.Wait();
+  EXPECT_EQ(mock_app_publisher.get_deltas().size(), 1U);
+  EXPECT_FALSE(mock_app_publisher.get_deltas().back()->intent_filters.empty());
+
+  const std::string shared_title = "My News";
+  const std::string shared_link = "http://example.com/news";
+  const GURL expected_url(share_target_url.spec() +
+                          "?headline=My+News&link=http://example.com/news");
+
+  ui_test_utils::AllBrowserTabAddedWaiter waiter;
+  {
+    auto launch_params = apps::CreateCrosapiLaunchParamsWithEventFlags(
+        apps::AppServiceProxyFactory::GetForProfile(profile()), app_id,
+        /*event_flags=*/0, apps::mojom::LaunchSource::kFromSharesheet,
+        display::kInvalidDisplayId);
+    launch_params->intent = apps_util::ConvertAppServiceToCrosapiIntent(
+        apps_util::CreateShareIntentFromText(shared_link, shared_title),
+        profile());
+
+    static_cast<crosapi::mojom::AppController&>(web_apps_publisher_host)
+        .Launch(std::move(launch_params), base::DoNothing());
+  }
+  content::WebContents* const web_contents = waiter.Wait();
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents));
+
+  EXPECT_EQ("GET", ReadTextContent(web_contents, "method"));
+  EXPECT_EQ(expected_url.spec(), ReadTextContent(web_contents, "url"));
+
+  EXPECT_EQ(shared_title, ReadTextContent(web_contents, "headline"));
+  // Gatherer web app's service worker detects omitted value.
+  EXPECT_EQ("N/A", ReadTextContent(web_contents, "author"));
+  EXPECT_EQ(shared_link, ReadTextContent(web_contents, "link"));
+}
+
+IN_PROC_BROWSER_TEST_F(WebAppsPublisherHostBrowserTest, ShareImage) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  const AppId app_id = InstallWebAppFromManifest(
+      browser(),
+      embedded_test_server()->GetURL("/web_share_target/multimedia.html"));
+  const std::string kData(12, '*');
+
+  MockAppPublisher mock_app_publisher;
+  WebAppsPublisherHost web_apps_publisher_host(profile());
+  web_apps_publisher_host.SetPublisherForTesting(&mock_app_publisher);
+  web_apps_publisher_host.Init();
+  mock_app_publisher.Wait();
+  EXPECT_EQ(mock_app_publisher.get_deltas().size(), 1U);
+  EXPECT_FALSE(mock_app_publisher.get_deltas().back()->intent_filters.empty());
+
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath image_file =
+      temp_dir.GetPath().Append(FILE_PATH_LITERAL("sample.webp"));
+  ASSERT_TRUE(base::WriteFile(image_file, kData));
+
+  ui_test_utils::AllBrowserTabAddedWaiter waiter;
+  {
+    crosapi::mojom::IntentPtr crosapi_intent = crosapi::mojom::Intent::New();
+    crosapi_intent->action = apps_util::kIntentActionSend;
+    crosapi_intent->mime_type = "image/webp";
+    std::vector<crosapi::mojom::IntentFilePtr> crosapi_files;
+    auto crosapi_file = crosapi::mojom::IntentFile::New();
+    crosapi_file->file_path = image_file;
+    crosapi_files.push_back(std::move(crosapi_file));
+    crosapi_intent->files = std::move(crosapi_files);
+
+    auto launch_params = apps::CreateCrosapiLaunchParamsWithEventFlags(
+        apps::AppServiceProxyFactory::GetForProfile(profile()), app_id,
+        /*event_flags=*/0, apps::mojom::LaunchSource::kFromSharesheet,
+        display::kInvalidDisplayId);
+    launch_params->intent = std::move(crosapi_intent);
+
+    static_cast<crosapi::mojom::AppController&>(web_apps_publisher_host)
+        .Launch(std::move(launch_params), base::DoNothing());
+  }
+  content::WebContents* const web_contents = waiter.Wait();
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents));
+
+  EXPECT_EQ("POST", ReadTextContent(web_contents, "method"));
+  EXPECT_EQ(kData, ReadTextContent(web_contents, "image"));
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/app_shim_registry_mac.cc b/chrome/browser/web_applications/app_shim_registry_mac.cc
index 75d455f..3929133 100644
--- a/chrome/browser/web_applications/app_shim_registry_mac.cc
+++ b/chrome/browser/web_applications/app_shim_registry_mac.cc
@@ -60,8 +60,7 @@
     const std::string& app_id,
     const std::string& profiles_key,
     std::set<base::FilePath>* profiles) const {
-  const base::DictionaryValue* cache =
-      GetPrefService()->GetDictionary(kAppShims);
+  const base::Value* cache = GetPrefService()->GetDictionary(kAppShims);
   const base::Value* app_info = cache->FindDictKey(app_id);
   if (!app_info)
     return;
@@ -104,8 +103,8 @@
 std::set<std::string> AppShimRegistry::GetInstalledAppsForProfile(
     const base::FilePath& profile) const {
   std::set<std::string> result;
-  const base::DictionaryValue* app_shims =
-      GetPrefService()->GetDictionary(kAppShims);
+  const base::DictionaryValue* app_shims = &base::Value::AsDictionaryValue(
+      *GetPrefService()->GetDictionary(kAppShims));
   if (!app_shims)
     return result;
   for (base::DictionaryValue::Iterator iter_app(*app_shims);
diff --git a/chrome/browser/web_applications/daily_metrics_helper.cc b/chrome/browser/web_applications/daily_metrics_helper.cc
index 3bf3cc7..7ac92cc 100644
--- a/chrome/browser/web_applications/daily_metrics_helper.cc
+++ b/chrome/browser/web_applications/daily_metrics_helper.cc
@@ -157,8 +157,8 @@
 }
 
 void EmitRecords(Profile* profile) {
-  const DictionaryValue* urls_to_features =
-      profile->GetPrefs()->GetDictionary(prefs::kWebAppsDailyMetrics);
+  const DictionaryValue* urls_to_features = &base::Value::AsDictionaryValue(
+      *profile->GetPrefs()->GetDictionary(prefs::kWebAppsDailyMetrics));
   DCHECK(urls_to_features);
 
   for (DictionaryValue::Iterator iter(*urls_to_features); !iter.IsAtEnd();
@@ -173,7 +173,7 @@
 }
 
 void RemoveRecords(PrefService* prefs) {
-  const DictionaryValue* urls_to_features =
+  const Value* urls_to_features =
       prefs->GetDictionary(prefs::kWebAppsDailyMetrics);
   if (!urls_to_features)
     return;
@@ -184,7 +184,7 @@
 void UpdateRecord(DailyInteraction& record, PrefService* prefs) {
   DCHECK(record.start_url.is_valid());
   const std::string& url = record.start_url.spec();
-  const DictionaryValue* urls_to_features =
+  const Value* urls_to_features =
       prefs->GetDictionary(prefs::kWebAppsDailyMetrics);
   CHECK(urls_to_features);
   const Value* existing_val = urls_to_features->FindDictKey(url);
diff --git a/chrome/browser/web_applications/externally_installed_web_app_prefs.cc b/chrome/browser/web_applications/externally_installed_web_app_prefs.cc
index 2d77f38d..af7c04a 100644
--- a/chrome/browser/web_applications/externally_installed_web_app_prefs.cc
+++ b/chrome/browser/web_applications/externally_installed_web_app_prefs.cc
@@ -54,7 +54,7 @@
 const base::Value* GetPreferenceValue(const PrefService* pref_service,
                                       const AppId& app_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  const base::DictionaryValue* urls_to_dicts =
+  const base::Value* urls_to_dicts =
       pref_service->GetDictionary(prefs::kWebAppsExtensionIDs);
   if (!urls_to_dicts) {
     return nullptr;
@@ -115,7 +115,7 @@
 std::map<AppId, GURL> ExternallyInstalledWebAppPrefs::BuildAppIdsMap(
     const PrefService* pref_service,
     ExternalInstallSource install_source) {
-  const base::DictionaryValue* urls_to_dicts =
+  const base::Value* urls_to_dicts =
       pref_service->GetDictionary(prefs::kWebAppsExtensionIDs);
 
   std::map<AppId, GURL> ids_to_urls;
@@ -187,7 +187,7 @@
 bool ExternallyInstalledWebAppPrefs::HasNoApps() const {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       pref_service_->GetDictionary(prefs::kWebAppsExtensionIDs);
   return dict->DictEmpty();
 }
@@ -214,7 +214,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   DCHECK(pref_service_->GetDictionary(prefs::kWebAppsExtensionIDs)
-             ->HasKey(url.spec()));
+             ->FindKey(url.spec()));
   DictionaryPrefUpdate update(pref_service_, prefs::kWebAppsExtensionIDs);
   base::Value* map = update.Get();
 
diff --git a/chrome/browser/web_applications/install_bounce_metric.cc b/chrome/browser/web_applications/install_bounce_metric.cc
index 5d0ce06..6556e83d 100644
--- a/chrome/browser/web_applications/install_bounce_metric.cc
+++ b/chrome/browser/web_applications/install_bounce_metric.cc
@@ -56,7 +56,7 @@
     const web_app::AppId& app_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  const base::DictionaryValue* ids_to_metrics =
+  const base::Value* ids_to_metrics =
       pref_service->GetDictionary(prefs::kWebAppInstallMetrics);
   if (!ids_to_metrics)
     return absl::nullopt;
diff --git a/chrome/browser/web_applications/isolation_prefs_utils.cc b/chrome/browser/web_applications/isolation_prefs_utils.cc
index 34e3a18..cd98252 100644
--- a/chrome/browser/web_applications/isolation_prefs_utils.cc
+++ b/chrome/browser/web_applications/isolation_prefs_utils.cc
@@ -83,7 +83,7 @@
 const std::string* GetStorageIsolationKey(PrefService* pref_service,
                                           const url::Origin& origin) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  const base::DictionaryValue* isolation_prefs =
+  const base::Value* isolation_prefs =
       pref_service->GetDictionary(prefs::kWebAppsIsolationState);
   if (!isolation_prefs)
     return nullptr;
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager.cc b/chrome/browser/web_applications/policy/web_app_policy_manager.cc
index f10e28d..0a6ce6e 100644
--- a/chrome/browser/web_applications/policy/web_app_policy_manager.cc
+++ b/chrome/browser/web_applications/policy/web_app_policy_manager.cc
@@ -231,8 +231,8 @@
   // No need to validate the types or values of the policy members because we
   // are using a SimpleSchemaValidatingPolicyHandler which should validate them
   // for us.
-  const base::DictionaryValue* web_app_dict =
-      pref_service_->GetDictionary(prefs::kWebAppSettings);
+  const base::DictionaryValue* web_app_dict = &base::Value::AsDictionaryValue(
+      *pref_service_->GetDictionary(prefs::kWebAppSettings));
 
   settings_by_url_.clear();
   default_settings_ = std::make_unique<WebAppPolicyManager::WebAppSetting>();
@@ -534,7 +534,7 @@
   if (!local_state)  // Sometimes it's not available in tests.
     return;
 
-  const base::ListValue* disabled_system_features_pref =
+  const base::Value* disabled_system_features_pref =
       local_state->GetList(policy::policy_prefs::kSystemFeaturesDisableList);
   if (!disabled_system_features_pref)
     return;
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager.cc b/chrome/browser/web_applications/preinstalled_web_app_manager.cc
index 55585133..af734bb 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager.cc
@@ -601,7 +601,7 @@
     std::vector<ExternalInstallOptions> desired_apps_install_options) {
   DCHECK(externally_managed_app_manager_);
 
-  std::map<GURL, std::vector<AppId>> desired_uninstalls;
+  std::map<InstallUrl, std::vector<AppId>> desired_uninstalls;
   for (const auto& entry : desired_apps_install_options) {
     if (!entry.uninstall_and_replace.empty())
       desired_uninstalls.emplace(entry.install_url,
@@ -617,9 +617,10 @@
 
 void PreinstalledWebAppManager::OnExternalWebAppsSynchronized(
     ExternallyManagedAppManager::SynchronizeCallback callback,
-    std::map<GURL, std::vector<AppId>> desired_uninstalls,
-    std::map<GURL, ExternallyManagedAppManager::InstallResult> install_results,
-    std::map<GURL, bool> uninstall_results) {
+    std::map<InstallUrl, std::vector<AppId>> desired_uninstalls,
+    std::map<InstallUrl, ExternallyManagedAppManager::InstallResult>
+        install_results,
+    std::map<InstallUrl, bool> uninstall_results) {
   // Note that we are storing the Chrome version (milestone number) instead of a
   // "has synchronised" bool in order to do version update specific logic.
   profile_->GetPrefs()->SetString(
@@ -652,7 +653,7 @@
     if (iter == desired_uninstalls.end())
       continue;
 
-    for (const auto& replace_id : iter->second) {
+    for (const AppId& replace_id : iter->second) {
       // We mark the app as migrated to a web app as long as the
       // installation was successful, even if the previous app was not
       // installed. This ensures we properly re-install apps if the
@@ -714,8 +715,9 @@
 }
 
 void PreinstalledWebAppManager::OnStartUpTaskCompleted(
-    std::map<GURL, ExternallyManagedAppManager::InstallResult> install_results,
-    std::map<GURL, bool> uninstall_results) {
+    std::map<InstallUrl, ExternallyManagedAppManager::InstallResult>
+        install_results,
+    std::map<InstallUrl, bool> uninstall_results) {
   if (debug_info_) {
     debug_info_->is_start_up_task_complete = true;
     debug_info_->install_results = std::move(install_results);
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager.h b/chrome/browser/web_applications/preinstalled_web_app_manager.h
index 84ea633..9d836aa 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager.h
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager.h
@@ -51,6 +51,7 @@
   using ConsumeInstallOptions =
       base::OnceCallback<void(std::vector<ExternalInstallOptions>)>;
   using SynchronizeCallback = ExternallyManagedAppManager::SynchronizeCallback;
+  using InstallUrl = GURL;
 
   // Observes whether default chrome app migration has completed and
   // triggers MostVisitedHandler to refresh the NTP tiles.
@@ -116,8 +117,9 @@
     using DisabledConfigWithReason =
         std::pair<ExternalInstallOptions, std::string>;
     std::vector<DisabledConfigWithReason> disabled_configs;
-    std::map<GURL, ExternallyManagedAppManager::InstallResult> install_results;
-    std::map<GURL, bool> uninstall_results;
+    std::map<InstallUrl, ExternallyManagedAppManager::InstallResult>
+        install_results;
+    std::map<InstallUrl, bool> uninstall_results;
   };
   const DebugInfo* debug_info() const { return debug_info_.get(); }
 
@@ -135,14 +137,14 @@
                    std::vector<ExternalInstallOptions>);
   void OnExternalWebAppsSynchronized(
       ExternallyManagedAppManager::SynchronizeCallback callback,
-      std::map<GURL, std::vector<AppId>> desired_uninstall_and_replaces,
-      std::map<GURL, ExternallyManagedAppManager::InstallResult>
+      std::map<InstallUrl, std::vector<AppId>> desired_uninstall_and_replaces,
+      std::map<InstallUrl, ExternallyManagedAppManager::InstallResult>
           install_results,
-      std::map<GURL, bool> uninstall_results);
+      std::map<InstallUrl, bool> uninstall_results);
   void OnStartUpTaskCompleted(
-      std::map<GURL, ExternallyManagedAppManager::InstallResult>
+      std::map<InstallUrl, ExternallyManagedAppManager::InstallResult>
           install_results,
-      std::map<GURL, bool> uninstall_results);
+      std::map<InstallUrl, bool> uninstall_results);
 
   // The directory where default web app configs are stored.
   // Empty if not applicable.
diff --git a/chrome/browser/web_applications/preinstalled_web_app_utils.cc b/chrome/browser/web_applications/preinstalled_web_app_utils.cc
index 602bdee..badabea 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_utils.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_utils.cc
@@ -586,7 +586,7 @@
 }
 
 bool WasAppMigratedToWebApp(Profile* profile, const std::string& app_id) {
-  const base::ListValue* migrated_apps =
+  const base::Value* migrated_apps =
       profile->GetPrefs()->GetList(webapps::kWebAppsMigratedPreinstalledApps);
   if (!migrated_apps)
     return false;
@@ -611,7 +611,7 @@
 }
 
 bool WasMigrationRun(Profile* profile, base::StringPiece feature_name) {
-  const base::ListValue* migrated_features =
+  const base::Value* migrated_features =
       profile->GetPrefs()->GetList(prefs::kWebAppsDidMigrateDefaultChromeApps);
   if (!migrated_features)
     return false;
@@ -637,7 +637,7 @@
 
 bool WasPreinstalledAppUninstalled(Profile* profile,
                                    const std::string& app_id) {
-  const base::ListValue* uninstalled_apps =
+  const base::Value* uninstalled_apps =
       profile->GetPrefs()->GetList(prefs::kWebAppsUninstalledDefaultChromeApps);
   if (!uninstalled_apps)
     return false;
diff --git a/chrome/browser/web_applications/web_app_prefs_utils.cc b/chrome/browser/web_applications/web_app_prefs_utils.cc
index d11d2a6..45d65ef 100644
--- a/chrome/browser/web_applications/web_app_prefs_utils.cc
+++ b/chrome/browser/web_applications/web_app_prefs_utils.cc
@@ -28,7 +28,7 @@
     const PrefService* pref_service,
     const AppId& app_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  const base::DictionaryValue* web_apps_prefs =
+  const base::Value* web_apps_prefs =
       pref_service->GetDictionary(prefs::kWebAppsPreferences);
   if (!web_apps_prefs)
     return nullptr;
diff --git a/chrome/browser/webauthn/cablev2_devices.cc b/chrome/browser/webauthn/cablev2_devices.cc
index a1b6549..7a70bd8 100644
--- a/chrome/browser/webauthn/cablev2_devices.cc
+++ b/chrome/browser/webauthn/cablev2_devices.cc
@@ -206,7 +206,7 @@
 
 std::vector<std::unique_ptr<Pairing>> GetLinkedDevices(Profile* const profile) {
   PrefService* const prefs = profile->GetPrefs();
-  const base::ListValue* pref_pairings =
+  const base::Value* pref_pairings =
       prefs->GetList(kWebAuthnCablePairingsPrefName);
 
   std::vector<std::unique_ptr<Pairing>> ret;
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index 94dc086..61c876f 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -72,7 +72,7 @@
     const std::string& relying_party_id) {
   const Profile* profile = Profile::FromBrowserContext(browser_context);
   const PrefService* prefs = profile->GetPrefs();
-  const base::ListValue* permit_attestation =
+  const base::Value* permit_attestation =
       prefs->GetList(prefs::kSecurityKeyPermitAttestation);
   return std::any_of(permit_attestation->GetList().begin(),
                      permit_attestation->GetList().end(),
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 9b00f95..85fe8609 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1639828705-c613c8ebfe579ca55114bb4d2300daea1b2d9532.profdata
+chrome-linux-main-1640066156-94933233ec1b6a9a6f24d16c4397135c77f89aa2.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 4ae3340..13c9a0c 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1639828705-b53a72807238bf278d3cf09ba81271fc19d9e318.profdata
+chrome-mac-main-1640066156-4fbc4846d6f8fa2a4568597d67d22a8aabccbc33.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index c846f5a..bce0f4d3 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1639828705-98c049ab1f42948b2defd2fc6b3a65de11cf091d.profdata
+chrome-win32-main-1640066156-e9886fd90650bc1f12cec7762cf16f81ce3c4692.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index d6cb04b..6dba82e 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1639800133-8bcfeb4a8b52b90be5b9d2f3cbbb02a816fc7309.profdata
+chrome-win64-main-1640076128-d59cda87adda071e89873e0874334903ea90dfa5.profdata
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index 694fb45..c540719 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//ash/ambient/resources/resources.gni")
 import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/locales.gni")
 import("//chrome/browser/buildflags.gni")
@@ -305,6 +306,11 @@
           "//ash/webui/resources:sample_system_web_app_untrusted_resources",
         ]
       }
+
+      if (include_ash_ambient_animation_resources) {
+        sources += [ "$root_gen_dir/ash/ambient/resources/ash_ambient_lottie_resources.pak" ]
+        deps += [ "//ash/ambient/resources:lottie_resources" ]
+      }
     }
     if (is_linux || is_chromeos) {
       sources += [ "$root_gen_dir/chrome/webui_js_error_resources.pak" ]
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index b4f5e2b..f8ef4ba 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -735,7 +735,10 @@
   }
 
   if (is_win) {
-    sources += [ "conflicts/module_event_sink_win.mojom" ]
+    sources += [
+      "conflicts/module_event_sink_win.mojom",
+      "font_prewarmer.mojom",
+    ]
   }
 
   if (enable_offline_pages) {
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 9c20459..4c16548 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -272,6 +272,11 @@
 const base::Feature kDesktopPWAsCacheDuringDefaultInstall{
     "DesktopPWAsCacheDuringDefaultInstall", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Generates customised default offline page that is shown when web app is
+// offline if no custom page is provided by developer.
+const base::Feature kDesktopPWAsDefaultOfflinePage{
+    "DesktopPWAsDefaultOfflinePage", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Moves the Extensions "puzzle piece" icon from the title bar into the app menu
 // for web app windows.
 const base::Feature kDesktopPWAsElidedExtensionsMenu{
@@ -669,6 +674,11 @@
                                    base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
+#if defined(OS_CHROMEOS)
+const base::Feature kLinkCapturingUiUpdate{"LinkCapturingUiUpdate",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 COMPONENT_EXPORT(CHROME_FEATURES)
 const base::Feature kLinuxLowMemoryMonitor{"LinuxLowMemoryMonitor",
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 3e18c8b..ae39a1e2 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -195,6 +195,9 @@
 extern const base::Feature kDesktopPWAsCacheDuringDefaultInstall;
 
 COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kDesktopPWAsDefaultOfflinePage;
+
+COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kDesktopPWAsElidedExtensionsMenu;
 
 COMPONENT_EXPORT(CHROME_FEATURES)
@@ -448,6 +451,11 @@
 extern const base::Feature kKernelnextVMs;
 #endif
 
+#if defined(OS_CHROMEOS)
+COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kLinkCapturingUiUpdate;
+#endif
+
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kLinuxLowMemoryMonitor;
diff --git a/chrome/common/font_prewarmer.mojom b/chrome/common/font_prewarmer.mojom
new file mode 100644
index 0000000..c5b9692
--- /dev/null
+++ b/chrome/common/font_prewarmer.mojom
@@ -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.
+
+module chrome.mojom;
+
+// Used to prewarm fonts. This interface is exposed per renderer. Note
+// that prewarming actually happens in a background thread.
+interface FontPrewarmer {
+  // Prewarms fonts previously returned via RenderFrameFontFamilyAccessor.
+  PrewarmFonts(array<string> primary_font_names,
+               array<string> fallback_font_names);
+};
+
+// Used to request the fonts used to generate first contenful paint. This is
+// exposed per RenderFrame and is an associated interface.
+interface RenderFrameFontFamilyAccessor {
+  // Requests the set of fonts used to generate first contentful paint.
+  GetFontFamilyNames() => (array<string> primary_font_names,
+                           array<string> fallback_font_names);
+};
diff --git a/chrome/installer/mac/BUILD.gn b/chrome/installer/mac/BUILD.gn
index a2d2713..0053726 100644
--- a/chrome/installer/mac/BUILD.gn
+++ b/chrome/installer/mac/BUILD.gn
@@ -166,7 +166,8 @@
       # Help the compiler find lipo for creating fat binaries.
       "-B",
       mac_bin_path,
-
+      "-fno-lto",
+      "-fno-whole-program-vtables",
       "-arch",
       "x86_64",
       "-arch",
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index cb4edc6..5ed326e8 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -377,6 +377,12 @@
 
   if (is_win) {
     deps += [ "//third_party/wtl" ]
+    sources += [
+      "font_prewarmer.cc",
+      "font_prewarmer.h",
+      "render_frame_font_family_accessor.cc",
+      "render_frame_font_family_accessor.h",
+    ]
   }
 
   if (is_chromeos_ash || is_chromeos_lacros) {
diff --git a/chrome/renderer/browser_exposed_renderer_interfaces.cc b/chrome/renderer/browser_exposed_renderer_interfaces.cc
index 9928133..84a7b0f 100644
--- a/chrome/renderer/browser_exposed_renderer_interfaces.cc
+++ b/chrome/renderer/browser_exposed_renderer_interfaces.cc
@@ -33,6 +33,10 @@
 #endif  // BUILDFLAG(USE_TCMALLOC)
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if defined(OS_WIN)
+#include "chrome/renderer/font_prewarmer.h"
+#endif
+
 namespace {
 
 void BindWebRTCLoggingAgent(
@@ -89,4 +93,9 @@
   binders->Add(base::BindRepeating(&BindSpellChecker, client),
                base::SequencedTaskRunnerHandle::Get());
 #endif
+
+#if defined(OS_WIN)
+  binders->Add(base::BindRepeating(&FontPrewarmer::Bind),
+               base::SequencedTaskRunnerHandle::Get());
+#endif
 }
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 7c601387..5f853f9 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -176,6 +176,7 @@
 
 #if defined(OS_WIN)
 #include "base/win/windows_version.h"
+#include "chrome/renderer/render_frame_font_family_accessor.h"
 #endif
 
 #if BUILDFLAG(ENABLE_FEED_V2)
@@ -726,6 +727,13 @@
                             render_frame->GetRoutingID()));
   }
 #endif
+
+#if defined(OS_WIN)
+  if (render_frame->IsMainFrame()) {
+    associated_interfaces->AddInterface(base::BindRepeating(
+        &RenderFrameFontFamilyAccessor::Bind, render_frame));
+  }
+#endif
 }
 
 void ChromeContentRendererClient::WebViewCreated(blink::WebView* web_view) {
diff --git a/chrome/renderer/font_prewarmer.cc b/chrome/renderer/font_prewarmer.cc
new file mode 100644
index 0000000..3f676456
--- /dev/null
+++ b/chrome/renderer/font_prewarmer.cc
@@ -0,0 +1,45 @@
+// 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 "chrome/renderer/font_prewarmer.h"
+
+#include "content/public/renderer/render_frame.h"
+#include "third_party/blink/public/platform/web_font_prewarmer.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_performance.h"
+#include "third_party/blink/public/web/win/web_font_rendering.h"
+
+// static
+void FontPrewarmer::Bind(
+    mojo::PendingReceiver<chrome::mojom::FontPrewarmer> pending_receiver) {
+  new FontPrewarmer(std::move(pending_receiver));
+}
+
+void FontPrewarmer::PrewarmFonts(
+    const std::vector<std::string>& primary_font_names,
+    const std::vector<std::string>& fallback_font_names) {
+  blink::WebFontPrewarmer* prewarmer =
+      blink::WebFontRendering::GetFontPrewarmer();
+  // `prewarmer` is not always present, such as in --single-process.
+  if (!prewarmer)
+    return;
+
+  for (const std::string& font_name : primary_font_names)
+    prewarmer->PrewarmFamily(blink::WebString::FromUTF8(font_name));
+  for (const std::string& font_name : fallback_font_names)
+    prewarmer->PrewarmFamily(blink::WebString::FromUTF8(font_name));
+}
+
+FontPrewarmer::FontPrewarmer(
+    mojo::PendingReceiver<chrome::mojom::FontPrewarmer> pending_receiver)
+    : receiver_(this, std::move(pending_receiver)) {
+  receiver_.set_disconnect_handler(
+      base::BindOnce(&FontPrewarmer::OnDisconnect, base::Unretained(this)));
+}
+
+FontPrewarmer::~FontPrewarmer() = default;
+
+void FontPrewarmer::OnDisconnect() {
+  delete this;
+}
diff --git a/chrome/renderer/font_prewarmer.h b/chrome/renderer/font_prewarmer.h
new file mode 100644
index 0000000..ae06604
--- /dev/null
+++ b/chrome/renderer/font_prewarmer.h
@@ -0,0 +1,36 @@
+// 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 CHROME_RENDERER_FONT_PREWARMER_H_
+#define CHROME_RENDERER_FONT_PREWARMER_H_
+
+#include "chrome/common/font_prewarmer.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+// Trivial forwards request from chrome::mojom::FontPrewarmer to blink.
+class FontPrewarmer : public chrome::mojom::FontPrewarmer {
+ public:
+  FontPrewarmer(const FontPrewarmer&) = delete;
+  FontPrewarmer& operator=(const FontPrewarmer&) = delete;
+
+  static void Bind(
+      mojo::PendingReceiver<chrome::mojom::FontPrewarmer> pending_receiver);
+
+  // chrome::mojom::FontPrewarmer:
+  void PrewarmFonts(
+      const std::vector<std::string>& primary_font_names,
+      const std::vector<std::string>& fallback_font_names) override;
+
+ private:
+  FontPrewarmer(
+      mojo::PendingReceiver<chrome::mojom::FontPrewarmer> pending_receiver);
+  ~FontPrewarmer() override;
+
+  void OnDisconnect();
+
+  mojo::Receiver<chrome::mojom::FontPrewarmer> receiver_;
+};
+
+#endif  // CHROME_RENDERER_FONT_PREWARMER_H_
diff --git a/chrome/renderer/render_frame_font_family_accessor.cc b/chrome/renderer/render_frame_font_family_accessor.cc
new file mode 100644
index 0000000..4076971
--- /dev/null
+++ b/chrome/renderer/render_frame_font_family_accessor.cc
@@ -0,0 +1,102 @@
+// 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 "chrome/renderer/render_frame_font_family_accessor.h"
+
+#include "content/public/renderer/render_frame.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_performance.h"
+
+namespace {
+
+std::vector<std::string> WebStringVectorToStl(
+    const blink::WebVector<blink::WebString>& web_vector) {
+  std::vector<std::string> stl_vector;
+  for (const blink::WebString& web_string : web_vector)
+    stl_vector.push_back(web_string.Utf8());
+  return stl_vector;
+}
+
+}  // namespace
+
+// static
+void RenderFrameFontFamilyAccessor::Bind(
+    content::RenderFrame* render_frame,
+    mojo::PendingAssociatedReceiver<
+        chrome::mojom::RenderFrameFontFamilyAccessor> pending_receiver) {
+  new RenderFrameFontFamilyAccessor(render_frame, std::move(pending_receiver));
+}
+
+RenderFrameFontFamilyAccessor::RenderFrameFontFamilyAccessor(
+    content::RenderFrame* render_frame,
+    mojo::PendingAssociatedReceiver<
+        chrome::mojom::RenderFrameFontFamilyAccessor> pending_receiver)
+    : RenderFrameObserver(render_frame),
+      receiver_(this, std::move(pending_receiver)) {
+  // While unlikely, it is possible the fonts were requested after fcp. If this
+  // happens copy the fonts now.
+  if (ShouldGetFontNames())
+    GetFontNames();
+}
+
+RenderFrameFontFamilyAccessor::~RenderFrameFontFamilyAccessor() {
+  // Mojo requires the receiver to be destroyed before the callback, otherwise
+  // a DCHECK will be hit.
+  receiver_.reset();
+}
+
+bool RenderFrameFontFamilyAccessor::ShouldGetFontNames() const {
+  return !render_frame()
+              ->GetWebFrame()
+              ->Performance()
+              .FirstContentfulPaintRenderedButNotPresentedAsMonotonicTime()
+              .is_null();
+}
+
+void RenderFrameFontFamilyAccessor::GetFontNames() {
+  family_names_ = render_frame()->GetWebFrame()->GetWebFontFamilyNames();
+}
+
+void RenderFrameFontFamilyAccessor::GetFontFamilyNames(
+    GetFontFamilyNamesCallback callback) {
+  if (got_font_names()) {
+    RunCallback(std::move(callback));
+  } else {
+    // Browser side only requests once per interface.
+    DCHECK(!callback_);
+    callback_ = std::move(callback);
+  }
+}
+
+void RenderFrameFontFamilyAccessor::OnDestruct() {
+  delete this;
+}
+
+void RenderFrameFontFamilyAccessor::DidChangePerformanceTiming() {
+  if (!got_commit_ || got_font_names() || !ShouldGetFontNames())
+    return;
+
+  GetFontNames();
+  if (callback_)
+    RunCallback(std::move(callback_));
+}
+
+void RenderFrameFontFamilyAccessor::ReadyToCommitNavigation(
+    blink::WebDocumentLoader* docuqment_loader) {
+  if (got_commit_) {
+    // This is the second time ReadyToCommitNavigation() has been called.
+    // This means the renderer has started loading a different page than the
+    // fonts were originally requested for.
+    delete this;
+  } else {
+    got_commit_ = true;
+  }
+}
+
+void RenderFrameFontFamilyAccessor::RunCallback(
+    GetFontFamilyNamesCallback callback) {
+  std::move(callback).Run(
+      WebStringVectorToStl(family_names_->primary_family_names),
+      WebStringVectorToStl(family_names_->fallback_family_names));
+}
diff --git a/chrome/renderer/render_frame_font_family_accessor.h b/chrome/renderer/render_frame_font_family_accessor.h
new file mode 100644
index 0000000..9202c64
--- /dev/null
+++ b/chrome/renderer/render_frame_font_family_accessor.h
@@ -0,0 +1,70 @@
+// 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 CHROME_RENDERER_RENDER_FRAME_FONT_FAMILY_ACCESSOR_H_
+#define CHROME_RENDERER_RENDER_FRAME_FONT_FAMILY_ACCESSOR_H_
+
+#include "chrome/common/font_prewarmer.mojom.h"
+#include "content/public/renderer/render_frame_observer.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/web/win/web_font_family_names.h"
+
+namespace content {
+class RenderFrame;
+}
+
+// Waits for the render frame to generate fcp and copies the font names so that
+// they can be supplied back to browser via
+// chrome::mojom::RenderFrameFontFamilyAccessor .
+class RenderFrameFontFamilyAccessor
+    : public chrome::mojom::RenderFrameFontFamilyAccessor,
+      public content::RenderFrameObserver {
+ public:
+  RenderFrameFontFamilyAccessor(const RenderFrameFontFamilyAccessor&) = delete;
+  RenderFrameFontFamilyAccessor& operator=(
+      const RenderFrameFontFamilyAccessor&) = delete;
+
+  static void Bind(
+      content::RenderFrame* render_frame,
+      mojo::PendingAssociatedReceiver<
+          chrome::mojom::RenderFrameFontFamilyAccessor> pending_receiver);
+
+  // chrome::mojom::RenderFrameFontFamilyAccessor:
+  void GetFontFamilyNames(GetFontFamilyNamesCallback callback) override;
+
+  // content::RenderFrameObserver:
+  void OnDestruct() override;
+  void DidChangePerformanceTiming() override;
+  void ReadyToCommitNavigation(
+      blink::WebDocumentLoader* document_loader) override;
+
+ private:
+  RenderFrameFontFamilyAccessor(
+      content::RenderFrame* render_frame,
+      mojo::PendingAssociatedReceiver<
+          chrome::mojom::RenderFrameFontFamilyAccessor> pending_receiver);
+  ~RenderFrameFontFamilyAccessor() override;
+
+  // Returns true if the font names should be obtained.
+  bool ShouldGetFontNames() const;
+
+  // Copies the fonts from blink.
+  void GetFontNames();
+
+  // Returns true if the font names have been copied.
+  bool got_font_names() const { return family_names_.has_value(); }
+
+  void RunCallback(GetFontFamilyNamesCallback callback);
+
+  absl::optional<blink::WebFontFamilyNames> family_names_;
+  GetFontFamilyNamesCallback callback_;
+  mojo::AssociatedReceiver<chrome::mojom::RenderFrameFontFamilyAccessor>
+      receiver_;
+  // Whether ReadyToCommitNavigation() has been called.
+  bool got_commit_ = false;
+};
+
+#endif  // CHROME_RENDERER_RENDER_FRAME_FONT_FAMILY_ACCESSOR_H_
diff --git a/chrome/renderer/resources/renderer_resources.grd b/chrome/renderer/resources/renderer_resources.grd
index ed5dfd4..c75a7d2 100644
--- a/chrome/renderer/resources/renderer_resources.grd
+++ b/chrome/renderer/resources/renderer_resources.grd
@@ -48,7 +48,7 @@
         <include name="IDR_TTS_ENGINE_CUSTOM_BINDINGS_JS" file="extensions\tts_engine_custom_bindings.js" type="BINDATA" />
         <include name="IDR_WEBRTC_DESKTOP_CAPTURE_PRIVATE_CUSTOM_BINDINGS_JS" file="extensions\webrtc_desktop_capture_private_custom_bindings.js" type="BINDATA" />
         <include name="IDR_WEBRTC_LOGGING_PRIVATE_CUSTOM_BINDINGS_JS" file="extensions\webrtc_logging_private_custom_bindings.js" type="BINDATA" />
-        <if expr="chromeos or lacros">
+        <if expr="chromeos_ash or chromeos_lacros">
           <include name="IDR_ENTERPRISE_PLATFORM_KEYS_CUSTOM_BINDINGS_JS" file="extensions\enterprise_platform_keys_custom_bindings.js" type="BINDATA" />
           <include name="IDR_ENTERPRISE_PLATFORM_KEYS_KEY_PAIR_JS" file="extensions\enterprise_platform_keys\key_pair.js" type="BINDATA" />
           <include name="IDR_ENTERPRISE_PLATFORM_KEYS_SUBTLE_CRYPTO_JS" file="extensions\enterprise_platform_keys\subtle_crypto.js" type="BINDATA" />
diff --git a/chrome/renderer/subresource_redirect/robots_rules_parser.cc b/chrome/renderer/subresource_redirect/robots_rules_parser.cc
index 155cf7e..4dfeb88b 100644
--- a/chrome/renderer/subresource_redirect/robots_rules_parser.cc
+++ b/chrome/renderer/subresource_redirect/robots_rules_parser.cc
@@ -32,9 +32,9 @@
 // That is, the pattern must match the whole string in order to match.
 //
 // We can convert the given |robots_rule| to one that is compatible with
-// |base::MatchPattern| by taking care of optionally-present '$' character and
-// backslash-escaping any '?' characters, since they should be interpreted
-// literally .
+// |base::MatchPattern| by taking care of the optionally-present '$' character
+// at the end of the line and backslash-escaping any '?' characters, since they
+// should be interpreted literally.
 std::string ConvertRobotsRuleToGlob(const std::string& robots_rule) {
   if (robots_rule.empty())
     return "*";
@@ -137,6 +137,7 @@
 RobotsRulesParser::CheckRobotsRules(int routing_id,
                                     const GURL& url,
                                     CheckResultCallback callback) {
+  DCHECK(url.is_valid());
   std::string path_with_query = url.path();
   if (url.has_query())
     base::StrAppend(&path_with_query, {"?", url.query()});
diff --git a/chrome/renderer/subresource_redirect/robots_rules_parser.h b/chrome/renderer/subresource_redirect/robots_rules_parser.h
index 481c1c5c..322d86a 100644
--- a/chrome/renderer/subresource_redirect/robots_rules_parser.h
+++ b/chrome/renderer/subresource_redirect/robots_rules_parser.h
@@ -80,12 +80,15 @@
 
   // Check whether the URL is allowed or disallowed by robots rules. When the
   // determination can be made immediately, the decision should be returned.
-  // Otherwise absl::nullopt should be returned and the |callback| will be
-  // added to |pending_check_requests_| and called when a decision can be made
-  // like when rules are retrieved, or rule fetch timeout, etc.
-  // The robots rules check will make use of the |url| path and query
-  // parameters.The |url| origin, ref fragment, etc are immaterial. |routing_id|
-  // is the render frame ID for which this URL is requested for.
+  // Otherwise, absl::nullopt should be returned and |callback| will be added to
+  // |pending_check_requests_| and called when a decision can be made, like when
+  // rules are retrieved, a rule fetch times out, etc.
+  //
+  // |routing_id| is the render frame ID for which this URL is requested for.
+  //
+  // The robots rules check will use the |url| path and query parameters, but
+  // the origin, ref fragment, etc are immaterial. Note that the |url| must be
+  // valid.
   absl::optional<CheckResult> CheckRobotsRules(int routing_id,
                                                const GURL& url,
                                                CheckResultCallback callback);
diff --git a/chrome/renderer/subresource_redirect/robots_rules_parser_cache.cc b/chrome/renderer/subresource_redirect/robots_rules_parser_cache.cc
index 7edfb3e6..7326a1f2 100644
--- a/chrome/renderer/subresource_redirect/robots_rules_parser_cache.cc
+++ b/chrome/renderer/subresource_redirect/robots_rules_parser_cache.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/renderer/subresource_redirect/robots_rules_parser_cache.h"
 
+#include "base/check.h"
 #include "base/no_destructor.h"
 #include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
 
@@ -47,6 +48,7 @@
     int routing_id,
     const GURL& url,
     RobotsRulesParser::CheckResultCallback callback) {
+  DCHECK(url.is_valid());
   auto it = parsers_cache_.Get(url::Origin::Create(url));
   if (it == parsers_cache_.end()) {
     return RobotsRulesParser::CheckResult::kEntryMissing;
diff --git a/chrome/renderer/subresource_redirect/robots_rules_parser_cache.h b/chrome/renderer/subresource_redirect/robots_rules_parser_cache.h
index 85b517b..0a420f97 100644
--- a/chrome/renderer/subresource_redirect/robots_rules_parser_cache.h
+++ b/chrome/renderer/subresource_redirect/robots_rules_parser_cache.h
@@ -44,7 +44,7 @@
   // rules parser for the url origin. When the determination can be made
   // immediately, the decision should be returned. Otherwise absl::nullopt
   // should be returned and the |callback| will be invoked when the decision was
-  // made.
+  // made. Note that |url| must be valid.
   absl::optional<RobotsRulesParser::CheckResult> CheckRobotsRules(
       int routing_id,
       const GURL& url,
diff --git a/chrome/renderer/subresource_redirect/robots_rules_parser_fuzzer.cc b/chrome/renderer/subresource_redirect/robots_rules_parser_fuzzer.cc
index 776374e..fa71be70 100644
--- a/chrome/renderer/subresource_redirect/robots_rules_parser_fuzzer.cc
+++ b/chrome/renderer/subresource_redirect/robots_rules_parser_fuzzer.cc
@@ -7,10 +7,13 @@
 #include <stdint.h>
 
 #include "base/command_line.h"
+#include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_timeouts.h"
+#include "base/time/time.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/icu/fuzzers/fuzzer_utils.h"
+#include "url/gurl.h"
 
 #include "chrome/renderer/subresource_redirect/robots_rules_parser.h"
 
@@ -37,13 +40,15 @@
 
   FuzzedDataProvider provider(data, size);
   std::string rules = provider.ConsumeRandomLengthString();
-  std::string url = provider.ConsumeRandomLengthString();
+  const GURL url(provider.ConsumeRandomLengthString());
+  if (!url.is_valid())  // Required by RobotsRulesParser::CheckRobotsRules.
+    return 0;
 
   base::RunLoop run_loop;
 
   RobotsRulesParser parser(base::Seconds(1));
   parser.UpdateRobotsRules({std::move(rules)});
-  parser.CheckRobotsRules(0, GURL(std::move(url)),
+  parser.CheckRobotsRules(0, url,
                           base::BindOnce(
                               [](base::RepeatingClosure run_loop_quit,
                                  RobotsRulesParser::CheckResult result) {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b158c12b..bd13f74 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1781,6 +1781,7 @@
       "../browser/prefetch/prefetch_proxy/prefetch_proxy_test_utils.cc",
       "../browser/prefetch/prefetch_proxy/prefetch_proxy_test_utils.h",
       "../browser/prefetch/search_prefetch/search_prefetch_service_browsertest.cc",
+      "../browser/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper_browsertest.cc",
       "../browser/prefs/pref_functional_browsertest.cc",
       "../browser/prefs/pref_service_browsertest.cc",
       "../browser/prefs/tracked/pref_hash_browsertest.cc",
@@ -2218,6 +2219,7 @@
       sources += [
         "../browser/chrome_browser_main_win_browsertest.cc",
         "../browser/chrome_main_browsertest.cc",
+        "../browser/font_prewarmer_tab_helper_browsertest.cc",
         "../browser/importer/edge_importer_browsertest_win.cc",
         "../browser/importer/ie_importer_browsertest_win.cc",
         "../browser/net/chrome_mojo_proxy_resolver_win_browsertest.cc",
@@ -2394,6 +2396,7 @@
         "//chromeos/ui/base",
         "//chromeos/ui/frame:test_support",
         "//components/account_manager_core:test_support",
+        "//components/services/app_service/public/cpp:intents",
       ]
     }
 
@@ -3188,7 +3191,8 @@
         "../browser/ash/first_run/drive_first_run_browsertest.cc",
         "../browser/ash/guest_os/guest_os_registry_service_icon_browsertest.cc",
         "../browser/ash/input_method/input_method_engine_browsertests.cc",
-        "../browser/ash/input_method/native_input_method_engine_browsertest.cc",
+        "../browser/ash/input_method/native_input_method_engine_with_ime_service_browsertest.cc",
+        "../browser/ash/input_method/native_input_method_engine_without_ime_service_browsertest.cc",
         "../browser/ash/input_method/textinput_browsertest.cc",
         "../browser/ash/input_method/textinput_surroundingtext_browsertest.cc",
         "../browser/ash/input_method/textinput_test_helper.cc",
@@ -3513,7 +3517,6 @@
         "../browser/ui/browser_navigator_browsertest_chromeos.cc",
         "../browser/ui/extensions/application_launch_browsertest.cc",
         "../browser/ui/settings_window_manager_browsertest_chromeos.cc",
-        "../browser/ui/sharing_hub/sharing_hub_bubble_controller_chromeos_browsertest.cc",
         "../browser/ui/views/apps/app_dialog/app_dialog_view_browsertest.cc",
         "../browser/ui/views/apps/app_dialog/app_uninstall_dialog_view_browsertest.cc",
         "../browser/ui/views/apps/chrome_native_app_window_views_aura_ash_browsertest.cc",
@@ -4534,6 +4537,7 @@
     "../browser/custom_handlers/test_protocol_handler_registry_delegate.h",
     "../browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc",
     "../browser/data_saver/subresource_redirect_login_robots_unittest.cc",
+    "../browser/download/bubble/download_display_controller_unittest.cc",
     "../browser/download/chrome_download_manager_delegate_unittest.cc",
     "../browser/download/deferred_client_wrapper_unittest.cc",
     "../browser/download/download_history_unittest.cc",
diff --git a/chrome/test/chromedriver/OWNERS b/chrome/test/chromedriver/OWNERS
index 2cd17ff1..1f40535 100644
--- a/chrome/test/chromedriver/OWNERS
+++ b/chrome/test/chromedriver/OWNERS
@@ -1,7 +1,7 @@
 # primary reviewer
-mathias@chromium.org
 nechaev@chromium.org
 sadym@chromium.org
 
 # secondary reviewer
+mathias@chromium.org
 johnchen@chromium.org
diff --git a/chrome/test/chromedriver/chrome/chrome_impl.cc b/chrome/test/chromedriver/chrome/chrome_impl.cc
index f84c76e..7c0ef44 100644
--- a/chrome/test/chromedriver/chrome/chrome_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_impl.cc
@@ -152,13 +152,13 @@
   params.SetStringKey("url", "about:blank");
   params.SetBoolKey("newWindow", type == WindowType::kWindow);
   params.SetBoolKey("background", true);
-  std::unique_ptr<base::DictionaryValue> result;
+  base::Value result;
   status = devtools_websocket_client_->SendCommandAndGetResult(
       "Target.createTarget", params, &result);
   if (status.IsError())
     return status;
 
-  const std::string* target_id_str = result->FindStringKey("targetId");
+  const std::string* target_id_str = result.FindStringKey("targetId");
   if (!target_id_str)
     return Status(kUnknownError, "no targetId from createTarget");
   *window_handle = *target_id_str;
@@ -173,13 +173,13 @@
 
   base::DictionaryValue params;
   params.SetStringKey("targetId", target_id);
-  std::unique_ptr<base::DictionaryValue> result;
+  base::Value result;
   status = devtools_websocket_client_->SendCommandAndGetResult(
       "Browser.getWindowForTarget", params, &result);
   if (status.IsError())
     return status;
 
-  return ParseWindow(std::move(result), window);
+  return ParseWindow(result, window);
 }
 
 Status ChromeImpl::GetWindowRect(const std::string& target_id,
@@ -272,13 +272,13 @@
 
   base::DictionaryValue params;
   params.SetIntKey("windowId", window_id);
-  std::unique_ptr<base::DictionaryValue> result;
+  base::Value result;
   status = devtools_websocket_client_->SendCommandAndGetResult(
       "Browser.getWindowBounds", params, &result);
   if (status.IsError())
     return status;
 
-  return ParseWindowBounds(std::move(result), window);
+  return ParseWindowBounds(result, window);
 }
 
 Status ChromeImpl::SetWindowBounds(
@@ -426,9 +426,8 @@
   return MakeFailedStatus(*desired_state, window->state);
 }
 
-Status ChromeImpl::ParseWindow(std::unique_ptr<base::DictionaryValue> params,
-                               Window* window) {
-  absl::optional<int> id = params->FindIntKey("windowId");
+Status ChromeImpl::ParseWindow(const base::Value& params, Window* window) {
+  absl::optional<int> id = params.FindIntKey("windowId");
   if (!id)
     return Status(kUnknownError, "no window id in response");
   window->id = *id;
@@ -436,10 +435,9 @@
   return ParseWindowBounds(std::move(params), window);
 }
 
-Status ChromeImpl::ParseWindowBounds(
-    std::unique_ptr<base::DictionaryValue> params,
-    Window* window) {
-  const base::Value* value = params->FindKey("bounds");
+Status ChromeImpl::ParseWindowBounds(const base::Value& params,
+                                     Window* window) {
+  const base::Value* value = params.FindKey("bounds");
   if (!value || !value->is_dict())
     return Status(kUnknownError, "no window bounds in response");
 
diff --git a/chrome/test/chromedriver/chrome/chrome_impl.h b/chrome/test/chromedriver/chrome/chrome_impl.h
index 2fd6624..834d3255 100644
--- a/chrome/test/chromedriver/chrome/chrome_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_impl.h
@@ -74,10 +74,8 @@
     int height;
   };
   virtual Status GetWindow(const std::string& target_id, Window* window);
-  Status ParseWindow(std::unique_ptr<base::DictionaryValue> params,
-                     Window* window);
-  Status ParseWindowBounds(std::unique_ptr<base::DictionaryValue> params,
-                           Window* window);
+  Status ParseWindow(const base::Value& params, Window* window);
+  Status ParseWindowBounds(const base::Value& params, Window* window);
   Status GetWindowBounds(int window_id, Window* window);
   Status SetWindowBounds(Window* window,
                          const std::string& target_id,
diff --git a/chrome/test/chromedriver/chrome/console_logger_unittest.cc b/chrome/test/chromedriver/chrome/console_logger_unittest.cc
index fe66b0c3..8d0c6ec 100644
--- a/chrome/test/chromedriver/chrome/console_logger_unittest.cc
+++ b/chrome/test/chromedriver/chrome/console_logger_unittest.cc
@@ -44,11 +44,11 @@
   // Overridden from DevToolsClient:
   Status ConnectIfNecessary() override { return listener_->OnConnected(this); }
 
-  Status SendCommandAndGetResult(
-      const std::string& method,
-      const base::DictionaryValue& params,
-      std::unique_ptr<base::DictionaryValue>* result) override {
+  Status SendCommandAndGetResult(const std::string& method,
+                                 const base::DictionaryValue& params,
+                                 base::Value* result) override {
     sent_command_queue_.push(method);
+    *result = base::Value(base::Value::Type::DICTIONARY);
     return Status(kOk);
   }
 
diff --git a/chrome/test/chromedriver/chrome/devtools_client.h b/chrome/test/chromedriver/chrome/devtools_client.h
index 14e996e..8b82821 100644
--- a/chrome/test/chromedriver/chrome/devtools_client.h
+++ b/chrome/test/chromedriver/chrome/devtools_client.h
@@ -11,6 +11,7 @@
 #include "base/callback_forward.h"
 
 namespace base {
+class Value;
 class DictionaryValue;
 }
 
@@ -53,16 +54,17 @@
       const std::string& method,
       const base::DictionaryValue& params) = 0;
 
-  virtual Status SendCommandAndGetResult(
-      const std::string& method,
-      const base::DictionaryValue& params,
-      std::unique_ptr<base::DictionaryValue>* result) = 0;
+  // A base::Value(base::Value::Type::DICTIONARY) gets assigned to |result|.
+  virtual Status SendCommandAndGetResult(const std::string& method,
+                                         const base::DictionaryValue& params,
+                                         base::Value* result) = 0;
 
+  // A base::Value(base::Value::Type::DICTIONARY) gets assigned to |result|.
   virtual Status SendCommandAndGetResultWithTimeout(
       const std::string& method,
       const base::DictionaryValue& params,
       const Timeout* timeout,
-      std::unique_ptr<base::DictionaryValue>* result) = 0;
+      base::Value* result) = 0;
 
   virtual Status SendCommandAndIgnoreResponse(
       const std::string& method,
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl.cc b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
index 9dd5509..cccf5758 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl.cc
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
@@ -245,21 +245,21 @@
     const std::string& method,
     const base::DictionaryValue& params,
     const Timeout* timeout) {
-  std::unique_ptr<base::DictionaryValue> result;
+  base::Value result;
   return SendCommandInternal(method, params, &result, true, true, 0, timeout);
 }
 
 Status DevToolsClientImpl::SendAsyncCommand(
     const std::string& method,
     const base::DictionaryValue& params) {
-  std::unique_ptr<base::DictionaryValue> result;
+  base::Value result;
   return SendCommandInternal(method, params, &result, false, false, 0, nullptr);
 }
 
 Status DevToolsClientImpl::SendCommandAndGetResult(
     const std::string& method,
     const base::DictionaryValue& params,
-    std::unique_ptr<base::DictionaryValue>* result) {
+    base::Value* result) {
   return SendCommandAndGetResultWithTimeout(method, params, nullptr, result);
 }
 
@@ -267,13 +267,13 @@
     const std::string& method,
     const base::DictionaryValue& params,
     const Timeout* timeout,
-    std::unique_ptr<base::DictionaryValue>* result) {
-  std::unique_ptr<base::DictionaryValue> intermediate_result;
+    base::Value* result) {
+  base::Value intermediate_result;
   Status status = SendCommandInternal(method, params, &intermediate_result,
                                       true, true, 0, timeout);
   if (status.IsError())
     return status;
-  if (!intermediate_result)
+  if (!intermediate_result.is_dict())
     return Status(kUnknownError, "inspector response missing result");
   *result = std::move(intermediate_result);
   return Status(kOk);
@@ -355,7 +355,7 @@
 Status DevToolsClientImpl::SendCommandInternal(
     const std::string& method,
     const base::DictionaryValue& params,
-    std::unique_ptr<base::DictionaryValue>* result,
+    base::Value* result,
     bool expect_response,
     bool wait_for_response,
     const int client_command_id,
@@ -422,10 +422,12 @@
       if (!response.result) {
         return internal::ParseInspectorError(response.error);
       }
-      *result = std::move(response.result);
+      *result = std::move(*response.result);
     }
   } else {
     CHECK(!wait_for_response);
+    if (result)
+      *result = base::Value(base::Value::Type::DICTIONARY);
   }
   return Status(kOk);
 }
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl.h b/chrome/test/chromedriver/chrome/devtools_client_impl.h
index 2049f70a..1e6193d 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl.h
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl.h
@@ -105,15 +105,13 @@
   Status SendAsyncCommand(
       const std::string& method,
       const base::DictionaryValue& params) override;
-  Status SendCommandAndGetResult(
-      const std::string& method,
-      const base::DictionaryValue& params,
-      std::unique_ptr<base::DictionaryValue>* result) override;
-  Status SendCommandAndGetResultWithTimeout(
-      const std::string& method,
-      const base::DictionaryValue& params,
-      const Timeout* timeout,
-      std::unique_ptr<base::DictionaryValue>* result) override;
+  Status SendCommandAndGetResult(const std::string& method,
+                                 const base::DictionaryValue& params,
+                                 base::Value* result) override;
+  Status SendCommandAndGetResultWithTimeout(const std::string& method,
+                                            const base::DictionaryValue& params,
+                                            const Timeout* timeout,
+                                            base::Value* result) override;
   Status SendCommandAndIgnoreResponse(
       const std::string& method,
       const base::DictionaryValue& params) override;
@@ -153,7 +151,7 @@
   };
   Status SendCommandInternal(const std::string& method,
                              const base::DictionaryValue& params,
-                             std::unique_ptr<base::DictionaryValue>* result,
+                             base::Value* result,
                              bool expect_response,
                              bool wait_for_response,
                              int client_command_id,
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc b/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc
index b4aaeabf..e9b9667 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc
@@ -173,11 +173,11 @@
   ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
   base::DictionaryValue params;
   params.SetInteger("param", 1);
-  std::unique_ptr<base::DictionaryValue> result;
+  base::Value result;
   Status status = client.SendCommandAndGetResult("method", params, &result);
   ASSERT_EQ(kOk, status.code());
   std::string json;
-  base::JSONWriter::Write(*result, &json);
+  base::JSONWriter::Write(result, &json);
   ASSERT_STREQ("{\"param\":1}", json.c_str());
 }
 
@@ -543,12 +543,12 @@
   client.SetParserFuncForTesting(
       base::BindRepeating(&ReturnEventThenResponse, &first));
   base::DictionaryValue params;
-  std::unique_ptr<base::DictionaryValue> result;
+  base::Value result;
   ASSERT_TRUE(client.SendCommandAndGetResult("method", params, &result).IsOk());
-  ASSERT_TRUE(result);
-  int key;
-  ASSERT_TRUE(result->GetInteger("key", &key));
-  ASSERT_EQ(2, key);
+  ASSERT_TRUE(result.is_dict());
+  absl::optional<int> key = result.FindIntKey("key");
+  ASSERT_TRUE(key);
+  ASSERT_EQ(2, key.value());
 }
 
 TEST(ParseInspectorMessage, NonJson) {
@@ -779,12 +779,12 @@
       base::BindRepeating(&ReturnOutOfOrderResponses, &recurse_count, &client));
   base::DictionaryValue params;
   params.SetInteger("param", 1);
-  std::unique_ptr<base::DictionaryValue> result;
+  base::Value result;
   ASSERT_TRUE(client.SendCommandAndGetResult("method", params, &result).IsOk());
-  ASSERT_TRUE(result);
-  int key;
-  ASSERT_TRUE(result->GetInteger("key", &key));
-  ASSERT_EQ(2, key);
+  ASSERT_TRUE(result.is_dict());
+  absl::optional<int> key = result.FindIntKey("key");
+  ASSERT_TRUE(key);
+  ASSERT_EQ(2, key.value());
 }
 
 namespace {
diff --git a/chrome/test/chromedriver/chrome/dom_tracker.cc b/chrome/test/chromedriver/chrome/dom_tracker.cc
index 06967bd..bcacb8f 100644
--- a/chrome/test/chromedriver/chrome/dom_tracker.cc
+++ b/chrome/test/chromedriver/chrome/dom_tracker.cc
@@ -66,7 +66,7 @@
 
     base::DictionaryValue params;
     params.SetString("frameId", *frame_id);
-    std::unique_ptr<base::DictionaryValue> result;
+    base::Value result;
     auto status =
         client->SendCommandAndGetResult("DOM.getFrameOwner", params, &result);
     if (status.IsError()) {
@@ -76,7 +76,7 @@
       }
       return status;
     }
-    auto ownder_node_id = result->FindIntKey("nodeId");
+    auto ownder_node_id = result.FindIntKey("nodeId");
     if (ownder_node_id.has_value()) {
       node_to_frame_map_.emplace(ownder_node_id.value(), *frame_id);
     } else {
@@ -127,7 +127,7 @@
   node_to_frame_map_.clear();
   base::DictionaryValue params;
   params.SetInteger("depth", -1);
-  std::unique_ptr<base::DictionaryValue> result;
+  base::Value result;
   // Fetch the root document and traverse it populating node_to_frame_map_.
   // The map will be updated later whenever Inspector pushes DOM node
   // information to the client.
@@ -137,7 +137,7 @@
     return status;
   }
 
-  if (const base::Value* root = result->FindKey("root")) {
+  if (const base::Value* root = result.FindKey("root")) {
     ProcessNode(*root);
   } else {
     status =
diff --git a/chrome/test/chromedriver/chrome/dom_tracker_unittest.cc b/chrome/test/chromedriver/chrome/dom_tracker_unittest.cc
index 0fc73ccd..dda638c 100644
--- a/chrome/test/chromedriver/chrome/dom_tracker_unittest.cc
+++ b/chrome/test/chromedriver/chrome/dom_tracker_unittest.cc
@@ -28,9 +28,7 @@
 
   MOCK_METHOD(Status,
               SendCommandAndGetResult,
-              (const std::string&,
-               const base::DictionaryValue&,
-               std::unique_ptr<base::DictionaryValue>*),
+              (const std::string&, const base::DictionaryValue&, base::Value*),
               (override));
 };
 
@@ -59,11 +57,9 @@
   using ::testing::_;
   EXPECT_CALL(client, SendCommandAndGetResult("DOM.getDocument", _, _))
       .WillOnce([](const std::string& method,
-                   const base::DictionaryValue& params,
-                   std::unique_ptr<base::DictionaryValue>* result) {
-        *result = std::make_unique<base::DictionaryValue>();
-        result->get()->SetDictionary("root",
-                                     std::make_unique<base::DictionaryValue>());
+                   const base::DictionaryValue& params, base::Value* result) {
+        *result = base::Value(base::Value::Type::DICTIONARY);
+        result->SetKey("root", base::Value(base::Value::Type::DICTIONARY));
         return Status(kOk);
       });
 
diff --git a/chrome/test/chromedriver/chrome/javascript_dialog_manager_unittest.cc b/chrome/test/chromedriver/chrome/javascript_dialog_manager_unittest.cc
index 2e176f6..672a72e 100644
--- a/chrome/test/chromedriver/chrome/javascript_dialog_manager_unittest.cc
+++ b/chrome/test/chromedriver/chrome/javascript_dialog_manager_unittest.cc
@@ -95,10 +95,9 @@
   }
 
   // Overridden from StubDevToolsClient:
-  Status SendCommandAndGetResult(
-      const std::string& method,
-      const base::DictionaryValue& params,
-      std::unique_ptr<base::DictionaryValue>* result) override {
+  Status SendCommandAndGetResult(const std::string& method,
+                                 const base::DictionaryValue& params,
+                                 base::Value* result) override {
     while (closing_count_ > 0) {
       base::DictionaryValue empty;
       Status status =
@@ -107,6 +106,7 @@
         return status;
       closing_count_--;
     }
+    *result = base::Value(base::Value::Type::DICTIONARY);
     return Status(kOk);
   }
   void AddListener(DevToolsEventListener* listener) override {
diff --git a/chrome/test/chromedriver/chrome/navigation_tracker.cc b/chrome/test/chromedriver/chrome/navigation_tracker.cc
index 2829d8f8..5ee9e18a3 100644
--- a/chrome/test/chromedriver/chrome/navigation_tracker.cc
+++ b/chrome/test/chromedriver/chrome/navigation_tracker.cc
@@ -120,10 +120,9 @@
   // (see crbug.com/524079).
   base::DictionaryValue params;
   params.SetString("expression", "1");
-  std::unique_ptr<base::DictionaryValue> result;
+  base::Value result;
   Status status = client_->SendCommandAndGetResultWithTimeout(
       "Runtime.evaluate", params, timeout, &result);
-  int value = 0;
   if (status.code() == kDisconnected) {
     // If we receive a kDisconnected status code from Runtime.evaluate, don't
     // wait for pending navigations to complete, since we won't see any more
@@ -139,8 +138,8 @@
              status.message().find(kTargetClosedMessage) != std::string::npos) {
     *is_pending = true;
     return Status(kOk);
-  } else if (status.IsError() || !result->GetInteger("result.value", &value) ||
-             value != 1) {
+  } else if (status.IsError() ||
+             result.FindIntPath("result.value").value_or(0) != 1) {
     return MakeNavigationCheckFailedStatus(status);
   }
 
@@ -154,10 +153,11 @@
     base::DictionaryValue empty_params;
     status = client_->SendCommandAndGetResultWithTimeout(
         "DOM.getDocument", empty_params, timeout, &result);
-    std::string base_url;
-    std::string doc_url;
-    if (status.IsError() || !result->GetString("root.baseURL", &base_url) ||
-        !result->GetString("root.documentURL", &doc_url))
+    if (status.IsError())
+      return MakeNavigationCheckFailedStatus(status);
+    std::string* base_url = result.FindStringPath("root.baseURL");
+    std::string* doc_url = result.FindStringPath("root.documentURL");
+    if (!base_url || !doc_url)
       return MakeNavigationCheckFailedStatus(status);
 
     // Need to check current frame valid again to avoid accessing invalid
@@ -168,7 +168,7 @@
       return Status(kOk);
     }
 
-    if (doc_url != "about:blank" && base_url == "about:blank") {
+    if (*doc_url != "about:blank" && *base_url == "about:blank") {
       *is_pending = true;
       *loading_state_ = kLoading;
       return Status(kOk);
@@ -188,13 +188,15 @@
                                               bool* exists) {
   base::DictionaryValue params;
   params.SetString("expression", "typeof(getWindowInfo)");
-  std::unique_ptr<base::DictionaryValue> result;
+  base::Value result;
   Status status = client_->SendCommandAndGetResultWithTimeout(
       "Runtime.evaluate", params, timeout, &result);
-  std::string type;
-  if (status.IsError() || !result->GetString("result.value", &type))
+  if (status.IsError())
     return MakeNavigationCheckFailedStatus(status);
-  *exists = type == "function";
+  std::string* type = result.FindStringPath("result.value");
+  if (!type)
+    return MakeNavigationCheckFailedStatus(status);
+  *exists = *type == "function";
   return Status(kOk);
 }
 
@@ -363,7 +365,7 @@
     *loading_state_ = kUnknown;
     base::DictionaryValue params;
     params.SetString("expression", "document.URL");
-    std::unique_ptr<base::DictionaryValue> result_dict;
+    base::Value result_dict;
     Status status(kOk);
     for (int attempt = 0; attempt < 3; attempt++) {
       status = client_->SendCommandAndGetResultWithTimeout(
@@ -376,10 +378,12 @@
       }
     }
 
-    std::string url;
-    if (status.IsError() || !result_dict->GetString("result.value", &url))
+    if (status.IsError())
       return MakeNavigationCheckFailedStatus(status);
-    if (loadingState() == kUnknown && url.empty())
+    std::string* url = result_dict.FindStringPath("result.value");
+    if (!url)
+      return MakeNavigationCheckFailedStatus(status);
+    if (loadingState() == kUnknown && url->empty())
       *loading_state_ = kLoading;
   }
   return Status(kOk);
diff --git a/chrome/test/chromedriver/chrome/navigation_tracker_unittest.cc b/chrome/test/chromedriver/chrome/navigation_tracker_unittest.cc
index fd21c84b..0aa6423 100644
--- a/chrome/test/chromedriver/chrome/navigation_tracker_unittest.cc
+++ b/chrome/test/chromedriver/chrome/navigation_tracker_unittest.cc
@@ -42,32 +42,29 @@
 
   ~DeterminingLoadStateDevToolsClient() override {}
 
-  Status SendCommandAndGetResult(
-      const std::string& method,
-      const base::DictionaryValue& params,
-      std::unique_ptr<base::DictionaryValue>* result) override {
+  Status SendCommandAndGetResult(const std::string& method,
+                                 const base::DictionaryValue& params,
+                                 base::Value* result) override {
     if (method == "DOM.getDocument") {
-      base::DictionaryValue result_dict;
+      base::Value result_dict(base::Value::Type::DICTIONARY);
       if (has_empty_base_url_) {
-        result_dict.SetString("root.baseURL", "about:blank");
-        result_dict.SetString("root.documentURL", "http://test");
+        result_dict.SetStringPath("root.baseURL", "about:blank");
+        result_dict.SetStringPath("root.documentURL", "http://test");
       } else {
-        result_dict.SetString("root.baseURL", "http://test");
-        result_dict.SetString("root.documentURL", "http://test");
+        result_dict.SetStringPath("root.baseURL", "http://test");
+        result_dict.SetStringPath("root.documentURL", "http://test");
       }
-      *result = base::DictionaryValue::From(
-          base::Value::ToUniquePtrValue(result_dict.Clone()));
+      *result = std::move(result_dict);
       return Status(kOk);
     } else if (method == "Runtime.evaluate") {
       std::string expression;
       if (params.GetString("expression", &expression)) {
-        base::DictionaryValue result_dict;
+        base::Value result_dict(base::Value::Type::DICTIONARY);
         if (expression == "1")
-          result_dict.SetInteger("result.value", 1);
+          result_dict.SetIntPath("result.value", 1);
         else if (expression == "document.readyState")
-          result_dict.SetString("result.value", "loading");
-        *result = base::DictionaryValue::From(
-            base::Value::ToUniquePtrValue(result_dict.Clone()));
+          result_dict.SetStringPath("result.value", "loading");
+        *result = std::move(result_dict);
         return Status(kOk);
       }
     }
@@ -81,10 +78,9 @@
       }
     }
 
-    base::DictionaryValue result_dict;
-    result_dict.SetBoolean("result.value", is_loading_);
-    *result = base::DictionaryValue::From(
-        base::Value::ToUniquePtrValue(result_dict.Clone()));
+    base::Value result_dict(base::Value::Type::DICTIONARY);
+    result_dict.SetBoolPath("result.value", is_loading_);
+    *result = std::move(result_dict);
     return Status(kOk);
   }
 
@@ -399,19 +395,18 @@
 
   ~FailToEvalScriptDevToolsClient() override {}
 
-  Status SendCommandAndGetResult(
-      const std::string& method,
-      const base::DictionaryValue& params,
-      std::unique_ptr<base::DictionaryValue>* result) override {
+  Status SendCommandAndGetResult(const std::string& method,
+                                 const base::DictionaryValue& params,
+                                 base::Value* result) override {
     if (!is_dom_getDocument_requested_ && method == "DOM.getDocument") {
       is_dom_getDocument_requested_ = true;
-      base::DictionaryValue result_dict;
-      result_dict.SetString("root.baseURL", "http://test");
-      *result = base::DictionaryValue::From(
-          base::Value::ToUniquePtrValue(result_dict.Clone()));
+      base::Value result_dict(base::Value::Type::DICTIONARY);
+      result_dict.SetStringPath("root.baseURL", "http://test");
+      *result = std::move(result_dict);
       return Status(kOk);
     }
     EXPECT_STREQ("Runtime.evaluate", method.c_str());
+    *result = base::Value(base::Value::Type::DICTIONARY);
     return Status(kUnknownError, "failed to eval script");
   }
 
@@ -584,10 +579,10 @@
 
   ~TargetClosedDevToolsClient() override {}
 
-  Status SendCommandAndGetResult(
-      const std::string& method,
-      const base::DictionaryValue& params,
-      std::unique_ptr<base::DictionaryValue>* result) override {
+  Status SendCommandAndGetResult(const std::string& method,
+                                 const base::DictionaryValue& params,
+                                 base::Value* result) override {
+    *result = base::Value(base::Value::Type::DICTIONARY);
     return Status(kUnknownError, "Inspected target navigated or closed");
   }
 };
diff --git a/chrome/test/chromedriver/chrome/network_conditions_override_manager.cc b/chrome/test/chromedriver/chrome/network_conditions_override_manager.cc
index 22c9256..37d004a 100644
--- a/chrome/test/chromedriver/chrome/network_conditions_override_manager.cc
+++ b/chrome/test/chromedriver/chrome/network_conditions_override_manager.cc
@@ -61,11 +61,10 @@
   if (status.IsError())
     return status;
 
-  std::unique_ptr<base::DictionaryValue> result;
+  base::Value result;
   status = client_->SendCommandAndGetResult(
       "Network.canEmulateNetworkConditions", empty_params, &result);
-  absl::optional<bool> can =
-      result ? result->FindBoolKey("result") : absl::nullopt;
+  absl::optional<bool> can = result.FindBoolKey("result");
   if (status.IsError() || !can)
     return Status(kUnknownError,
         "unable to detect if chrome can emulate network conditions", status);
diff --git a/chrome/test/chromedriver/chrome/recorder_devtools_client.cc b/chrome/test/chromedriver/chrome/recorder_devtools_client.cc
index e378e31..4d3d3f89 100644
--- a/chrome/test/chromedriver/chrome/recorder_devtools_client.cc
+++ b/chrome/test/chromedriver/chrome/recorder_devtools_client.cc
@@ -15,13 +15,13 @@
 Status RecorderDevToolsClient::SendCommandAndGetResult(
     const std::string& method,
     const base::DictionaryValue& params,
-    std::unique_ptr<base::DictionaryValue>* result) {
-  commands_.push_back(Command(method, params));
+    base::Value* result) {
+  commands_.emplace_back(method, params);
 
   // For any tests that directly call SendCommandAndGetResults, we'll just
   // always return { "result": true }. Currently only used when testing
   // "canEmulateNetworkConditions".
-  (*result) = std::make_unique<base::DictionaryValue>();
-  (*result)->SetBoolean("result", true);
+  *result = base::Value(base::Value::Type::DICTIONARY);
+  result->SetBoolKey("result", true);
   return Status(kOk);
 }
diff --git a/chrome/test/chromedriver/chrome/recorder_devtools_client.h b/chrome/test/chromedriver/chrome/recorder_devtools_client.h
index 38e7c3d9..779ab198 100644
--- a/chrome/test/chromedriver/chrome/recorder_devtools_client.h
+++ b/chrome/test/chromedriver/chrome/recorder_devtools_client.h
@@ -10,10 +10,6 @@
 #include "base/values.h"
 #include "chrome/test/chromedriver/chrome/stub_devtools_client.h"
 
-namespace base {
-class DictionaryValue;
-}
-
 class Status;
 
 struct Command {
@@ -43,10 +39,9 @@
   ~RecorderDevToolsClient() override;
 
   // Overridden from StubDevToolsClient:
-  Status SendCommandAndGetResult(
-      const std::string& method,
-      const base::DictionaryValue& params,
-      std::unique_ptr<base::DictionaryValue>* result) override;
+  Status SendCommandAndGetResult(const std::string& method,
+                                 const base::DictionaryValue& params,
+                                 base::Value* result) override;
 
   std::vector<Command> commands_;
 };
diff --git a/chrome/test/chromedriver/chrome/stub_devtools_client.cc b/chrome/test/chromedriver/chrome/stub_devtools_client.cc
index 9621272..c0f0d15 100644
--- a/chrome/test/chromedriver/chrome/stub_devtools_client.cc
+++ b/chrome/test/chromedriver/chrome/stub_devtools_client.cc
@@ -34,7 +34,7 @@
 Status StubDevToolsClient::SendCommand(
     const std::string& method,
     const base::DictionaryValue& params) {
-  std::unique_ptr<base::DictionaryValue> result;
+  base::Value result;
   return SendCommandAndGetResult(method, params, &result);
 }
 
@@ -61,8 +61,8 @@
 Status StubDevToolsClient::SendCommandAndGetResult(
     const std::string& method,
     const base::DictionaryValue& params,
-    std::unique_ptr<base::DictionaryValue>* result) {
-  *result = std::make_unique<base::DictionaryValue>();
+    base::Value* result) {
+  *result = base::Value(base::Value::Type::DICTIONARY);
   return Status(kOk);
 }
 
@@ -70,7 +70,7 @@
     const std::string& method,
     const base::DictionaryValue& params,
     const Timeout* timeout,
-    std::unique_ptr<base::DictionaryValue>* result) {
+    base::Value* result) {
   return SendCommandAndGetResult(method, params, result);
 }
 
diff --git a/chrome/test/chromedriver/chrome/stub_devtools_client.h b/chrome/test/chromedriver/chrome/stub_devtools_client.h
index e6e79aa1..c5daffc 100644
--- a/chrome/test/chromedriver/chrome/stub_devtools_client.h
+++ b/chrome/test/chromedriver/chrome/stub_devtools_client.h
@@ -12,6 +12,7 @@
 #include "chrome/test/chromedriver/chrome/devtools_client.h"
 
 namespace base {
+class Value;
 class DictionaryValue;
 }
 
@@ -41,15 +42,13 @@
   Status SendAsyncCommand(
       const std::string& method,
       const base::DictionaryValue& params) override;
-  Status SendCommandAndGetResult(
-      const std::string& method,
-      const base::DictionaryValue& params,
-      std::unique_ptr<base::DictionaryValue>* result) override;
-  Status SendCommandAndGetResultWithTimeout(
-      const std::string& method,
-      const base::DictionaryValue& params,
-      const Timeout* timeout,
-      std::unique_ptr<base::DictionaryValue>* result) override;
+  Status SendCommandAndGetResult(const std::string& method,
+                                 const base::DictionaryValue& params,
+                                 base::Value* result) override;
+  Status SendCommandAndGetResultWithTimeout(const std::string& method,
+                                            const base::DictionaryValue& params,
+                                            const Timeout* timeout,
+                                            base::Value* result) override;
   Status SendCommandAndIgnoreResponse(
       const std::string& method,
       const base::DictionaryValue& params) override;
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index 389374981..76e1dd19 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -278,16 +278,16 @@
 
 Status WebViewImpl::GetUrl(std::string* url) {
   base::DictionaryValue params;
-  std::unique_ptr<base::DictionaryValue> result;
-  Status status = client_->SendCommandAndGetResult(
-      "Page.getNavigationHistory", params, &result);
+  base::DictionaryValue result;
+  Status status = client_->SendCommandAndGetResult("Page.getNavigationHistory",
+                                                   params, &result);
   if (status.IsError())
     return status;
   int current_index = 0;
-  if (!result->GetInteger("currentIndex", &current_index))
+  if (!result.GetInteger("currentIndex", &current_index))
     return Status(kUnknownError, "navigation history missing currentIndex");
   base::ListValue* entries = nullptr;
-  if (!result->GetList("entries", &entries))
+  if (!result.GetList("entries", &entries))
     return Status(kUnknownError, "navigation history missing entries");
   base::DictionaryValue* entry = nullptr;
   if (!entries->GetDictionary(current_index, &entry))
@@ -352,8 +352,9 @@
         const std::string& cmd,
         const base::DictionaryValue& params,
         std::unique_ptr<base::Value>* value) {
-  std::unique_ptr<base::DictionaryValue> result;
-  Status status = client_->SendCommandAndGetResult(cmd, params, &result);
+  std::unique_ptr<base::DictionaryValue> result =
+      std::make_unique<base::DictionaryValue>();
+  Status status = client_->SendCommandAndGetResult(cmd, params, result.get());
   if (status.IsError())
     return status;
   *value = std::move(result);
@@ -362,9 +363,9 @@
 
 Status WebViewImpl::TraverseHistory(int delta, const Timeout* timeout) {
   base::DictionaryValue params;
-  std::unique_ptr<base::DictionaryValue> result;
-  Status status = client_->SendCommandAndGetResult(
-      "Page.getNavigationHistory", params, &result);
+  base::DictionaryValue result;
+  Status status = client_->SendCommandAndGetResult("Page.getNavigationHistory",
+                                                   params, &result);
   if (status.IsError()) {
     // TODO(samuong): remove this once we stop supporting WebView on KitKat.
     // Older versions of WebView on Android (on KitKat and earlier) do not have
@@ -379,11 +380,11 @@
   }
 
   int current_index;
-  if (!result->GetInteger("currentIndex", &current_index))
+  if (!result.GetInteger("currentIndex", &current_index))
     return Status(kUnknownError, "DevTools didn't return currentIndex");
 
   base::ListValue* entries;
-  if (!result->GetList("entries", &entries))
+  if (!result.GetList("entries", &entries))
     return Status(kUnknownError, "DevTools didn't return entries");
 
   base::DictionaryValue* entry;
@@ -804,7 +805,7 @@
 Status WebViewImpl::GetCookies(std::unique_ptr<base::ListValue>* cookies,
                                const std::string& current_page_url) {
   base::DictionaryValue params;
-  std::unique_ptr<base::DictionaryValue> result;
+  base::DictionaryValue result;
 
   if (browser_info_->browser_name != "webview") {
     base::ListValue url_list;
@@ -822,7 +823,7 @@
   }
 
   base::ListValue* cookies_tmp;
-  if (!result->GetList("cookies", &cookies_tmp))
+  if (!result.GetList("cookies", &cookies_tmp))
     return Status(kUnknownError, "DevTools didn't return cookies");
   *cookies = cookies_tmp->CreateDeepCopy();
   return Status(kOk);
@@ -864,12 +865,12 @@
   if (expiry >= 0)
     params.SetDoubleKey("expires", expiry);
 
-  std::unique_ptr<base::DictionaryValue> result;
+  base::DictionaryValue result;
   Status status =
       client_->SendCommandAndGetResult("Network.setCookie", params, &result);
   if (status.IsError())
     return Status(kUnableToSetCookie);
-  if (!result->FindBoolKey("success").value_or(false))
+  if (!result.FindBoolKey("success").value_or(false))
     return Status(kUnableToSetCookie);
   return Status(kOk);
 }
@@ -944,13 +945,13 @@
 Status WebViewImpl::CaptureScreenshot(
     std::string* screenshot,
     const base::DictionaryValue& params) {
-  std::unique_ptr<base::DictionaryValue> result;
+  base::DictionaryValue result;
   Timeout timeout(base::Seconds(10));
   Status status = client_->SendCommandAndGetResultWithTimeout(
       "Page.captureScreenshot", params, &timeout, &result);
   if (status.IsError())
     return status;
-  if (!result->GetString("data", screenshot))
+  if (!result.GetString("data", screenshot))
     return Status(kUnknownError, "expected string 'data' in response");
   return Status(kOk);
 }
@@ -962,7 +963,7 @@
     return Status(kUnknownError,
                   "PrintToPDF is only supported in headless mode");
   }
-  std::unique_ptr<base::DictionaryValue> result;
+  base::DictionaryValue result;
   Timeout timeout(base::Seconds(10));
   Status status = client_->SendCommandAndGetResultWithTimeout(
       "Page.printToPDF", params, &timeout, &result);
@@ -972,7 +973,7 @@
     }
     return status;
   }
-  if (!result->GetString("data", pdf))
+  if (!result.GetString("data", pdf))
     return Status(kUnknownError, "expected string 'data' in response");
   return Status(kOk);
 }
@@ -1023,21 +1024,21 @@
     // Convert the node_id to a Runtime.RemoteObject
     std::string inputRemoteObjectId;
     {
-      std::unique_ptr<base::DictionaryValue> cmd_result;
+      base::DictionaryValue cmd_result;
       base::DictionaryValue params;
       params.SetInteger("nodeId", node_id);
       status = client_->SendCommandAndGetResult("DOM.resolveNode", params,
                                                 &cmd_result);
       if (status.IsError())
         return status;
-      if (!cmd_result->GetString("object.objectId", &inputRemoteObjectId))
+      if (!cmd_result.GetString("object.objectId", &inputRemoteObjectId))
         return Status(kUnknownError, "DevTools didn't return objectId");
     }
 
     // figure out how many files there are
     int numberOfFiles = 0;
     {
-      std::unique_ptr<base::DictionaryValue> cmd_result;
+      base::DictionaryValue cmd_result;
       base::DictionaryValue params;
       params.SetString("functionDeclaration",
                        "function() { return this.files.length }");
@@ -1046,7 +1047,7 @@
                                                 params, &cmd_result);
       if (status.IsError())
         return status;
-      if (!cmd_result->GetInteger("result.value", &numberOfFiles))
+      if (!cmd_result.GetInteger("result.value", &numberOfFiles))
         return Status(kUnknownError, "DevTools didn't return value");
     }
 
@@ -1054,7 +1055,7 @@
     for (int i = 0; i < numberOfFiles; i++) {
       std::string fileObjectId;
       {
-        std::unique_ptr<base::DictionaryValue> cmd_result;
+        base::DictionaryValue cmd_result;
         base::DictionaryValue params;
         params.SetString(
             "functionDeclaration",
@@ -1065,7 +1066,7 @@
                                                   params, &cmd_result);
         if (status.IsError())
           return status;
-        if (!cmd_result->GetString("result.objectId", &fileObjectId))
+        if (!cmd_result.GetString("result.objectId", &fileObjectId))
           return Status(kUnknownError, "DevTools didn't return objectId");
       }
 
@@ -1073,14 +1074,14 @@
       {
         base::DictionaryValue params;
         params.SetString("objectId", fileObjectId);
-        std::unique_ptr<base::DictionaryValue> getFileInfoResult;
+        base::DictionaryValue getFileInfoResult;
         status = client_->SendCommandAndGetResult("DOM.getFileInfo", params,
                                                   &getFileInfoResult);
         if (status.IsError())
           return status;
         // Add the full path to the file_list
         std::string fullPath;
-        if (!getFileInfoResult->GetString("path", &fullPath))
+        if (!getFileInfoResult.GetString("path", &fullPath))
           return Status(kUnknownError, "DevTools didn't return path");
         file_list.Append(fullPath);
       }
@@ -1142,10 +1143,11 @@
 
 Status WebViewImpl::EndProfile(std::unique_ptr<base::Value>* profile_data) {
   base::DictionaryValue params;
-  std::unique_ptr<base::DictionaryValue> profile_result;
+  std::unique_ptr<base::DictionaryValue> profile_result =
+      std::make_unique<base::DictionaryValue>();
 
-  Status status = client_->SendCommandAndGetResult(
-      "Profiler.stop", params, &profile_result);
+  Status status = client_->SendCommandAndGetResult("Profiler.stop", params,
+                                                   profile_result.get());
 
   if (status.IsError()) {
     Status disable_profile_status = StopProfileInternal();
@@ -1390,7 +1392,7 @@
     params.SetInteger("contextId", context_id);
   params.SetBoolean("returnByValue", return_type == ReturnByValue);
   params.SetBoolean("awaitPromise", awaitPromise);
-  std::unique_ptr<base::DictionaryValue> cmd_result;
+  base::DictionaryValue cmd_result;
 
   Timeout local_timeout(timeout);
   Status status = client->SendCommandAndGetResultWithTimeout(
@@ -1398,15 +1400,15 @@
   if (status.IsError())
     return status;
 
-  if (cmd_result->HasKey("exceptionDetails")) {
+  if (cmd_result.HasKey("exceptionDetails")) {
     std::string description = "unknown";
-    cmd_result->GetString("result.description", &description);
+    cmd_result.GetString("result.description", &description);
     return Status(kUnknownError,
                   "Runtime.evaluate threw exception: " + description);
   }
 
   base::DictionaryValue* unscoped_result;
-  if (!cmd_result->GetDictionary("result", &unscoped_result))
+  if (!cmd_result.GetDictionary("result", &unscoped_result))
     return Status(kUnknownError, "evaluate missing dictionary 'result'");
   result->reset(unscoped_result->DeepCopy());
   return Status(kOk);
@@ -1515,12 +1517,12 @@
     return Status(kOk);
   }
 
-  std::unique_ptr<base::DictionaryValue> cmd_result;
+  base::DictionaryValue cmd_result;
   {
     base::DictionaryValue params;
     params.SetString("objectId", element_id);
-    status = client->SendCommandAndGetResult(
-        "DOM.requestNode", params, &cmd_result);
+    status =
+        client->SendCommandAndGetResult("DOM.requestNode", params, &cmd_result);
   }
   {
     // Release the remote object before doing anything else.
@@ -1536,7 +1538,7 @@
   if (status.IsError())
     return status;
 
-  if (!cmd_result->GetInteger("nodeId", node_id))
+  if (!cmd_result.GetInteger("nodeId", node_id))
     return Status(kUnknownError, "DOM.requestNode missing int 'nodeId'");
   *found_node = true;
   return Status(kOk);
diff --git a/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc b/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
index 6c30896..d73ad9f 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
@@ -38,13 +38,12 @@
   }
 
   // Overridden from DevToolsClient:
-  Status SendCommandAndGetResult(
-      const std::string& method,
-      const base::DictionaryValue& params,
-      std::unique_ptr<base::DictionaryValue>* result) override {
+  Status SendCommandAndGetResult(const std::string& method,
+                                 const base::DictionaryValue& params,
+                                 base::Value* result) override {
     if (status_.IsError())
       return status_;
-    result->reset(result_.DeepCopy());
+    *result = result_.Clone();
     return Status(kOk);
   }
 
diff --git a/chrome/test/chromedriver/performance_logger_unittest.cc b/chrome/test/chromedriver/performance_logger_unittest.cc
index 5a1a49b..1cf190f 100644
--- a/chrome/test/chromedriver/performance_logger_unittest.cc
+++ b/chrome/test/chromedriver/performance_logger_unittest.cc
@@ -64,12 +64,12 @@
   // Overridden from DevToolsClient:
   Status ConnectIfNecessary() override { return listener_->OnConnected(this); }
 
-  Status SendCommandAndGetResult(
-      const std::string& method,
-      const base::DictionaryValue& params,
-      std::unique_ptr<base::DictionaryValue>* result) override {
+  Status SendCommandAndGetResult(const std::string& method,
+                                 const base::DictionaryValue& params,
+                                 base::Value* result) override {
     sent_commands_.push_back(
         std::make_unique<DevToolsCommand>(method, params.DeepCopy()));
+    *result = base::Value(base::Value::Type::DICTIONARY);
     return Status(kOk);
   }
 
diff --git a/chrome/test/data/cart/coupons/fl_code_discounts.json b/chrome/test/data/cart/coupons/fl_code_discounts.json
index 81798d1..e3d3d2f 100644
--- a/chrome/test/data/cart/coupons/fl_code_discounts.json
+++ b/chrome/test/data/cart/coupons/fl_code_discounts.json
@@ -1,8 +1,7 @@
 {
   "discounts": [
-    // Use case for:
-    //  * FetchFLCodeDiscountWorkerBrowserTest.TwoCartsOneWithDiscountOneWithoutDiscount
     {
+      "_comment": "Use case for: FetchFLCodeDiscountWorkerBrowserTest.TwoCartsOneWithDiscountOneWithoutDiscount",
       "merchantIdentifier": {
         "cartUrl": "https://www.merchant1.com/cart",
         "merchantId": "10046"
@@ -24,9 +23,8 @@
         "languageCode": "en-US"
       }
     },
-    // Use case for:
-    //  * FetchFLCodeDiscountWorkerBrowserTest.SimplePercentDiscountWithCodeTest
     {
+      "_comment": "Use case for: FetchFLCodeDiscountWorkerBrowserTest.SimplePercentDiscountWithCodeTest",
       "merchantIdentifier": {
         "cartUrl": "https://www.merchant2.com/cart",
         "merchantId": "10047"
@@ -48,6 +46,6 @@
         "languageCode": "en-US"
       }
     }
-    // cartUrl needs to be unique. Next cartUrl is https://www.merchant3.com/cart.
-  ]
+  ],
+  "_comment": "cartUrl needs to be unique. Next cartUrl is https://www.merchant3.com/cart."
 }
diff --git a/chrome/test/data/cart/coupons/fl_codeless_discounts.json b/chrome/test/data/cart/coupons/fl_codeless_discounts.json
index 87b61d1..48c9c2d 100644
--- a/chrome/test/data/cart/coupons/fl_codeless_discounts.json
+++ b/chrome/test/data/cart/coupons/fl_codeless_discounts.json
@@ -1,9 +1,11 @@
 {
   "discounts": [
-    // Use case for:
-    //  * FetchFLCodelessDiscountWorkerBrowserTest.SimplePercentOffTest
-    //  * FetchFLCodelessDiscountWorkerBrowserTest.TwoCartsOneWithDiscountOneWithoutDiscount
     {
+      "_comment": [
+        "Use case for:",
+        "FetchFLCodelessDiscountWorkerBrowserTest.SimplePercentOffTest",
+        "FetchFLCodelessDiscountWorkerBrowserTest.TwoCartsOneWithDiscountOneWithoutDiscount"
+      ],
       "merchantIdentifier": {
         "cartUrl": "https://www.merchant1.com/cart",
         "merchantId": "10046"
@@ -16,8 +18,8 @@
         "languageCode": "en-US"
       }
     },
-    // Use case for FetchFLCodelessDiscountWorkerBrowserTest.SimpleDollarOffTest
     {
+      "_comment": "Use case for: FetchFLCodelessDiscountWorkerBrowserTest.SimpleDollarOffTest",
       "merchantIdentifier": {
         "cartUrl": "https://www.merchant2.com/cart",
         "merchantId": "10046"
@@ -30,6 +32,6 @@
         "languageCode": "en-US"
       }
     }
-    // cartUrl needs to be unique. Next cartUrl is https://www.merchant3.com/cart.
-  ]
+  ],
+  "_comment": "cartUrl needs to be unique. Next cartUrl is https://www.merchant3.com/cart."
 }
diff --git a/chrome/test/data/webui/chromeos/ash_common/keyboard_diagram_test.js b/chrome/test/data/webui/chromeos/ash_common/keyboard_diagram_test.js
index 6ee7af56..3095dca 100644
--- a/chrome/test/data/webui/chromeos/ash_common/keyboard_diagram_test.js
+++ b/chrome/test/data/webui/chromeos/ash_common/keyboard_diagram_test.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 {MechanicalLayout, PhysicalLayout} from 'chrome://resources/ash/common/keyboard_diagram.js';
+import {MechanicalLayout, PhysicalLayout, TopRowKey} from 'chrome://resources/ash/common/keyboard_diagram.js';
 import {assertEquals, assertNotEquals, assertTrue} from '../../chai_assert.js';
 import {flushTasks, waitAfterNextRender} from '../../test_util.js';
 
@@ -70,7 +70,6 @@
   });
 
   test('dell-enterprise', async () => {
-    assertKeyHidden('dellDeleteKey');
     assertKeyHidden('dellPageDownKey');
     assertKeyHidden('dellPageUpKey');
     assertKeyHidden('fnKey');
@@ -79,7 +78,6 @@
     diagramElement.physicalLayout = PhysicalLayout.kChromeOSDellEnterprise;
     await flushTasks();
 
-    assertKeyVisible('dellDeleteKey');
     assertKeyVisible('dellPageDownKey');
     assertKeyVisible('dellPageUpKey');
     assertKeyVisible('fnKey');
@@ -111,4 +109,26 @@
     await waitAfterNextRender(keyboardElement);
     assertEquals(290, keyboardElement.offsetHeight);
   });
+
+  test('topRowKeys', async () => {
+    const topRowContainer = diagramElement.$.topRow;
+    const testKeySet = [
+      TopRowKey.kBack,
+      TopRowKey.kRefresh,
+      TopRowKey.kNone,
+      TopRowKey.kNone,
+      TopRowKey.kScreenMirror,
+      TopRowKey.kDelete,
+    ];
+
+    diagramElement.topRowKeys = testKeySet;
+    await flushTasks();
+
+    const keyElements = topRowContainer.getElementsByTagName('keyboard-key');
+    // Add 2 for the escape and power keys, which are in the same container.
+    assertEquals(testKeySet.length + 2, keyElements.length);
+
+    assertEquals('keyboard:back', keyElements[1].icon);
+    assertEquals('delete', keyElements[6].mainGlyph);
+  });
 }
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.js
index ef699894..5713981 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.js
@@ -343,4 +343,38 @@
 
     await clearTimeoutPromise;
   });
+
+  test('skips updating OnWallpaperChange while in fullscreen', async () => {
+    personalizationStore.data.wallpaper.fullscreen = true;
+
+    wallpaperSelectedElement =
+        initElement(WallpaperSelected.is, {'path': Paths.CollectionImages});
+    await waitAfterNextRender(wallpaperSelectedElement);
+
+    personalizationStore.resetLastAction();
+
+    await wallpaperProvider.wallpaperObserverRemote.onWallpaperChanged(
+        wallpaperProvider.currentWallpaper);
+    await waitAfterNextRender(wallpaperSelectedElement);
+
+    assertEquals(null, personalizationStore.lastAction);
+
+    personalizationStore.data.wallpaper.fullscreen = false;
+    personalizationStore.notifyObservers();
+
+    personalizationStore.expectAction(WallpaperActionName.SET_SELECTED_IMAGE);
+
+    wallpaperProvider.wallpaperObserverRemote.onWallpaperChanged(
+        wallpaperProvider.currentWallpaper);
+
+    const action = await personalizationStore.waitForAction(
+        WallpaperActionName.SET_SELECTED_IMAGE);
+
+    assertDeepEquals(
+        {
+          name: WallpaperActionName.SET_SELECTED_IMAGE,
+          image: wallpaperProvider.currentWallpaper,
+        },
+        action);
+  });
 }
diff --git a/chrome/test/data/webui/cr_components/chromeos/bluetooth/bluetooth_pairing_device_item_test.js b/chrome/test/data/webui/cr_components/chromeos/bluetooth/bluetooth_pairing_device_item_test.js
index efb55f0..9d94042 100644
--- a/chrome/test/data/webui/cr_components/chromeos/bluetooth/bluetooth_pairing_device_item_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/bluetooth/bluetooth_pairing_device_item_test.js
@@ -86,9 +86,9 @@
   });
 
   test('Pairing message is shown', async function() {
+    const deviceName = 'BeatsX';
     const device = createDefaultBluetoothDevice(
-        /*id=*/ '12//345&6789',
-        /*publicName=*/ 'BeatsX',
+        /*id=*/ '12//345&6789', deviceName,
         /*connectionState=*/
         chromeos.bluetoothConfig.mojom.DeviceConnectionState.kConnected,
         /*nickname=*/ 'device1',
@@ -98,11 +98,28 @@
     bluetoothPairingDeviceItem.device = device.deviceProperties;
     await flushAsync();
 
+    const itemIndex = 1;
+    const listSize = 10;
+    bluetoothPairingDeviceItem.itemIndex = itemIndex;
+    bluetoothPairingDeviceItem.listSize = listSize;
+
     const getSecondaryLabel = () =>
         bluetoothPairingDeviceItem.shadowRoot.querySelector('#secondaryLabel');
+    const getItemSecondaryA11yLabel = () =>
+        bluetoothPairingDeviceItem.shadowRoot.querySelector('.text-row')
+            .ariaLabel;
+    const getItemA11yLabel = () =>
+        bluetoothPairingDeviceItem.shadowRoot.querySelector('#container')
+            .ariaLabel;
 
     assertTrue(!!getSecondaryLabel());
     assertEquals('', getSecondaryLabel().textContent.trim());
+    assertEquals(
+        getItemA11yLabel(),
+        bluetoothPairingDeviceItem.i18n(
+            'bluetoothPairingDeviceItemA11YLabelMouse', itemIndex + 1, listSize,
+            deviceName));
+    assertEquals(getItemSecondaryA11yLabel(), '');
 
     bluetoothPairingDeviceItem.deviceItemState = DeviceItemState.PAIRING;
     await flushAsync();
@@ -110,6 +127,10 @@
     assertEquals(
         bluetoothPairingDeviceItem.i18n('bluetoothPairing'),
         getSecondaryLabel().textContent.trim());
+    assertEquals(
+        getItemSecondaryA11yLabel(),
+        bluetoothPairingDeviceItem.i18n(
+            'bluetoothPairingDeviceItemSecondaryPairingA11YLabel', deviceName));
 
     bluetoothPairingDeviceItem.deviceItemState = DeviceItemState.FAILED;
     await flushAsync();
@@ -117,6 +138,10 @@
     assertEquals(
         bluetoothPairingDeviceItem.i18n('bluetoothPairingFailed'),
         getSecondaryLabel().textContent.trim());
+    assertEquals(
+        getItemSecondaryA11yLabel(),
+        bluetoothPairingDeviceItem.i18n(
+            'bluetoothPairingDeviceItemSecondaryErrorA11YLabel', deviceName));
 
     bluetoothPairingDeviceItem.deviceItemState = DeviceItemState.DEFAULT;
     await flushAsync();
diff --git a/chrome/test/data/webui/print_preview/color_settings_test.ts b/chrome/test/data/webui/print_preview/color_settings_test.ts
index 35236a1..10a37e7d 100644
--- a/chrome/test/data/webui/print_preview/color_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/color_settings_test.ts
@@ -54,7 +54,7 @@
     assertTrue(colorSection.getSetting('color').setFromUi);
   });
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   // Tests that if the setting is enforced by enterprise policy it is
   // disabled.
   test('disabled by policy', function() {
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test.ts b/chrome/test/data/webui/print_preview/destination_settings_test.ts
index 5f5ca3cd..f4b64135 100644
--- a/chrome/test/data/webui/print_preview/destination_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/destination_settings_test.ts
@@ -9,7 +9,7 @@
 import {eventToPromise, fakeDataBind, waitBeforeNextRender} from 'chrome://webui-test/test_util.js';
 
 import {CloudPrintInterfaceStub} from './cloud_print_interface_stub.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {NativeLayerCrosStub, setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 // </if>
 import {NativeLayerStub} from './native_layer_stub.js';
@@ -33,7 +33,7 @@
     UpdateRecentDestinations: 'update recent destinations',
     DisabledSaveAsPdf: 'disabled save as pdf',
     NoDestinations: 'no destinations',
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     EulaIsRetrieved: 'eula is retrieved',
     DriveIsNotMounted: 'drive is not mounted',
     // </if>
@@ -49,7 +49,7 @@
 
   let cloudPrintInterface: CloudPrintInterfaceStub;
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   let nativeLayerCros: NativeLayerCrosStub;
   // </if>
 
@@ -68,7 +68,7 @@
   const defaultUser: string = 'foo@chromium.org';
 
   const driveDestinationKey: string =
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       'Save to Drive CrOS/local/';
   // </if>
   // <if expr="not chromeos and not lacros">
@@ -85,7 +85,7 @@
     // Stub out native layer and cloud print interface.
     nativeLayer = new NativeLayerStub();
     NativeLayerImpl.setInstance(nativeLayer);
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     nativeLayerCros = setNativeLayerCrosInstance();
     // </if>
     localDestinations = [];
@@ -189,7 +189,7 @@
       });
 
   function getLocalOrigin(): DestinationOrigin {
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     return DestinationOrigin.CROS;
     // </if>
     // <if expr="not chromeos and not lacros">
@@ -198,7 +198,7 @@
   }
 
   function assertGoogleDrive() {
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     assertEquals(
         GooglePromotedDestinationId.SAVE_TO_DRIVE_CROS,
         destinationSettings.destination.id);
@@ -261,7 +261,7 @@
           assertFalse(destinationSettings.$.destinationSelect.disabled);
           const dropdownItems = [
             'Save as PDF/local/',
-            // <if expr="chromeos or lacros">
+            // <if expr="chromeos_ash or chromeos_lacros">
             driveDestinationKey,
             // </if>
           ];
@@ -294,7 +294,7 @@
               const dropdownItems = [
                 makeLocalDestinationKey('ID1'), makeLocalDestinationKey('ID2'),
                 makeLocalDestinationKey('ID3'), 'Save as PDF/local/',
-                // <if expr="chromeos or lacros">
+                // <if expr="chromeos_ash or chromeos_lacros">
                 driveDestinationKey,
                 // </if>
               ];
@@ -329,7 +329,7 @@
               const dropdownItems = [
                 makeLocalDestinationKey('ID1'), makeLocalDestinationKey('ID3'),
                 'Save as PDF/local/',
-                // <if expr="chromeos or lacros">
+                // <if expr="chromeos_ash or chromeos_lacros">
                 driveDestinationKey,
                 // </if>
               ];
@@ -360,7 +360,7 @@
           const dropdownItems = [
             makeLocalDestinationKey('ID1'), makeLocalDestinationKey('ID3'),
             makeLocalDestinationKey('ID4'), 'Save as PDF/local/',
-            // <if expr="chromeos or lacros">
+            // <if expr="chromeos_ash or chromeos_lacros">
             driveDestinationKey,
             // </if>
           ];
@@ -393,7 +393,7 @@
               assertEquals('ID1', destinationSettings.destination.id);
               assertFalse(destinationSettings.$.destinationSelect.disabled);
 
-              // <if expr="chromeos or lacros">
+              // <if expr="chromeos_ash or chromeos_lacros">
               const dropdownItems = [
                 makeLocalDestinationKey('ID1'),
                 makeLocalDestinationKey('ID3'),
@@ -441,7 +441,7 @@
               assertGoogleDrive();
               assertFalse(destinationSettings.$.destinationSelect.disabled);
 
-              // <if expr="chromeos or lacros">
+              // <if expr="chromeos_ash or chromeos_lacros">
               const dropdownItems = [
                 makeLocalDestinationKey('ID2'),
                 makeLocalDestinationKey('ID3'),
@@ -488,7 +488,7 @@
           const dropdownItems = [
             makeLocalDestinationKey('ID1'), makeLocalDestinationKey('ID3'),
             makeLocalDestinationKey('ID4'), 'Save as PDF/local/',
-            // <if expr="chromeos or lacros">
+            // <if expr="chromeos_ash or chromeos_lacros">
             driveDestinationKey,
             // </if>
           ];
@@ -539,7 +539,7 @@
               // This will result in the destination store setting the most
               // recent destination.
               assertEquals('ID1', destinationSettings.destination.id);
-              // <if expr="chromeos or lacros">
+              // <if expr="chromeos_ash or chromeos_lacros">
               const dropdownItems = [
                 makeLocalDestinationKey('ID1'),
                 makeLocalDestinationKey('ID3'),
@@ -600,7 +600,7 @@
               const dropdownItems = [
                 makeLocalDestinationKey('ID1'), makeLocalDestinationKey('ID2'),
                 makeLocalDestinationKey('ID3'), 'Save as PDF/local/',
-                // <if expr="chromeos or lacros">
+                // <if expr="chromeos_ash or chromeos_lacros">
                 driveDestinationKey,
                 // </if>
               ];
@@ -643,7 +643,7 @@
           const dropdownItems = [
             makeLocalDestinationKey('ID1'), makeLocalDestinationKey('ID2'),
             makeLocalDestinationKey('ID3'), 'Save as PDF/local/',
-            // <if expr="chromeos or lacros">
+            // <if expr="chromeos_ash or chromeos_lacros">
             driveDestinationKey,
             // </if>
           ];
@@ -702,7 +702,7 @@
               const dropdownItems = [
                 'FooCloud/cookies/foo@chromium.org',
                 makeLocalDestinationKey('ID1'), 'Save as PDF/local/',
-                // <if expr="chromeos or lacros">
+                // <if expr="chromeos_ash or chromeos_lacros">
                 driveDestinationKey,
                 // </if>
               ];
@@ -730,7 +730,7 @@
               const dropdownItems = [
                 'BarCloud/cookies/bar@chromium.org',
                 makeLocalDestinationKey('ID1'), 'Save as PDF/local/',
-                // <if expr="chromeos or lacros">
+                // <if expr="chromeos_ash or chromeos_lacros">
                 driveDestinationKey,
                 // </if>
               ];
@@ -857,7 +857,7 @@
               // Because the 'Save as PDF' fallback is unavailable, the first
               // destination is selected.
               const expectedDestination =
-                  // <if expr="chromeos or lacros">
+                  // <if expr="chromeos_ash or chromeos_lacros">
                   'Save to Drive CrOS/local/';
               // </if>
               // <if expr="not chromeos and not lacros">
@@ -891,7 +891,7 @@
         .then(() => assertDropdownItems(['noDestinations']));
   });
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * Tests that destinations with a EULA will fetch the EULA URL when
    * selected.
diff --git a/chrome/test/data/webui/print_preview/destination_store_test.ts b/chrome/test/data/webui/print_preview/destination_store_test.ts
index 9ecf766..a39762d 100644
--- a/chrome/test/data/webui/print_preview/destination_store_test.ts
+++ b/chrome/test/data/webui/print_preview/destination_store_test.ts
@@ -15,7 +15,7 @@
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {CloudPrintInterfaceStub} from './cloud_print_interface_stub.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 // </if>
 import {NativeLayerStub} from './native_layer_stub.js';
@@ -47,7 +47,7 @@
     MultipleRecentDestinationsAccounts: 'multiple recent destinations accounts',
     // </if>
     LoadAndSelectDestination: 'select loaded destination',
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     MultipleRecentDestinationsAccountsCros:
         'multiple recent destinations accounts for Chrome OS',
     LoadSaveToDriveCros: 'load Save to Drive Cros',
@@ -85,7 +85,7 @@
 
     nativeLayer = new NativeLayerStub();
     NativeLayerImpl.setInstance(nativeLayer);
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     setNativeLayerCrosInstance();
     // </if>
 
@@ -241,7 +241,7 @@
           // Verify that all local printers have been added to the store.
           const reportedPrinters = destinationStore.destinations();
           destinations.forEach(destination => {
-            // <if expr="chromeos or lacros">
+            // <if expr="chromeos_ash or chromeos_lacros">
             assertEquals(DestinationOrigin.CROS, destination.origin);
             // </if>
             // <if expr="not chromeos and not lacros">
@@ -316,7 +316,7 @@
           // should have been selected so there was only one preview request.
           const reportedPrinters = destinationStore.destinations();
           const expectedPrinters =
-              // <if expr="chromeos or lacros">
+              // <if expr="chromeos_ash or chromeos_lacros">
               7;
           // </if>
           // <if expr="not chromeos and not lacros">
@@ -622,7 +622,7 @@
             });
       });
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   /**
    * Tests that if there are recent destinations from different accounts, only
    * destinations associated with the most recent account are fetched.
diff --git a/chrome/test/data/webui/print_preview/duplex_settings_test.ts b/chrome/test/data/webui/print_preview/duplex_settings_test.ts
index 6460efaf..cda5827d 100644
--- a/chrome/test/data/webui/print_preview/duplex_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/duplex_settings_test.ts
@@ -96,7 +96,7 @@
     assertTrue(duplexSection.getSetting('duplexShortEdge').setFromUi);
   });
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   // Tests that if settings are enforced by enterprise policy the
   // appropriate UI is disabled.
   test('disabled by policy', function() {
diff --git a/chrome/test/data/webui/print_preview/invalid_settings_browsertest.ts b/chrome/test/data/webui/print_preview/invalid_settings_browsertest.ts
index 2bf15eb5..38477ea 100644
--- a/chrome/test/data/webui/print_preview/invalid_settings_browsertest.ts
+++ b/chrome/test/data/webui/print_preview/invalid_settings_browsertest.ts
@@ -9,7 +9,7 @@
 import {eventToPromise, waitBeforeNextRender} from 'chrome://webui-test/test_util.js';
 
 import {CloudPrintInterfaceStub} from './cloud_print_interface_stub.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 // </if>
 import {NativeLayerStub} from './native_layer_stub.js';
@@ -62,7 +62,7 @@
   setup(function() {
     nativeLayer = new NativeLayerStub();
     NativeLayerImpl.setInstance(nativeLayer);
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     setNativeLayerCrosInstance();
     // </if>
     cloudPrintInterface = new CloudPrintInterfaceStub();
diff --git a/chrome/test/data/webui/print_preview/key_event_test.ts b/chrome/test/data/webui/print_preview/key_event_test.ts
index 248055f..d73e859 100644
--- a/chrome/test/data/webui/print_preview/key_event_test.ts
+++ b/chrome/test/data/webui/print_preview/key_event_test.ts
@@ -11,7 +11,7 @@
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 // </if>
 import {NativeLayerStub} from './native_layer_stub.js';
@@ -51,7 +51,7 @@
         getCddTemplateWithAdvancedSettings(1, initialSettings.printerName));
     nativeLayer.setPageCount(3);
     NativeLayerImpl.setInstance(nativeLayer);
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     setNativeLayerCrosInstance();
     // </if>
     const pluginProxy = new TestPluginProxy();
diff --git a/chrome/test/data/webui/print_preview/model_settings_availability_test.ts b/chrome/test/data/webui/print_preview/model_settings_availability_test.ts
index 6d0111c..8dd3175e 100644
--- a/chrome/test/data/webui/print_preview/model_settings_availability_test.ts
+++ b/chrome/test/data/webui/print_preview/model_settings_availability_test.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {Destination, DestinationConnectionStatus, DestinationOrigin, DestinationType, DuplexType, GooglePromotedDestinationId, Margins, MarginsType, PrintPreviewModelElement, Size} from 'chrome://print/print_preview.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 // </if>
 
@@ -558,7 +558,7 @@
     // Windows and macOS depend on policy - see policy_test.js for their
     // testing coverage.
     model.set('documentSettings.isModifiable', false);
-    // <if expr="chromeos or lacros or is_linux">
+    // <if expr="chromeos_ash or chromeos_lacros or is_linux">
     // Always available for PDFs on Linux and ChromeOS
     assertTrue(model.settings.rasterize.available);
     assertFalse(model.settings.rasterize.setFromUi);
@@ -602,7 +602,7 @@
     assertFalse(model.settings.pagesPerSheet.available);
   });
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   test('pin', function() {
     // Make device unmanaged.
     loadTimeData.overrideValues({isEnterpriseManaged: false});
diff --git a/chrome/test/data/webui/print_preview/model_settings_policy_test.ts b/chrome/test/data/webui/print_preview/model_settings_policy_test.ts
index fb362f3..f254549 100644
--- a/chrome/test/data/webui/print_preview/model_settings_policy_test.ts
+++ b/chrome/test/data/webui/print_preview/model_settings_policy_test.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {ColorModeRestriction, Destination, DestinationConnectionStatus, DestinationOrigin, DestinationType, DuplexModeRestriction, Margins, PrintPreviewModelElement, Size} from 'chrome://print/print_preview.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {PinModeRestriction} from 'chrome://print/print_preview.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 // </if>
@@ -249,7 +249,7 @@
     });
   });
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   test('pin managed', function() {
     [{
       // No policies, settings is modifiable.
diff --git a/chrome/test/data/webui/print_preview/model_test.ts b/chrome/test/data/webui/print_preview/model_test.ts
index 9541f64..9b8a3ec 100644
--- a/chrome/test/data/webui/print_preview/model_test.ts
+++ b/chrome/test/data/webui/print_preview/model_test.ts
@@ -3,11 +3,11 @@
 // found in the LICENSE file.
 
 import {Destination, DestinationConnectionStatus, DestinationOrigin, DestinationType, DuplexMode, MarginsType, PrinterType, PrintPreviewModelElement, PrintTicket, ScalingType, Size} from 'chrome://print/print_preview.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {GooglePromotedDestinationId} from 'chrome://print/print_preview.js';
 // </if>
 import {assert} from 'chrome://resources/js/assert.m.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 // </if>
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -64,7 +64,7 @@
       isLandscapeEnabled: false,
       isColorEnabled: true,
       vendorOptions: {},
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       isPinEnabled: false,
       pinValue: '',
       // </if>
@@ -91,7 +91,7 @@
         paperType: 1,
         printArea: 6,
       },
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       isPinEnabled: true,
       pinValue: '0000',
       // </if>
@@ -156,7 +156,7 @@
             .then(() => testStickySetting('scalingType', 'scalingType'))
             .then(() => testStickySetting('scalingTypePdf', 'scalingTypePdf'))
             .then(() => testStickySetting('vendorItems', 'vendorOptions'));
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     promise = promise.then(() => testStickySetting('pin', 'isPinEnabled'))
                   .then(() => testStickySetting('pinValue', 'pinValue'));
     // </if>
@@ -225,7 +225,7 @@
         paperType: 1,
       },
       ranges: [{from: 2, to: 2}],
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       pin: true,
       pinValue: '0000',
       // </if>
@@ -267,7 +267,7 @@
    * print ticket.
    */
   test(assert(model_test.TestNames.GetPrintTicket), function() {
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     const origin = DestinationOrigin.CROS;
     // </if>
     // <if expr="not chromeos and not lacros">
@@ -279,7 +279,7 @@
     testDestination.capabilities =
         getCddTemplateWithAdvancedSettings(2, 'FooDevice').capabilities;
 
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     // Make device managed. It's used for testing pin setting behavior.
     loadTimeData.overrideValues({isEnterpriseManaged: true});
     // </if>
@@ -315,7 +315,7 @@
       pageHeight: 792,
       showSystemDialog: false,
     };
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     expectedDefaultTicketObject.advancedSettings = {
       printArea: 4,
       paperType: 0,
@@ -359,7 +359,7 @@
         marginLeft: 400,
       },
     };
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     expectedNewTicketObject.pinValue = '0000';
     expectedNewTicketObject.advancedSettings = {
       printArea: 6,
@@ -543,7 +543,7 @@
     assertEquals(false, model.getSettingValue('duplex'));
   });
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   // Tests that printToGoogleDrive is set correctly on the print ticket for Save
   // to Drive CrOS.
   test(assert(model_test.TestNames.PrintToGoogleDriveCros), function() {
diff --git a/chrome/test/data/webui/print_preview/native_layer_stub.ts b/chrome/test/data/webui/print_preview/native_layer_stub.ts
index 34b3189..c6c633e8 100644
--- a/chrome/test/data/webui/print_preview/native_layer_stub.ts
+++ b/chrome/test/data/webui/print_preview/native_layer_stub.ts
@@ -145,7 +145,7 @@
     if (printerId === GooglePromotedDestinationId.SAVE_AS_PDF) {
       return Promise.resolve(getPdfPrinter());
     }
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     if (printerId === GooglePromotedDestinationId.SAVE_TO_DRIVE_CROS) {
       return Promise.resolve(getPdfPrinter());
     }
diff --git a/chrome/test/data/webui/print_preview/policy_test.ts b/chrome/test/data/webui/print_preview/policy_test.ts
index f12adab2..55077a5 100644
--- a/chrome/test/data/webui/print_preview/policy_test.ts
+++ b/chrome/test/data/webui/print_preview/policy_test.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {BackgroundGraphicsModeRestriction, CrButtonElement, CrCheckboxElement, NativeInitialSettings, NativeLayerImpl, PluginProxyImpl, PolicyObjectEntry, PrintPreviewAppElement, PrintPreviewPluralStringProxyImpl, SerializedSettings} from 'chrome://print/print_preview.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {ColorModeRestriction, DuplexMode, DuplexModeRestriction, PinModeRestriction} from 'chrome://print/print_preview.js';
 // </if>
 
@@ -12,7 +12,7 @@
 import {assertEquals, assertFalse} from 'chrome://webui-test/chai_assert.js';
 import {TestPluralStringProxy} from 'chrome://webui-test/test_plural_string_proxy.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 // </if>
 
@@ -70,7 +70,7 @@
         [{deviceName: initialSettings.printerName, printerName: 'FooName'}]);
     nativeLayer.setPageCount(3);
     NativeLayerImpl.setInstance(nativeLayer);
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     setNativeLayerCrosInstance();
     // </if>
     const pluginProxy = new TestPluginProxy();
@@ -380,7 +380,7 @@
     }
   });
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   // Tests different scenarios of color printing policy.
   test(assert(policy_tests.TestNames.ColorPolicy), async () => {
     const tests = [
diff --git a/chrome/test/data/webui/print_preview/preview_generation_test.ts b/chrome/test/data/webui/print_preview/preview_generation_test.ts
index f109aac..e0101cf 100644
--- a/chrome/test/data/webui/print_preview/preview_generation_test.ts
+++ b/chrome/test/data/webui/print_preview/preview_generation_test.ts
@@ -7,7 +7,7 @@
 
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 // </if>
 
@@ -59,7 +59,7 @@
   setup(function() {
     nativeLayer = new NativeLayerStub();
     NativeLayerImpl.setInstance(nativeLayer);
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     setNativeLayerCrosInstance();
     // </if>
     document.body.innerHTML = '';
diff --git a/chrome/test/data/webui/print_preview/print_button_test.ts b/chrome/test/data/webui/print_preview/print_button_test.ts
index 4e1cd3c..9a2571db 100644
--- a/chrome/test/data/webui/print_preview/print_button_test.ts
+++ b/chrome/test/data/webui/print_preview/print_button_test.ts
@@ -3,13 +3,13 @@
 // found in the LICENSE file.
 
 import {CrButtonElement, NativeInitialSettings, NativeLayerImpl, PluginProxyImpl, PrintPreviewAppElement, PrintTicket} from 'chrome://print/print_preview.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {GooglePromotedDestinationId} from 'chrome://print/print_preview.js';
 // </if>
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 // </if>
 
@@ -42,7 +42,7 @@
   setup(function() {
     nativeLayer = new NativeLayerStub();
     NativeLayerImpl.setInstance(nativeLayer);
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     setNativeLayerCrosInstance();
     // </if>
     document.body.innerHTML = '';
@@ -147,7 +147,7 @@
         });
   });
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   // Tests that hidePreview() is not called if Save to Drive is selected on
   // Chrome OS and the user clicks print while the preview is loading because
   // Save to Drive needs to be treated like Save as PDF.
diff --git a/chrome/test/data/webui/print_preview/print_preview_app_test.ts b/chrome/test/data/webui/print_preview/print_preview_app_test.ts
index 211c9817..50b9837 100644
--- a/chrome/test/data/webui/print_preview/print_preview_app_test.ts
+++ b/chrome/test/data/webui/print_preview/print_preview_app_test.ts
@@ -10,7 +10,7 @@
 
 import {CloudPrintInterfaceStub} from './cloud_print_interface_stub.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 // </if>
 
@@ -82,7 +82,7 @@
     document.body.innerHTML = '';
     nativeLayer = new NativeLayerStub();
     NativeLayerImpl.setInstance(nativeLayer);
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     setNativeLayerCrosInstance();
     // </if>
     cloudPrintInterface = new CloudPrintInterfaceStub();
diff --git a/chrome/test/data/webui/print_preview/print_preview_sidebar_test.ts b/chrome/test/data/webui/print_preview/print_preview_sidebar_test.ts
index e07e4a1..efa272b 100644
--- a/chrome/test/data/webui/print_preview/print_preview_sidebar_test.ts
+++ b/chrome/test/data/webui/print_preview/print_preview_sidebar_test.ts
@@ -8,7 +8,7 @@
 import {fakeDataBind} from 'chrome://webui-test/test_util.js';
 
 import {CloudPrintInterfaceStub} from './cloud_print_interface_stub.js';
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 // </if>
 
@@ -40,7 +40,7 @@
     // Stub out the native layer and cloud print interface
     nativeLayer = new NativeLayerStub();
     NativeLayerImpl.setInstance(nativeLayer);
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     setNativeLayerCrosInstance();
     // </if>
     nativeLayer.setLocalDestinationCapabilities(getCddTemplate('FooDevice'));
diff --git a/chrome/test/data/webui/print_preview/print_preview_test_utils.ts b/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
index 945f309e..1bf56e41 100644
--- a/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
+++ b/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
@@ -89,7 +89,7 @@
       }
     }
   };
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   template.capabilities!.printer.pin = {supported: true};
   // </if>
   return template;
@@ -250,7 +250,7 @@
   // <if expr="not chromeos and not lacros">
   const origin = DestinationOrigin.LOCAL;
   // </if>
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   const origin = DestinationOrigin.CROS;
   // </if>
   // Five destinations. FooDevice is the system default.
@@ -334,7 +334,7 @@
       testListenerElement.addWebUIListener.bind(testListenerElement));
 }
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 /**
  * @return The Google Drive destination.
  */
diff --git a/chrome/test/data/webui/print_preview/restore_state_test.ts b/chrome/test/data/webui/print_preview/restore_state_test.ts
index e698215..c4e01b3 100644
--- a/chrome/test/data/webui/print_preview/restore_state_test.ts
+++ b/chrome/test/data/webui/print_preview/restore_state_test.ts
@@ -6,7 +6,7 @@
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 // </if>
 
@@ -35,7 +35,7 @@
   setup(function() {
     nativeLayer = new NativeLayerStub();
     NativeLayerImpl.setInstance(nativeLayer);
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     setNativeLayerCrosInstance();
     // </if>
     document.body.innerHTML = '';
@@ -143,7 +143,7 @@
           isDuplexShortEdge: true,
           isLandscapeEnabled: true,
           isColorEnabled: true,
-          // <if expr="chromeos or lacros">
+          // <if expr="chromeos_ash or chromeos_lacros">
           isPinEnabled: true,
           pinValue: '0000',
           // </if>
@@ -184,7 +184,7 @@
           isDuplexShortEdge: false,
           isLandscapeEnabled: false,
           isColorEnabled: false,
-          // <if expr="chromeos or lacros">
+          // <if expr="chromeos_ash or chromeos_lacros">
           isPinEnabled: false,
           pinValue: '',
           // </if>
@@ -301,7 +301,7 @@
           printArea: 6,
         },
       },
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       {
         section: 'print-preview-pin-settings',
         settingName: 'pin',
diff --git a/chrome/test/data/webui/print_preview/user_manager_test.ts b/chrome/test/data/webui/print_preview/user_manager_test.ts
index 15f5297..291ab008 100644
--- a/chrome/test/data/webui/print_preview/user_manager_test.ts
+++ b/chrome/test/data/webui/print_preview/user_manager_test.ts
@@ -7,7 +7,8 @@
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 import {CloudPrintInterfaceStub} from './cloud_print_interface_stub.js';
-// <if expr="chromeos or lacros">
+
+// <if expr="chromeos_ash or chromeos_lacros">
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 // </if>
 
@@ -37,7 +38,7 @@
     // Create data classes
     nativeLayer = new NativeLayerStub();
     NativeLayerImpl.setInstance(nativeLayer);
-    // <if expr="chromeos or lacros">
+    // <if expr="chromeos_ash or chromeos_lacros">
     setNativeLayerCrosInstance();
     // </if>
     cloudPrintInterface = new CloudPrintInterfaceStub();
diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/settings/BUILD.gn
index 3c311874..c37fdc0 100644
--- a/chrome/test/data/webui/settings/BUILD.gn
+++ b/chrome/test/data/webui/settings/BUILD.gn
@@ -36,13 +36,13 @@
   "a11y/manage_profile_a11y_test.js",
   "a11y/passwords_a11y_test.js",
   "a11y/sign_out_a11y_test.js",
-  "advanced_page_test.js",
+  "advanced_page_test.ts",
   "all_sites_tests.ts",
   "appearance_fonts_page_test.ts",
   "autofill_page_test.ts",
   "autofill_section_test.ts",
   "avatar_icon_test.ts",
-  "basic_page_test.js",
+  "basic_page_test.ts",
   "category_default_setting_tests.js",
   "category_setting_exceptions_tests.js",
   "checkbox_tests.ts",
@@ -88,19 +88,19 @@
   "safety_check_page_test.ts",
   "search_engines_page_test.ts",
   "search_page_test.ts",
-  "search_settings_test.js",
+  "search_settings_test.ts",
   "secure_dns_interactive_test.ts",
   "secure_dns_test.ts",
   "security_keys_subpage_test.ts",
-  "settings_animated_pages_test.js",
+  "settings_animated_pages_test.ts",
   "settings_category_default_radio_group_tests.ts",
   "settings_main_test.ts",
   "settings_menu_interactive_ui_test.ts",
   "settings_page_test_util.ts",
   "settings_slider_tests.ts",
-  "settings_subpage_test.js",
+  "settings_subpage_test.ts",
   "settings_textarea_tests.ts",
-  "settings_ui_tests.js",
+  "settings_ui_tests.ts",
   "site_data_details_subpage_tests.js",
   "site_data_test.js",
   "site_details_permission_tests.ts",
diff --git a/chrome/test/data/webui/settings/advanced_page_test.js b/chrome/test/data/webui/settings/advanced_page_test.ts
similarity index 62%
rename from chrome/test/data/webui/settings/advanced_page_test.js
rename to chrome/test/data/webui/settings/advanced_page_test.ts
index ae2b4a3..b39ba0ea0 100644
--- a/chrome/test/data/webui/settings/advanced_page_test.js
+++ b/chrome/test/data/webui/settings/advanced_page_test.ts
@@ -6,52 +6,49 @@
 
 // clang-format off
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {CrSettingsPrefs, SettingsBasicPageElement, SettingsMainElement, SettingsUiElement} from 'chrome://settings/settings.js';
-
-import {assertEquals, assertGT, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {CrSettingsPrefs, SettingsBasicPageElement, SettingsSectionElement} from 'chrome://settings/settings.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {getPage, getSection} from './settings_page_test_util.js';
 
 // clang-format on
 
 suite('AdvancedPage', function() {
-  /** @type {?SettingsBasicPageElement} */
-  let basicPage = null;
+  let basicPage: SettingsBasicPageElement;
 
   suiteSetup(function() {
     document.body.innerHTML = '';
-    const settingsUi = /** @type {SettingsUiElement} */ (
-        document.createElement('settings-ui'));
+    const settingsUi = document.createElement('settings-ui');
     document.body.appendChild(settingsUi);
     return CrSettingsPrefs.initialized
         .then(() => {
           return getPage('basic');
         })
         .then(page => {
-          basicPage = page;
-          const settingsMain = /** @type {!SettingsMainElement} */ (
-              settingsUi.shadowRoot.querySelector('settings-main'));
+          basicPage = page as SettingsBasicPageElement;
+          const settingsMain =
+              settingsUi.shadowRoot!.querySelector('settings-main');
           assertTrue(!!settingsMain);
-          settingsMain.advancedToggleExpanded = true;
+          settingsMain!.advancedToggleExpanded = true;
           flush();
         });
   });
 
   /**
    * Verifies that a section is rendered but hidden, including all its subpages.
-   * @param {!Node} section The DOM node for the section.
+   * @param section The DOM node for the section.
    */
-  function verifySectionWithSubpagesHidden(section) {
+  function verifySectionWithSubpagesHidden(section: SettingsSectionElement) {
     // Check if there are sub-pages to verify.
-    const pages = section.firstElementChild.shadowRoot.querySelector(
+    const pages = section.firstElementChild!.shadowRoot!.querySelector(
         'settings-animated-pages');
     if (!pages) {
       return;
     }
 
-    const children = pages.shadowRoot.querySelector('slot')
-                         .assignedNodes({flatten: true})
-                         .filter(n => n.nodeType === Node.ELEMENT_NODE);
+    const children =
+        pages.shadowRoot!.querySelector('slot')!.assignedNodes({flatten: true})
+            .filter(n => n.nodeType === Node.ELEMENT_NODE) as HTMLElement[];
 
     const stampedChildren = children.filter(function(element) {
       return element.tagName !== 'TEMPLATE';
@@ -62,10 +59,10 @@
     const main = stampedChildren.filter(function(element) {
       return element.getAttribute('route-path') === 'default';
     });
-    const sectionName = /** @type {{section: string}} */ (section).section;
+    const sectionName = section.section;
     assertEquals(
         main.length, 1, 'default card not found for section ' + sectionName);
-    assertEquals(main[0].offsetHeight, 0);
+    assertEquals(main[0]!.offsetHeight, 0);
 
     // Any other stamped subpages should also be hidden.
     const subpages = stampedChildren.filter(function(element) {
@@ -86,10 +83,9 @@
   test('advanced pages', function() {
     const sections = ['a11y', 'languages', 'downloads', 'reset'];
     for (let i = 0; i < sections.length; i++) {
-      const section = getSection(
-          /** @type {!SettingsBasicPageElement} */ (basicPage), sections[i]);
+      const section = getSection(basicPage, sections[i]!);
       assertTrue(!!section);
-      verifySectionWithSubpagesHidden(/** @type {!Node} */ (section));
+      verifySectionWithSubpagesHidden(section!);
     }
   });
 });
diff --git a/chrome/test/data/webui/settings/appearance_page_test.ts b/chrome/test/data/webui/settings/appearance_page_test.ts
index bbaba26..dd295156 100644
--- a/chrome/test/data/webui/settings/appearance_page_test.ts
+++ b/chrome/test/data/webui/settings/appearance_page_test.ts
@@ -194,7 +194,7 @@
   });
   // </if>
 
-  // <if expr="not is_linux or chromeos or lacros">
+  // <if expr="not is_linux or chromeos_ash or chromeos_lacros">
   test('useDefaultTheme', function() {
     assertFalse(!!appearancePage.get(THEME_ID_PREF));
     assertFalse(!!appearancePage.shadowRoot!.querySelector('#useDefault'));
diff --git a/chrome/test/data/webui/settings/basic_page_test.js b/chrome/test/data/webui/settings/basic_page_test.ts
similarity index 76%
rename from chrome/test/data/webui/settings/basic_page_test.js
rename to chrome/test/data/webui/settings/basic_page_test.ts
index 0e19d90..7ae0911 100644
--- a/chrome/test/data/webui/settings/basic_page_test.js
+++ b/chrome/test/data/webui/settings/basic_page_test.ts
@@ -10,16 +10,17 @@
 
 import {isChromeOS} from 'chrome://resources/js/cr.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {loadTimeData, pageVisibility, Router, routes} from 'chrome://settings/settings.js';
-
+import {pageVisibility, Router, routes, SettingsBasicPageElement, SettingsIdleLoadElement, SettingsSectionElement} from 'chrome://settings/settings.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+
 // clang-format on
 
 suite('SettingsBasicPage', () => {
-  let page = null;
+  let page: SettingsBasicPageElement;
 
   setup(async function() {
-    PolymerTest.clearBody();
+    document.body.innerHTML = '';
     page = document.createElement('settings-basic-page');
     document.body.appendChild(page);
     page.scroller = document.body;
@@ -31,8 +32,9 @@
 
     // Ensure that all settings-section instances are rendered.
     flush();
-    await page.$$('#advancedPageTemplate').get();
-    const sections = page.shadowRoot.querySelectorAll('settings-section');
+    await page.shadowRoot!
+        .querySelector<SettingsIdleLoadElement>('#advancedPageTemplate')!.get();
+    const sections = page.shadowRoot!.querySelectorAll('settings-section');
     assertTrue(sections.length > 1);
 
     await whenDone;
@@ -54,7 +56,8 @@
     flush();
 
     for (const section of sections) {
-      const sectionElement = page.$$(`settings-section[section=${section}]`);
+      const sectionElement = page.shadowRoot!.querySelector(
+          `settings-section[section=${section}]`);
       assertTrue(!!sectionElement);
     }
   });
@@ -66,40 +69,40 @@
     });
     flush();
 
-    const sectionElement = page.$$('settings-section-safety-check');
+    const sectionElement =
+        page.shadowRoot!.querySelector('settings-section-safety-check');
     assertFalse(!!sectionElement);
   });
 
-  /** @param {string} section */
-  function assertActiveSection(section) {
+  function assertActiveSection(section: string) {
     const activeSections =
-        page.shadowRoot.querySelectorAll('settings-section[active]');
+        page.shadowRoot!.querySelectorAll<SettingsSectionElement>(
+            'settings-section[active]');
     assertEquals(1, activeSections.length);
-    assertEquals(section, activeSections[0].section);
+    assertEquals(section, activeSections[0]!.section);
 
     // Check that only the |active| section is visible.
-    for (const s of page.shadowRoot.querySelectorAll('settings-section')) {
+    for (const s of page.shadowRoot!.querySelectorAll('settings-section')) {
       assertEquals(s === activeSections[0], isVisible(s));
     }
   }
 
-  /** @param {string} section */
-  function assertActiveSubpage(section) {
+  function assertActiveSubpage(section: string) {
     // Check that only the subpage of the |active| section is visible.
-    const settingsPages = page.shadowRoot.querySelectorAll(
+    const settingsPages = page.shadowRoot!.querySelectorAll(
         `settings-section[active] settings-${section}-page`);
     assertEquals(1, settingsPages.length);
     const subpages =
-        settingsPages[0].shadowRoot.querySelectorAll('settings-subpage');
+        settingsPages[0]!.shadowRoot!.querySelectorAll('settings-subpage');
     assertEquals(1, subpages.length);
-    assertTrue(isVisible(subpages[0]));
+    assertTrue(isVisible(subpages[0]!));
   }
 
   test('OnlyOneSectionShown', async () => {
     // RouteState.INITIAL -> RoutState.TOP_LEVEL
     // Check that only one is marked as |active|.
     assertActiveSection(routes.PEOPLE.section);
-    assertTrue(!!page.shadowRoot.querySelector(
+    assertTrue(!!page.shadowRoot!.querySelector(
         'settings-section[active] settings-people-page'));
 
     // RouteState.TOP_LEVEL -> RoutState.SECTION
@@ -109,22 +112,22 @@
     await whenDone;
     await flushTasks();
     assertActiveSection(routes.SEARCH.section);
-    assertTrue(!!page.shadowRoot.querySelector(
+    assertTrue(!!page.shadowRoot!.querySelector(
         'settings-section[active] settings-search-page'));
 
     // Helper functions.
     function getCardElement() {
-      return page.shadowRoot.querySelector(
+      return page.shadowRoot!.querySelector(
           'settings-section[active] settings-appearance-page');
     }
 
     function getDefault() {
-      return getCardElement().shadowRoot.querySelector(
+      return getCardElement()!.shadowRoot!.querySelector(
           'div[route-path="default"].iron-selected');
     }
 
     function getSubpage() {
-      return getCardElement().shadowRoot.querySelector(
+      return getCardElement()!.shadowRoot!.querySelector(
           'settings-subpage.iron-selected settings-appearance-fonts-page');
     }
 
@@ -196,12 +199,13 @@
     await flushTasks();
 
     const activeSections =
-        page.shadowRoot.querySelectorAll('settings-section[active]');
+        page.shadowRoot!.querySelectorAll<SettingsSectionElement>(
+            'settings-section[active]');
     assertEquals(2, activeSections.length);
-    assertEquals(routes.SAFETY_CHECK.section, activeSections[0].section);
+    assertEquals(routes.SAFETY_CHECK.section, activeSections[0]!.section);
     assertEquals(
         routes.PRIVACY.section,
-        activeSections[0].getAttribute('nest-under-section'));
-    assertEquals(routes.PRIVACY.section, activeSections[1].section);
+        activeSections[0]!.getAttribute('nest-under-section'));
+    assertEquals(routes.PRIVACY.section, activeSections[1]!.section);
   });
 });
diff --git a/chrome/test/data/webui/settings/clear_browsing_data_test.ts b/chrome/test/data/webui/settings/clear_browsing_data_test.ts
index 0d251016..9b471f9 100644
--- a/chrome/test/data/webui/settings/clear_browsing_data_test.ts
+++ b/chrome/test/data/webui/settings/clear_browsing_data_test.ts
@@ -636,7 +636,7 @@
     });
   });
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   // On ChromeOS the footer is never shown.
   test('ClearBrowsingDataSyncAccountInfo', function() {
     assertTrue(element.$.clearBrowsingDataDialog.open);
diff --git a/chrome/test/data/webui/settings/search_settings_test.js b/chrome/test/data/webui/settings/search_settings_test.ts
similarity index 72%
rename from chrome/test/data/webui/settings/search_settings_test.js
rename to chrome/test/data/webui/settings/search_settings_test.ts
index 9017f6e..25e8728 100644
--- a/chrome/test/data/webui/settings/search_settings_test.js
+++ b/chrome/test/data/webui/settings/search_settings_test.ts
@@ -3,17 +3,18 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {getSearchManager} from 'chrome://settings/settings.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {BaseMixin, getSearchManager, SearchManager} from 'chrome://settings/settings.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 // clang-format on
 
 suite('SearchSettingsTest', function() {
-  let searchManager;
+  let searchManager: SearchManager;
 
   setup(function() {
     searchManager = getSearchManager();
-    PolymerTest.clearBody();
+    document.body.innerHTML = '';
   });
 
   /**
@@ -27,8 +28,8 @@
            <div id="mydiv">${optionText}</div>
          </settings-section>`;
 
-    const section = document.querySelector('settings-section');
-    const div = document.querySelector('#mydiv');
+    const section = document.querySelector('settings-section')!;
+    const div = document.querySelector('#mydiv')!;
 
     assertTrue(section.hiddenBySearch);
     return searchManager.search('settings', section)
@@ -39,15 +40,15 @@
               div.querySelector('.search-highlight-wrapper');
           assertTrue(!!highlightWrapper);
 
-          const originalContent = highlightWrapper.querySelector(
+          const originalContent = highlightWrapper!.querySelector(
               '.search-highlight-original-content');
           assertTrue(!!originalContent);
-          assertEquals(optionText, originalContent.textContent);
+          assertEquals(optionText, originalContent!.textContent);
 
-          const searchHits =
-              highlightWrapper.querySelectorAll('.search-highlight-hit');
+          const searchHits = highlightWrapper!.querySelectorAll<HTMLElement>(
+              '.search-highlight-hit');
           assertEquals(1, searchHits.length);
-          assertEquals('Settings', searchHits[0].textContent);
+          assertEquals('Settings', searchHits[0]!.textContent);
 
           // Check that original DOM structure is restored when search
           // highlights are cleared.
@@ -73,8 +74,8 @@
            </select>
          </settings-section>`;
 
-    const section = document.querySelector('settings-section');
-    const select = section.querySelector('select');
+    const section = document.querySelector('settings-section')!;
+    const select = section.querySelector('select')!;
 
     assertTrue(section.hiddenBySearch);
     return searchManager.search('settings', section)
@@ -90,9 +91,9 @@
         .then(function() {
           const options = select.querySelectorAll('option');
           assertEquals(3, options.length);
-          assertEquals('Foo', options[0].textContent);
-          assertEquals('Settings', options[1].textContent);
-          assertEquals('Baz', options[2].textContent);
+          assertEquals('Foo', options[0]!.textContent);
+          assertEquals('Settings', options[1]!.textContent);
+          assertEquals('Baz', options[2]!.textContent);
         });
   });
 
@@ -114,7 +115,7 @@
            <template>${text}</template>
          </settings-section>`;
 
-    const section = document.querySelector('settings-section');
+    const section = document.querySelector('settings-section')!;
     assertTrue(section.hiddenBySearch);
 
     return searchManager.search(text, section).then(function() {
@@ -124,55 +125,63 @@
 
   test('no-search elements are ignored', function() {
     // Define a dummy test element with the necessary structure for testing.
-    Polymer({
-      is: 'dummy-test-element',
+    const DummyTestElementBase = BaseMixin(PolymerElement);
+    class DummyTestElement extends DummyTestElementBase {
+      get is() {
+        return 'dummy-test-element';
+      }
 
-      properties: {
-        noSearch: {
-          type: Boolean,
-          value: true,
-        },
-      },
-    });
-
-    const text = 'hello';
-
-    document.body.innerHTML = `
-      <dom-module id="dummy-test-element">
-        <template>
+      static get template() {
+        return html`
           <button></button>
           <settings-section hidden-by-search>
             <!-- Test case were no-search is part of a data binding. -->
             <template is="dom-if" route-path="/myPath0"
                 no-search="[[noSearch]]">
               <settings-subpage associated-control="[[$$('button')]]">
-                ${text}
+                hello
               </settings-subpage>
             </template>
 
             <!-- Test case were no-search is not part of any data binding.-->
             <template is="dom-if" route-path="/myPath1" no-search>
               <settings-subpage associated-control="[[$$('button')]]">
-                ${text}
+                hello
               </settings-subpage>
             </template>
           </settings-section>
-        </template>
-      </dom-module>
-      <dummy-test-element></dummy-test-element>`;
+         `;
+      }
 
-    const element = document.body.querySelector('dummy-test-element');
-    const section = element.$$('settings-section');
+      get properties() {
+        return {
+          noSearch: Boolean,
+        };
+      }
+
+      noSearch: boolean = true;
+    }
+
+    customElements.define('dummy-test-element', DummyTestElement);
+
+    const text = 'hello';
+
+    document.body.innerHTML = `<dummy-test-element></dummy-test-element>`;
+
+    const element =
+        document.body.querySelector<DummyTestElement>('dummy-test-element')!;
+    const section = element.shadowRoot!.querySelector('settings-section')!;
 
     // Ensure that no settings-subpage instance exists.
-    assertEquals(null, element.$$('settings-subpage'));
+    assertEquals(null, element.shadowRoot!.querySelector('settings-subpage'));
 
     return searchManager.search(text, section)
         .then(function() {
           assertTrue(section.hiddenBySearch);
           // Check that searching did not cause a settings-subpage instance to
           // be forced rendered.
-          assertEquals(null, element.$$('settings-subpage'));
+          assertEquals(
+              null, element.shadowRoot!.querySelector('settings-subpage'));
 
           element.noSearch = false;
           return searchManager.search(text, section);
@@ -181,7 +190,7 @@
           // Check that searching caused a settings-subpage instance to be
           // forced rendered.
           assertFalse(section.hiddenBySearch);
-          assertTrue(!!element.$$('settings-subpage'));
+          assertTrue(!!element.shadowRoot!.querySelector('settings-subpage'));
         });
   });
 
@@ -203,12 +212,12 @@
 
     return Promise.all(sections.map(s => searchManager.search('there', s)))
         .then(function(requests) {
-          assertTrue(requests[0].didFindMatches());
-          assertFalse(sections[0].hiddenBySearch);
-          assertTrue(requests[1].didFindMatches());
-          assertFalse(sections[1].hiddenBySearch);
-          assertFalse(requests[2].didFindMatches());
-          assertTrue(sections[2].hiddenBySearch);
+          assertTrue(requests[0]!.didFindMatches());
+          assertFalse(sections[0]!.hiddenBySearch);
+          assertTrue(requests[1]!.didFindMatches());
+          assertFalse(sections[1]!.hiddenBySearch);
+          assertFalse(requests[2]!.didFindMatches());
+          assertTrue(sections[2]!.hiddenBySearch);
         });
   });
 
@@ -219,20 +228,20 @@
           <div id="mydiv">${originalText}</div>
         </settings-section>`;
 
-    const section = document.querySelector('settings-section');
-    const div = document.querySelector('#mydiv');
+    const section = document.querySelector('settings-section')!;
+    const div = document.querySelector('#mydiv')!;
     assertTrue(section.hiddenBySearch);
     return searchManager.search('settings', document.body).then(() => {
       assertFalse(section.hiddenBySearch);
       assertEquals(1, div.childNodes.length);
-      const highlightWrapper = div.firstChild;
+      const highlightWrapper = div.firstChild as HTMLElement;
       assertTrue(
           highlightWrapper.classList.contains('search-highlight-wrapper'));
       const originalContent =
           highlightWrapper.querySelector('.search-highlight-original-content');
       assertTrue(!!originalContent);
-      originalContent.childNodes[0].nodeValue = 'Foo';
-      return new Promise(resolve => {
+      originalContent!.childNodes[0]!.nodeValue = 'Foo';
+      return new Promise<void>(resolve => {
         setTimeout(() => {
           assertFalse(section.hiddenBySearch);
           assertEquals(1, div.childNodes.length);
@@ -248,8 +257,8 @@
         <div id="mydiv">Match</div>
         <settings-section></settings-section>`;
 
-    const section = document.querySelector('settings-section');
-    const mydiv = document.querySelector('#mydiv');
+    const section = document.querySelector('settings-section')!;
+    const mydiv = document.querySelector('#mydiv')!;
 
     await searchManager.search('Match', document.body);
     assertTrue(section.hiddenBySearch);
@@ -257,9 +266,9 @@
     const highlight = mydiv.querySelector('.search-highlight-wrapper');
     assertTrue(!!highlight);
 
-    const searchHits = highlight.querySelectorAll('.search-highlight-hit');
+    const searchHits = highlight!.querySelectorAll('.search-highlight-hit');
     assertEquals(1, searchHits.length);
-    assertEquals('Match', searchHits[0].textContent);
+    assertEquals('Match', searchHits[0]!.textContent);
   });
 
   test('associated control causes search highlight bubble', async () => {
@@ -270,7 +279,7 @@
             hello
           </settings-subpage>
         </settings-section>`;
-    const subpage = document.querySelector('settings-subpage');
+    const subpage = document.querySelector('settings-subpage')!;
     subpage.associatedControl = document.querySelector('button');
 
     await searchManager.search('hello', document.body);
@@ -294,15 +303,15 @@
           </settings-subpage>
         </setting-section>`;
 
-    const subpage = document.querySelector('settings-subpage');
+    const subpage = document.querySelector('settings-subpage')!;
     subpage.associatedControl = document.querySelector('button');
 
     await searchManager.search('hello', document.body);
 
     const bubbles = document.querySelectorAll('.search-bubble');
     assertEquals(2, bubbles.length);
-    assertEquals('4 results', bubbles[1].textContent);
-    assertEquals('1 result', bubbles[0].textContent);
+    assertEquals('4 results', bubbles[1]!.textContent);
+    assertEquals('1 result', bubbles[0]!.textContent);
   });
 
   test('diacritics', async () => {
@@ -318,7 +327,7 @@
           danger zone
         </setting-section>`;
 
-    const subpage = document.querySelector('settings-subpage');
+    const subpage = document.querySelector('settings-subpage')!;
     subpage.associatedControl = document.querySelector('button');
 
     await searchManager.search('an', document.body);
diff --git a/chrome/test/data/webui/settings/settings_animated_pages_test.js b/chrome/test/data/webui/settings/settings_animated_pages_test.ts
similarity index 77%
rename from chrome/test/data/webui/settings/settings_animated_pages_test.js
rename to chrome/test/data/webui/settings/settings_animated_pages_test.ts
index df37fd70..0d8fca2 100644
--- a/chrome/test/data/webui/settings/settings_animated_pages_test.js
+++ b/chrome/test/data/webui/settings/settings_animated_pages_test.ts
@@ -4,6 +4,7 @@
 
 // clang-format off
 import {Route, Router} from 'chrome://settings/settings.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {setupPopstateListener} from './test_util.js';
@@ -11,20 +12,32 @@
 // clang-format on
 
 suite('settings-animated-pages', function() {
-  /** @type {?SettingsRoutes}  */
-  let testRoutes = null;
+  let testRoutes: {
+    ABOUT: Route,
+    ADVANCED: Route,
+    BASIC: Route,
+    PRIVACY: Route,
+    SEARCH_ENGINES: Route,
+    SEARCH: Route,
+    SITE_SETTINGS_COOKIES: Route,
+    SITE_SETTINGS: Route,
+  };
 
   setup(function() {
-    testRoutes = {
-      BASIC: new Route('/'),
-    };
-    testRoutes.SEARCH = testRoutes.BASIC.createSection('/search', 'search');
-    testRoutes.SEARCH_ENGINES = testRoutes.SEARCH.createChild('/searchEngines');
+    const basicRoute = new Route('/');
+    const searchRoute = basicRoute.createSection('/search', 'search');
+    const privacyRoute = basicRoute.createSection('/privacy', 'privacy');
 
-    testRoutes.PRIVACY = testRoutes.BASIC.createSection('/privacy', 'privacy');
-    testRoutes.SITE_SETTINGS = testRoutes.PRIVACY.createChild('/content');
-    testRoutes.SITE_SETTINGS_COOKIES =
-        testRoutes.PRIVACY.createChild('/cookies');
+    testRoutes = {
+      ABOUT: basicRoute.createChild('/about'),
+      ADVANCED: basicRoute.createChild('/advanced'),
+      BASIC: basicRoute,
+      PRIVACY: privacyRoute,
+      SEARCH_ENGINES: searchRoute.createChild('/searchEngines'),
+      SEARCH: searchRoute,
+      SITE_SETTINGS_COOKIES: privacyRoute.createChild('/cookies'),
+      SITE_SETTINGS: privacyRoute.createChild('/content'),
+    };
 
     Router.resetInstanceForTesting(new Router(testRoutes));
     setupPopstateListener();
@@ -42,14 +55,14 @@
       </settings-animated-pages>`;
 
     const animatedPages =
-        document.body.querySelector('settings-animated-pages');
+        document.body.querySelector('settings-animated-pages')!;
     animatedPages.focusConfig = new Map();
     animatedPages.focusConfig.set(
         testRoutes.SEARCH_ENGINES.path, '#subpage-trigger');
 
     const trigger = document.body.querySelector('#subpage-trigger');
     assertTrue(!!trigger);
-    const whenDone = eventToPromise('focus', trigger);
+    const whenDone = eventToPromise('focus', trigger!);
 
     // Trigger subpage exit navigation.
     Router.getInstance().navigateTo(testRoutes.BASIC);
@@ -74,7 +87,7 @@
       </settings-animated-pages>`;
 
     const animatedPages =
-        document.body.querySelector('settings-animated-pages');
+        document.body.querySelector('settings-animated-pages')!;
     animatedPages.focusConfig = new Map();
     animatedPages.focusConfig.set(
         testRoutes.SITE_SETTINGS_COOKIES.path + '_' + testRoutes.PRIVACY.path,
@@ -88,7 +101,7 @@
     // correct element #subpageTrigger1 is focused.
     const trigger1 = document.body.querySelector('#subpage-trigger1');
     assertTrue(!!trigger1);
-    let whenDone = eventToPromise('focus', trigger1);
+    let whenDone = eventToPromise('focus', trigger1!);
 
     Router.getInstance().navigateTo(testRoutes.PRIVACY);
     Router.getInstance().navigateTo(testRoutes.SITE_SETTINGS_COOKIES);
@@ -99,7 +112,7 @@
     // correct element #subpageTrigger1 is focused.
     const trigger2 = document.body.querySelector('#subpage-trigger2');
     assertTrue(!!trigger2);
-    whenDone = eventToPromise('focus', trigger2);
+    whenDone = eventToPromise('focus', trigger2!);
 
     Router.getInstance().navigateTo(testRoutes.SITE_SETTINGS);
     Router.getInstance().navigateTo(testRoutes.SITE_SETTINGS_COOKIES);
@@ -116,16 +129,16 @@
         </settings-subpage>
       </settings-animated-pages>`;
 
-    const subpage = document.body.querySelector('settings-subpage');
+    const subpage = document.body.querySelector('settings-subpage')!;
     let counter = 0;
 
-    const whenFired = new Promise(resolve => {
+    const whenFired = new Promise<void>(resolve => {
       // Override |focusBackButton| to check how many times it is called.
       subpage.focusBackButton = () => {
         counter++;
 
         if (counter === 1) {
-          const other = document.body.querySelector('div');
+          const other = document.body.querySelector('div')!;
           other.dispatchEvent(new CustomEvent('iron-select', {bubbles: true}));
           resolve();
         }
diff --git a/chrome/test/data/webui/settings/settings_page_test_util.ts b/chrome/test/data/webui/settings/settings_page_test_util.ts
index fe3c2174..b554d2c 100644
--- a/chrome/test/data/webui/settings/settings_page_test_util.ts
+++ b/chrome/test/data/webui/settings/settings_page_test_util.ts
@@ -4,6 +4,7 @@
 
 // clang-format off
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {SettingsSectionElement} from 'chrome://settings/settings.js';
 import {assertTrue} from 'chrome://webui-test/chai_assert.js';
 // clang-format on
 
@@ -38,7 +39,8 @@
  * @param section The settings page section, e.g. 'appearance'.
  * @return The DOM node for the section.
  */
-export function getSection(page: HTMLElement, section: string): Node|undefined {
+export function getSection(
+    page: HTMLElement, section: string): SettingsSectionElement|undefined {
   const sections = page.shadowRoot!.querySelectorAll('settings-section');
   assertTrue(!!sections);
   for (const s of sections) {
diff --git a/chrome/test/data/webui/settings/settings_subpage_test.js b/chrome/test/data/webui/settings/settings_subpage_test.ts
similarity index 62%
rename from chrome/test/data/webui/settings/settings_subpage_test.js
rename to chrome/test/data/webui/settings/settings_subpage_test.ts
index 1bc1c41..da5f8811 100644
--- a/chrome/test/data/webui/settings/settings_subpage_test.js
+++ b/chrome/test/data/webui/settings/settings_subpage_test.ts
@@ -4,7 +4,8 @@
 
 // clang-format off
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {Route, Router} from 'chrome://settings/settings.js';
+import {loadTimeData, Route, Router} from 'chrome://settings/settings.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
 
 import {setupPopstateListener} from './test_util.js';
@@ -12,27 +13,46 @@
 // clang-format on
 
 suite('SettingsSubpage', function() {
-  let testRoutes;
+  let testRoutes: {
+    ABOUT: Route,
+    ADVANCED: Route,
+    BASIC: Route,
+    CERTIFICATES: Route,
+    COOKIE_DETAILS: Route,
+    PEOPLE: Route,
+    PRIVACY: Route,
+    SEARCH_ENGINES: Route,
+    SEARCH: Route,
+    SITE_DATA: Route,
+    SYNC: Route,
+  };
 
   setup(function() {
+    const basicRoute = new Route('/');
+    const searchRoute = basicRoute.createSection('/search', 'search');
+    const peopleRoute = basicRoute.createSection('/people', 'people');
+    const privacyRoute = basicRoute.createSection('/privacy', 'privacy');
+    const siteDataRoute = basicRoute.createChild('/siteData');
+
     testRoutes = {
-      BASIC: new Route('/'),
+      ABOUT: basicRoute.createChild('/about'),
+      ADVANCED: basicRoute.createChild('/advanced'),
+      BASIC: basicRoute,
+      CERTIFICATES: privacyRoute.createChild('/certificates'),
+      COOKIE_DETAILS: siteDataRoute.createChild('/cookies/detail'),
+      PEOPLE: peopleRoute,
+      PRIVACY: privacyRoute,
+      SEARCH_ENGINES: searchRoute.createChild('/searchEngines'),
+      SEARCH: searchRoute,
+      SITE_DATA: siteDataRoute,
+      SYNC: peopleRoute.createChild('/syncSetup'),
     };
-    testRoutes.SEARCH = testRoutes.BASIC.createSection('/search', 'search');
-    testRoutes.SEARCH_ENGINES = testRoutes.SEARCH.createChild('/searchEngines');
-    testRoutes.PEOPLE = testRoutes.BASIC.createSection('/people', 'people');
-    testRoutes.SYNC = testRoutes.PEOPLE.createChild('/syncSetup');
-    testRoutes.PRIVACY = testRoutes.BASIC.createSection('/privacy', 'privacy');
-    testRoutes.CERTIFICATES = testRoutes.PRIVACY.createChild('/certificates');
-    testRoutes.SITE_DATA = testRoutes.BASIC.createChild('/siteData');
-    testRoutes.COOKIE_DETAILS =
-        testRoutes.SITE_DATA.createChild('/cookies/detail');
 
     Router.resetInstanceForTesting(new Router(testRoutes));
 
     setupPopstateListener();
 
-    PolymerTest.clearBody();
+    document.body.innerHTML = '';
   });
 
   function createSettingsSubpageWithPreserveSearchTerm() {
@@ -52,14 +72,14 @@
     // Check that the help icon only shows up when a |learnMoreUrl| is
     // specified.
     assertFalse(
-        !!subpage.shadowRoot.querySelector('[iron-icon="cr:help-outline"]'));
+        !!subpage.shadowRoot!.querySelector('[iron-icon="cr:help-outline"]'));
     subpage.learnMoreUrl = 'https://www.chromium.org';
     flush();
-    const icon =
-        subpage.shadowRoot.querySelector('[iron-icon="cr:help-outline"]');
+    const icon = subpage.shadowRoot!.querySelector<HTMLElement>(
+        '[iron-icon="cr:help-outline"]');
     assertTrue(!!icon);
     // Check that the icon is forced to always use 'ltr' mode.
-    assertEquals('ltr', icon.getAttribute('dir'));
+    assertEquals('ltr', icon!.getAttribute('dir'));
   });
 
   test('clear search (event)', function() {
@@ -68,12 +88,13 @@
     subpage.searchLabel = 'test';
     document.body.appendChild(subpage);
     flush();
-    const search = subpage.shadowRoot.querySelector('cr-search-field');
+    const search = subpage.shadowRoot!.querySelector('cr-search-field');
     assertTrue(!!search);
-    search.setValue('Hello');
-    subpage.fire('clear-subpage-search');
+    search!.setValue('Hello');
+    subpage.dispatchEvent(new CustomEvent(
+        'clear-subpage-search', {bubbles: true, composed: true}));
     flush();
-    assertEquals('', search.getValue());
+    assertEquals('', search!.getValue());
   });
 
   test('clear search (click)', async () => {
@@ -82,14 +103,14 @@
     subpage.searchLabel = 'test';
     document.body.appendChild(subpage);
     flush();
-    const search = subpage.shadowRoot.querySelector('cr-search-field');
+    const search = subpage.shadowRoot!.querySelector('cr-search-field');
     assertTrue(!!search);
-    search.setValue('Hello');
-    assertEquals(null, search.root.activeElement);
-    search.$.clearSearch.click();
+    search!.setValue('Hello');
+    assertEquals(null, search!.shadowRoot!.activeElement);
+    search!.$.clearSearch.click();
     await flushTasks();
-    assertEquals('', search.getValue());
-    assertEquals(search.$.searchInput, search.root.activeElement);
+    assertEquals('', search!.getValue());
+    assertEquals(search!.$.searchInput, search!.shadowRoot!.activeElement);
   });
 
   test('preserve search result when back button is clicked', async () => {
@@ -99,10 +120,10 @@
     flush();
 
     // Set search field.
-    let search = subpage.shadowRoot.querySelector('cr-search-field');
+    let search = subpage.shadowRoot!.querySelector('cr-search-field');
     assertTrue(!!search);
-    search.setValue('test');
-    assertEquals('test', search.getValue());
+    search!.setValue('test');
+    assertEquals('test', search!.getValue());
 
     // Navigate to another subpage.
     Router.getInstance().navigateTo(testRoutes.COOKIE_DETAILS);
@@ -112,17 +133,17 @@
     Router.getInstance().navigateToPreviousRoute();
     subpage = createSettingsSubpageWithPreserveSearchTerm();
     await eventToPromise('popstate', window);
-    search = subpage.shadowRoot.querySelector('cr-search-field');
+    search = subpage.shadowRoot!.querySelector('cr-search-field');
     assertTrue(!!search);
-    assertEquals('test', search.getValue());
+    assertEquals('test', search!.getValue());
 
     // Go back to settings subpage, verify search field is empty
     Router.getInstance().navigateToPreviousRoute();
     subpage = createSettingsSubpageWithPreserveSearchTerm();
     await eventToPromise('popstate', window);
-    search = subpage.shadowRoot.querySelector('cr-search-field');
+    search = subpage.shadowRoot!.querySelector('cr-search-field');
     assertTrue(!!search);
-    assertEquals('', search.getValue());
+    assertEquals('', search!.getValue());
   });
 
   test('preserve search result from URL input', async function() {
@@ -131,9 +152,9 @@
     Router.getInstance().navigateTo(testRoutes.SITE_DATA, params);
     const subpage = createSettingsSubpageWithPreserveSearchTerm();
     await flushTasks();
-    const search = subpage.shadowRoot.querySelector('cr-search-field');
+    const search = subpage.shadowRoot!.querySelector('cr-search-field');
     assertTrue(!!search);
-    assertEquals('test', search.getValue());
+    assertEquals('test', search!.getValue());
   });
 
   test('navigates to parent when there is no history', function() {
@@ -146,7 +167,7 @@
     const subpage = document.createElement('settings-subpage');
     document.body.appendChild(subpage);
 
-    subpage.shadowRoot.querySelector('cr-icon-button').click();
+    subpage.shadowRoot!.querySelector('cr-icon-button')!.click();
     assertEquals(testRoutes.PRIVACY, Router.getInstance().getCurrentRoute());
   });
 
@@ -158,12 +179,10 @@
     const subpage = document.createElement('settings-subpage');
     document.body.appendChild(subpage);
 
-    subpage.shadowRoot.querySelector('cr-icon-button').click();
+    subpage.shadowRoot!.querySelector('cr-icon-button')!.click();
 
     await eventToPromise('popstate', window);
-    assertEquals(
-        Router.getInstance().getRoutes().BASIC,
-        Router.getInstance().getCurrentRoute());
+    assertEquals(testRoutes.BASIC, Router.getInstance().getCurrentRoute());
   });
 
   test('updates the title of the document when active', function() {
@@ -183,16 +202,16 @@
 
 suite('SettingsSubpageSearch', function() {
   test('host autofocus propagates to <cr-input>', function() {
-    PolymerTest.clearBody();
+    document.body.innerHTML = '';
     const element = document.createElement('cr-search-field');
-    element.setAttribute('autofocus', true);
+    element.toggleAttribute('autofocus', true);
     document.body.appendChild(element);
 
-    assertTrue(
-        element.shadowRoot.querySelector('cr-input').hasAttribute('autofocus'));
+    assertTrue(element.shadowRoot!.querySelector('cr-input')!.hasAttribute(
+        'autofocus'));
 
     element.removeAttribute('autofocus');
-    assertFalse(
-        element.shadowRoot.querySelector('cr-input').hasAttribute('autofocus'));
+    assertFalse(element.shadowRoot!.querySelector('cr-input')!.hasAttribute(
+        'autofocus'));
   });
 });
diff --git a/chrome/test/data/webui/settings/settings_ui_tests.js b/chrome/test/data/webui/settings/settings_ui_tests.ts
similarity index 67%
rename from chrome/test/data/webui/settings/settings_ui_tests.js
rename to chrome/test/data/webui/settings/settings_ui_tests.ts
index ec55fd0..a731c75 100644
--- a/chrome/test/data/webui/settings/settings_ui_tests.js
+++ b/chrome/test/data/webui/settings/settings_ui_tests.ts
@@ -4,34 +4,25 @@
 
 // clang-format off
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {CrSettingsPrefs, Router, routes, SettingsMenuElement, SettingsUiElement} from 'chrome://settings/settings.js';
-
+import {CrDrawerElement, CrSettingsPrefs, CrToolbarElement, CrToolbarSearchFieldElement, Router, routes, SettingsMenuElement, SettingsUiElement} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 // clang-format on
 
 /** @fileoverview Suite of tests for the Settings layout. */
 suite('SettingsUIToolbarAndDrawer', function() {
-  /** @type {!SettingsUiElement} */
-  let ui;
-
-  /** @type {!CrToolbarElement} */
-  let toolbar;
-
-  /** @type {!CrDrawerElement} */
-  let drawer;
+  let ui: SettingsUiElement;
+  let toolbar: CrToolbarElement;
+  let drawer: CrDrawerElement;
 
   setup(function() {
     document.body.innerHTML = '';
-    ui = /** @type {!SettingsUiElement} */ (
-        document.createElement('settings-ui'));
+    ui = document.createElement('settings-ui');
     document.body.appendChild(ui);
     return CrSettingsPrefs.initialized.then(() => {
       flush();
-      toolbar = /** @type {!CrToolbarElement} */ (
-          ui.shadowRoot.querySelector('cr-toolbar'));
-      drawer = /** @type {!CrDrawerElement} */ (
-          ui.shadowRoot.querySelector('#drawer'));
+      toolbar = ui.$.toolbar;
+      drawer = ui.$.drawer;
     });
   });
 
@@ -45,7 +36,7 @@
   });
 
   test('app drawer', async () => {
-    assertEquals(null, ui.shadowRoot.querySelector('cr-drawer settings-menu'));
+    assertEquals(null, ui.shadowRoot!.querySelector('cr-drawer settings-menu'));
     assertFalse(!!drawer.open);
 
     const drawerOpened = eventToPromise('cr-drawer-opened', drawer);
@@ -54,7 +45,7 @@
 
     // Validate that dialog is open and menu is shown so it will animate.
     assertTrue(drawer.open);
-    assertTrue(!!ui.shadowRoot.querySelector('cr-drawer settings-menu'));
+    assertTrue(!!ui.shadowRoot!.querySelector('cr-drawer settings-menu'));
 
     await drawerOpened;
     const drawerClosed = eventToPromise('close', drawer);
@@ -64,7 +55,7 @@
     // Drawer is closed, but menu is still stamped so
     // its contents remain visible as the drawer slides
     // out.
-    assertTrue(!!ui.shadowRoot.querySelector('cr-drawer settings-menu'));
+    assertTrue(!!ui.shadowRoot!.querySelector('cr-drawer settings-menu'));
   });
 
   test('app drawer closes when exiting narrow mode', async () => {
@@ -82,61 +73,60 @@
 });
 
 suite('SettingsUIAdvanced', function() {
-  /** @type {!SettingsUiElement} */
-  let ui;
+  let ui: SettingsUiElement;
 
   setup(function() {
     document.body.innerHTML = '';
-    ui = /** @type {!SettingsUiElement} */ (
-        document.createElement('settings-ui'));
+    ui = document.createElement('settings-ui');
     document.body.appendChild(ui);
     return CrSettingsPrefs.initialized.then(() => flush());
   });
 
   test('advanced UIs stay in sync', function() {
-    const main = ui.shadowRoot.querySelector('settings-main');
-    const floatingMenu =
-        /** @type {!SettingsMenuElement} */ (
-            ui.shadowRoot.querySelector('#left settings-menu'));
+    const main = ui.$.main;
+    const floatingMenu = ui.shadowRoot!.querySelector<SettingsMenuElement>(
+        '#left settings-menu');
     assertTrue(!!main);
     assertTrue(!!floatingMenu);
 
-    assertFalse(!!ui.shadowRoot.querySelector('cr-drawer settings-menu'));
+    assertFalse(!!ui.shadowRoot!.querySelector('cr-drawer settings-menu'));
     assertFalse(ui.getAdvancedOpenedInMainForTest());
     assertFalse(ui.getAdvancedOpenedInMenuForTest());
-    assertFalse(floatingMenu.advancedOpened);
+    assertFalse(floatingMenu!.advancedOpened);
     assertFalse(main.advancedToggleExpanded);
 
     main.advancedToggleExpanded = true;
     flush();
 
-    assertFalse(!!ui.shadowRoot.querySelector('cr-drawer settings-menu'));
+    assertFalse(!!ui.shadowRoot!.querySelector('cr-drawer settings-menu'));
     assertTrue(ui.getAdvancedOpenedInMainForTest());
     assertTrue(ui.getAdvancedOpenedInMenuForTest());
-    assertTrue(floatingMenu.advancedOpened);
+    assertTrue(floatingMenu!.advancedOpened);
     assertTrue(main.advancedToggleExpanded);
 
-    ui.shadowRoot.querySelector('#drawerTemplate').if = true;
+    ui.$.drawerTemplate.if = true;
     flush();
 
-    const drawerMenu = ui.shadowRoot.querySelector('cr-drawer settings-menu');
+    const drawerMenu = ui.$.drawer.querySelector('settings-menu');
     assertTrue(!!drawerMenu);
-    assertTrue(floatingMenu.advancedOpened);
-    assertTrue(drawerMenu.advancedOpened);
+    assertTrue(floatingMenu!.advancedOpened);
+    assertTrue(drawerMenu!.advancedOpened);
 
     // Collapse 'Advanced' in the menu
-    drawerMenu.shadowRoot.querySelector('#advancedButton').click();
+    drawerMenu!.shadowRoot!.querySelector<HTMLElement>(
+                               '#advancedButton')!.click();
     flush();
 
     // Collapsing it in the menu should not collapse it in the main area
-    assertFalse(drawerMenu.advancedOpened);
-    assertFalse(floatingMenu.advancedOpened);
+    assertFalse(drawerMenu!.advancedOpened);
+    assertFalse(floatingMenu!.advancedOpened);
     assertFalse(ui.getAdvancedOpenedInMenuForTest());
     assertTrue(main.advancedToggleExpanded);
     assertTrue(ui.getAdvancedOpenedInMainForTest());
 
     // Expand both 'Advanced's again
-    drawerMenu.shadowRoot.querySelector('#advancedButton').click();
+    drawerMenu!.shadowRoot!.querySelector<HTMLElement>(
+                               '#advancedButton')!.click();
 
     // Collapse 'Advanced' in the main area
     main.advancedToggleExpanded = false;
@@ -144,34 +134,25 @@
 
     // Collapsing it in the main area should not collapse it in the menu
     assertFalse(ui.getAdvancedOpenedInMainForTest());
-    assertTrue(drawerMenu.advancedOpened);
-    assertTrue(floatingMenu.advancedOpened);
+    assertTrue(drawerMenu!.advancedOpened);
+    assertTrue(floatingMenu!.advancedOpened);
     assertTrue(ui.getAdvancedOpenedInMenuForTest());
   });
 });
 
 suite('SettingsUISearch', function() {
-  /** @type {!SettingsUiElement} */
-  let ui;
-
-  /** @type {!CrToolbarElement} */
-  let toolbar;
-
-  /** @type {!CrToolbarSearchFieldElement} */
-  let searchField;
+  let ui: SettingsUiElement;
+  let toolbar: CrToolbarElement;
+  let searchField: CrToolbarSearchFieldElement;
 
   setup(function() {
     document.body.innerHTML = '';
-    ui = /** @type {!SettingsUiElement} */ (
-        document.createElement('settings-ui'));
+    ui = document.createElement('settings-ui');
     document.body.appendChild(ui);
     return CrSettingsPrefs.initialized.then(() => {
       flush();
-      toolbar = /** @type {!CrToolbarElement} */ (
-          ui.shadowRoot.querySelector('cr-toolbar'));
-      searchField =
-          /** @type {!CrToolbarSearchFieldElement} */ (
-              toolbar.getSearchField());
+      toolbar = ui.$.toolbar;
+      searchField = toolbar.getSearchField();
     });
   });
 
@@ -186,7 +167,7 @@
 
   test('search box initiated search propagates to URL', function() {
     Router.getInstance().navigateTo(
-        routes.BASIC, /* dynamicParams */ null,
+        routes.BASIC, /* dynamicParams */ undefined,
         /* removeSearch */ true);
     assertEquals('', searchField.getSearchInput().value);
     assertFalse(Router.getInstance().getQueryParameters().has('search'));
@@ -224,9 +205,8 @@
   test('MaintainsFocusOnMenus', async () => {
     // Start in non-narrow mode with focus in the left menu.
     toolbar.narrow = false;
-    ui.shadowRoot.querySelector('#leftMenu').focusFirstItem();
-    assertEquals(
-        ui.shadowRoot.querySelector('#leftMenu'), ui.shadowRoot.activeElement);
+    ui.$.leftMenu.focusFirstItem();
+    assertEquals(ui.$.leftMenu, ui.shadowRoot!.activeElement);
 
     // Switch to narrow mode and test that focus moves to menu button.
     toolbar.narrow = true;
@@ -237,7 +217,6 @@
     // Switch back to non-narrow mode and test that focus moves to left menu.
     toolbar.narrow = false;
     flush();
-    assertEquals(
-        ui.shadowRoot.querySelector('#leftMenu'), ui.shadowRoot.activeElement);
+    assertEquals(ui.$.leftMenu, ui.shadowRoot!.activeElement);
   });
 });
diff --git a/chrome/updater/app/app.h b/chrome/updater/app/app.h
index 800da19..5182062 100644
--- a/chrome/updater/app/app.h
+++ b/chrome/updater/app/app.h
@@ -45,10 +45,6 @@
   App();
   virtual ~App();
 
-  // Called on the main sequence while blocking is allowed and before
-  // shutting down the thread pool.
-  virtual void Uninitialize() {}
-
   // Triggers program shutdown. Must be called on the main sequence. The program
   // will exit with the specified code.
   void Shutdown(int exit_code);
@@ -65,6 +61,10 @@
   // sequence while blocking is still allowed.
   virtual void Initialize() {}
 
+  // Called on the main sequence while blocking is allowed and before
+  // shutting down the thread pool.
+  virtual void Uninitialize() {}
+
   // Concrete implementations of App can execute their first task in this
   // method. It is called on the main sequence. Blocking is not allowed. It may
   // call Shutdown.
diff --git a/chrome/updater/app/app_uninstall.cc b/chrome/updater/app/app_uninstall.cc
index 52c3ac0..396a8a7 100644
--- a/chrome/updater/app/app_uninstall.cc
+++ b/chrome/updater/app/app_uninstall.cc
@@ -18,6 +18,7 @@
 #include "base/process/launch.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/updater/app/app.h"
 #include "chrome/updater/app/app_utils.h"
@@ -83,6 +84,7 @@
  private:
   ~AppUninstall() override = default;
   void Initialize() override;
+  void Uninitialize() override;
   void FirstTaskRun() override;
 
   // Conditionally set, if prefs must be acquired for some uninstall scenarios.
@@ -98,6 +100,10 @@
     global_prefs_ = CreateGlobalPrefs(updater_scope());
 }
 
+void AppUninstall::Uninitialize() {
+  global_prefs_ = nullptr;
+}
+
 void AppUninstall::FirstTaskRun() {
   const base::CommandLine* command_line =
       base::CommandLine::ForCurrentProcess();
@@ -113,6 +119,7 @@
             },
             updater_scope()),
         base::BindOnce(&AppUninstall::Shutdown, this));
+    return;
   }
 
   if (command_line->HasSwitch(kUninstallSelfSwitch)) {
@@ -126,22 +133,24 @@
 
   if (command_line->HasSwitch(kUninstallIfUnusedSwitch)) {
     CHECK(global_prefs_);
-    if (ShouldUninstall(
-            base::MakeRefCounted<PersistedData>(global_prefs_->GetPrefService())
-                ->GetAppIds(),
-            global_prefs_->CountServerStarts())) {
+    const bool should_uninstall = ShouldUninstall(
+        base::MakeRefCounted<PersistedData>(global_prefs_->GetPrefService())
+            ->GetAppIds(),
+        global_prefs_->CountServerStarts());
+    VLOG(1) << "ShouldUninstall returned: " << should_uninstall;
+    if (should_uninstall) {
       base::ThreadPool::PostTaskAndReplyWithResult(
           FROM_HERE, {base::MayBlock()},
           base::BindOnce(&Uninstall, updater_scope()),
-          base::BindOnce(
-              [](base::OnceCallback<void(int)> shutdown, int exit_code) {
-                // global_prefs is captured so that this process holds the prefs
-                // lock through uninstallation.
-                std::move(shutdown).Run(exit_code);
-              },
-              base::BindOnce(&AppUninstall::Shutdown, this)));
+          base::BindOnce(&AppUninstall::Shutdown, this));
+    } else {
+      base::SequencedTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&AppUninstall::Shutdown, this, 0));
     }
+    return;
   }
+
+  NOTREACHED();
 }
 
 scoped_refptr<App> MakeAppUninstall() {
diff --git a/chrome/updater/configurator.cc b/chrome/updater/configurator.cc
index 24d194c..d16b16b 100644
--- a/chrome/updater/configurator.cc
+++ b/chrome/updater/configurator.cc
@@ -111,7 +111,7 @@
 
 base::flat_map<std::string, std::string> Configurator::ExtraRequestParams()
     const {
-  return {{"testrequest", "1"}, {"testsource", "dev"}};
+  return {};
 }
 
 std::string Configurator::GetDownloadPreference() const {
diff --git a/chrome/updater/mac/BUILD.gn b/chrome/updater/mac/BUILD.gn
index cc1254dd..8d298bf 100644
--- a/chrome/updater/mac/BUILD.gn
+++ b/chrome/updater/mac/BUILD.gn
@@ -31,6 +31,8 @@
 
   sources = [ "main.cc" ]
   deps = [
+    ":keystone_agent_bundle_resource_executable",
+    ":keystone_agent_bundle_resource_plist",
     ":updater_bundle_keystone_executable",
     ":updater_bundle_keystone_helpers",
     ":updater_bundle_keystone_plist",
@@ -47,6 +49,8 @@
 
   sources = [ "main.cc" ]
   deps = [
+    ":keystone_agent_bundle_resource_executable",
+    ":keystone_agent_bundle_resource_plist",
     ":updater_bundle_keystone_executable",
     ":updater_bundle_keystone_helpers",
     ":updater_bundle_keystone_plist",
@@ -192,12 +196,40 @@
   ]
 }
 
+mac_app_bundle("keystone_agent_bundle") {
+  info_plist = "keystone/Info.plist"
+  sources = [ "keystone/agent_main.cc" ]
+  output_name = keystone_app_name + "Agent"
+  extra_substitutions = [
+    "MAC_BUNDLE_IDENTIFIER=${keystone_app_name}Agent",
+    "PACKAGE_TYPE=APPL",
+  ]
+}
+
+bundle_data("keystone_agent_bundle_resource_executable") {
+  sources = [ "$root_out_dir/${keystone_app_name}Agent.app/Contents/MacOS/${keystone_app_name}Agent" ]
+
+  outputs = [ "{{bundle_contents_dir}}/Helpers/$keystone_app_name.bundle/Contents/Resources/${keystone_app_name}Agent.app/Contents/MacOS/{{source_file_part}}" ]
+  public_deps = [ ":keystone_agent_bundle" ]
+}
+
+bundle_data("keystone_agent_bundle_resource_plist") {
+  sources =
+      [ "$root_out_dir/${keystone_app_name}Agent.app/Contents/Info.plist" ]
+
+  outputs = [ "{{bundle_contents_dir}}/Helpers/$keystone_app_name.bundle/Contents/Resources/${keystone_app_name}Agent.app/Contents/MacOS/{{source_file_part}}" ]
+  public_deps = [ ":keystone_agent_bundle" ]
+}
+
 mac_app_bundle("keystone_bundle") {
   info_plist = "keystone/Info.plist"
   sources = [ "keystone/keystone_main.cc" ]
   output_name = keystone_app_name
   package_type = "bundle"
-  extra_substitutions = [ "MAC_BUNDLE_IDENTIFIER=$keystone_app_name" ]
+  extra_substitutions = [
+    "MAC_BUNDLE_IDENTIFIER=$keystone_app_name",
+    "PACKAGE_TYPE=BNDL",
+  ]
   public_deps = [ ":keystone_helpers" ]
 }
 
diff --git a/chrome/updater/mac/keystone/Info.plist b/chrome/updater/mac/keystone/Info.plist
index 7ce696b..32c7f51 100644
--- a/chrome/updater/mac/keystone/Info.plist
+++ b/chrome/updater/mac/keystone/Info.plist
@@ -13,7 +13,7 @@
 	<key>CFBundleName</key>
 	<string>${PRODUCT_NAME}</string>
 	<key>CFBundlePackageType</key>
-	<string>BNDL</string>
+	<string>${PACKAGE_TYPE}</string>
 	<key>CFBundleVersion</key>
 	<string>2.0</string>
 	<key>LSUIElement</key>
diff --git a/chrome/updater/mac/keystone/agent_main.cc b/chrome/updater/mac/keystone/agent_main.cc
new file mode 100644
index 0000000..099d574
--- /dev/null
+++ b/chrome/updater/mac/keystone/agent_main.cc
@@ -0,0 +1,6 @@
+// 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.
+
+// The agent is a shim to trick the keystone registration framework.
+int main() {}
diff --git a/chrome/updater/mac/keystone/ksadmin.mm b/chrome/updater/mac/keystone/ksadmin.mm
index 312367e..8009361 100644
--- a/chrome/updater/mac/keystone/ksadmin.mm
+++ b/chrome/updater/mac/keystone/ksadmin.mm
@@ -21,6 +21,7 @@
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/message_loop/message_pump_type.h"
+#include "base/path_service.h"
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/task/single_thread_task_executor.h"
@@ -52,7 +53,6 @@
 constexpr char kCommandSystemStore[] = "system-store";
 constexpr char kCommandUserInitiated[] = "user-initiated";
 constexpr char kCommandUserStore[] = "user-store";
-constexpr char kCommandStorePath[] = "store";
 constexpr char kCommandBrandKey[] = "brand-key";
 constexpr char kCommandBrandPath[] = "brand-path";
 constexpr char kCommandProductId[] = "productid";
@@ -127,7 +127,6 @@
       {kCommandTagKey, "K"},      {kCommandTagPath, "H"},
       {kCommandVersion, "v"},     {kCommandVersionKey, "e"},
       {kCommandVersionPath, "a"}, {kCommandXCPath, "x"},
-      {kCommandStorePath, {"s"}},
   };
   if (!base::Contains(aliases, arg))
     return "";
@@ -139,7 +138,7 @@
   return GetKeystoneFolderPath(scope)
       ->Append(FILE_PATH_LITERAL("TicketStore"))
       .Append(FILE_PATH_LITERAL("Keystone.ticketstore"))
-      .AsUTF8Unsafe();
+      .value();
 }
 
 UpdaterScope Scope(const base::flat_map<std::string, std::string>& switches) {
@@ -148,8 +147,12 @@
   if (HasSwitch(kCommandUserStore, switches))
     return UpdaterScope::kUser;
 
-  return SwitchValue(kCommandStorePath, switches) ==
-                 KeystoneTicketStorePath(UpdaterScope::kSystem)
+  base::FilePath executable_path;
+  if (!base::PathService::Get(base::FILE_EXE, &executable_path))
+    return UpdaterScope::kUser;
+
+  return base::StartsWith(executable_path.value(),
+                          GetKeystoneFolderPath(UpdaterScope::kSystem)->value())
              ? UpdaterScope::kSystem
              : UpdaterScope::kUser;
 }
@@ -173,7 +176,7 @@
   void PrintUsage(const std::string& error_message);
   void PrintVersion();
   void PrintTickets();
-  void PrintKeystoneTickets();
+  void PrintKeystoneTickets(const std::string& app_id);
 
   UpdaterScope Scope() const;
   bool HasSwitch(const std::string& arg) const;
@@ -366,26 +369,43 @@
   Shutdown(0);
 }
 
-void KSAdminApp::PrintKeystoneTickets() {
+void KSAdminApp::PrintKeystoneTickets(const std::string& app_id) {
+  // Print all tickets if `app_id` is empty. Otherwise only print ticket for
+  // the given app id.
   @autoreleasepool {
     NSDictionary<NSString*, KSTicket*>* store = LoadTicketStore();
-    if (store.count > 0) {
-      for (NSString* key in store) {
-        printf("%s\n",
-               base::SysNSStringToUTF8([store[key] description]).c_str());
+    if (app_id.empty()) {
+      if (store.count > 0) {
+        for (NSString* key in store) {
+          printf("%s\n",
+                 base::SysNSStringToUTF8([store[key] description]).c_str());
+        }
+        return;
       }
     } else {
-      printf("No tickets\n");
+      KSTicket* ticket = [store
+          objectForKey:[base::SysUTF8ToNSString(app_id) lowercaseString]];
+      if (ticket) {
+        printf("%s\n", base::SysNSStringToUTF8([ticket description]).c_str());
+        return;
+      }
     }
+
+    printf("No tickets.\n");
   }
 }
 
 void KSAdminApp::PrintTickets() {
+  const std::string app_id = SwitchValue(kCommandProductId);
   service_proxy_->GetAppStates(base::BindOnce(
-      [](base::OnceCallback<void()> fallback_cb,
+      [](const std::string& app_id, base::OnceCallback<void()> fallback_cb,
          base::OnceCallback<void(int)> done_cb,
          const std::vector<updater::UpdateService::AppState>& states) {
         for (const updater::UpdateService::AppState& state : states) {
+          if (!app_id.empty() &&
+              !base::EqualsCaseInsensitiveASCII(app_id, state.app_id)) {
+            continue;
+          }
           KSTicket* ticket =
               [[[KSTicket alloc] initWithAppState:state] autorelease];
           printf("%s\n", base::SysNSStringToUTF8([ticket description]).c_str());
@@ -398,7 +418,7 @@
         }
         std::move(done_cb).Run(0);
       },
-      base::BindOnce(&KSAdminApp::PrintKeystoneTickets, this),
+      app_id, base::BindOnce(&KSAdminApp::PrintKeystoneTickets, this, app_id),
       base::BindOnce(&KSAdminApp::Shutdown, this)));
 }
 
diff --git a/chrome/updater/persisted_data.cc b/chrome/updater/persisted_data.cc
index cd468c9..7a0ce36 100644
--- a/chrome/updater/persisted_data.cc
+++ b/chrome/updater/persisted_data.cc
@@ -151,7 +151,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!pref_service_)
     return nullptr;
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       pref_service_->GetDictionary(kPersistedDataPreference);
   if (!dict)
     return nullptr;
diff --git a/chrome/updater/test/integration_tests.cc b/chrome/updater/test/integration_tests.cc
index f9fbc75b..7bf75d3d 100644
--- a/chrome/updater/test/integration_tests.cc
+++ b/chrome/updater/test/integration_tests.cc
@@ -493,17 +493,22 @@
 TEST_F(IntegrationTest, UninstallCmdLine) {
   Install();
   ExpectInstalled();
+  ExpectVersionActive(kUpdaterVersion);
+  ExpectActiveUpdater();
+
+  // Running the uninstall command does not uninstall this instance of the
+  // updater right after installing it (not enough server starts).
+  RunUninstallCmdLine();
+  WaitForServerExit();
+  ExpectInstalled();
 
   // TODO(crbug.com/1270520) - use a switch that can uninstall immediately if
   // unused, instead of requiring server starts.
   SetServerStarts(24);
 
-  ExpectVersionActive(kUpdaterVersion);
-  ExpectActiveUpdater();
-
+  // Uninstall the idle updater.
   RunUninstallCmdLine();
   WaitForServerExit();
-  SleepFor(2);
   ExpectClean();
 }
 #endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
diff --git a/chromecast/ui/display_settings/gamma_configurator.cc b/chromecast/ui/display_settings/gamma_configurator.cc
index 88379b8..5d0c14a 100644
--- a/chromecast/ui/display_settings/gamma_configurator.cc
+++ b/chromecast/ui/display_settings/gamma_configurator.cc
@@ -65,6 +65,9 @@
 }
 
 void GammaConfigurator::SetColorInversion(bool invert) {
+  if (is_inverted_ == invert)
+    return;
+
   is_inverted_ = invert;
 
   if (is_initialized_)
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 134d4e45..37f54056 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-14401.0.0
+14408.0.0
\ No newline at end of file
diff --git a/chromeos/dbus/shill/shill_ipconfig_client.cc b/chromeos/dbus/shill/shill_ipconfig_client.cc
index 1d1bedd..1099840 100644
--- a/chromeos/dbus/shill/shill_ipconfig_client.cc
+++ b/chromeos/dbus/shill/shill_ipconfig_client.cc
@@ -108,13 +108,11 @@
   // IPConfig supports writing basic type and string array properties.
   switch (value.type()) {
     case base::Value::Type::LIST: {
-      const base::ListValue* list_value = nullptr;
-      value.GetAsList(&list_value);
       dbus::MessageWriter variant_writer(nullptr);
       writer.OpenVariant("as", &variant_writer);
       dbus::MessageWriter array_writer(nullptr);
       variant_writer.OpenArray("s", &array_writer);
-      for (const auto& entry : list_value->GetList()) {
+      for (const auto& entry : value.GetList()) {
         DLOG_IF(ERROR, !entry.is_string())
             << "Unexpected type " << entry.type();
         array_writer.AppendString(entry.is_string() ? entry.GetString()
diff --git a/chromeos/dbus/typecd/fake_typecd_client.cc b/chromeos/dbus/typecd/fake_typecd_client.cc
index d0dda69..d6e246d 100644
--- a/chromeos/dbus/typecd/fake_typecd_client.cc
+++ b/chromeos/dbus/typecd/fake_typecd_client.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chromeos/dbus/typecd/fake_typecd_client.h"
+#include "third_party/cros_system_api/dbus/typecd/dbus-constants.h"
 
 namespace chromeos {
 
@@ -14,4 +15,8 @@
   NotifyOnThunderboltDeviceConnected(is_thunderbolt_only);
 }
 
+void FakeTypecdClient::EmitCableWarningSignal(typecd::CableWarningType type) {
+  NotifyOnCableWarning(type);
+}
+
 }  // namespace chromeos
diff --git a/chromeos/dbus/typecd/fake_typecd_client.h b/chromeos/dbus/typecd/fake_typecd_client.h
index 311117b..026a953 100644
--- a/chromeos/dbus/typecd/fake_typecd_client.h
+++ b/chromeos/dbus/typecd/fake_typecd_client.h
@@ -7,6 +7,7 @@
 
 #include "base/component_export.h"
 #include "chromeos/dbus/typecd/typecd_client.h"
+#include "third_party/cros_system_api/dbus/typecd/dbus-constants.h"
 
 namespace chromeos {
 
@@ -20,6 +21,7 @@
   // This is a simple fake to notify observers of a simulated D-Bus received
   // signal.
   void EmitThunderboltDeviceConnectedSignal(bool is_thunderbolt_only);
+  void EmitCableWarningSignal(typecd::CableWarningType type);
 };
 
 }  // namespace chromeos
diff --git a/chromeos/dbus/typecd/typecd_client.cc b/chromeos/dbus/typecd/typecd_client.cc
index 2e0c09e..c3e177a1 100644
--- a/chromeos/dbus/typecd/typecd_client.cc
+++ b/chromeos/dbus/typecd/typecd_client.cc
@@ -30,6 +30,7 @@
 
  private:
   void ThunderboltDeviceConnectedReceived(dbus::Signal* signal);
+  void CableWarningReceived(dbus::Signal* signal);
   void OnSignalConnected(const std::string& interface_name,
                          const std::string& signal_name,
                          bool success);
@@ -47,7 +48,8 @@
   typedef void (TypecdClientImpl::*SignalMethod)(dbus::Signal*);
   const std::pair<const char*, SignalMethod> kSignalMethods[] = {
       {typecd::kTypecdDeviceConnected,
-       &TypecdClientImpl::ThunderboltDeviceConnectedReceived}};
+       &TypecdClientImpl::ThunderboltDeviceConnectedReceived},
+      {typecd::kTypecdCableWarning, &TypecdClientImpl::CableWarningReceived}};
 
   auto on_connected_callback = base::BindRepeating(
       &TypecdClientImpl::OnSignalConnected, weak_ptr_factory_.GetWeakPtr());
@@ -77,6 +79,21 @@
       static_cast<uint32_t>(typecd::DeviceConnectedType::kThunderboltOnly));
 }
 
+void TypecdClientImpl::CableWarningReceived(dbus::Signal* signal) {
+  dbus::MessageReader reader(signal);
+  uint32_t cable_warning_signal = 0u;
+  if (!reader.PopUint32(&cable_warning_signal)) {
+    LOG(ERROR) << "Typecd: Unable to decode cable warning type from"
+               << typecd::kTypecdCableWarning << " signal.";
+    return;
+  }
+  typecd::CableWarningType cable_warning_type =
+      static_cast<typecd::CableWarningType>(cable_warning_signal);
+  VLOG(1) << "Typecd: Received cable warning signal with "
+          << "CableWarningType: " << cable_warning_signal;
+  NotifyOnCableWarning(cable_warning_type);
+}
+
 void TypecdClientImpl::OnSignalConnected(const std::string& interface_name,
                                          const std::string& signal_name,
                                          bool success) {
@@ -102,6 +119,12 @@
     observer.OnThunderboltDeviceConnected(is_thunderbolt_only);
 }
 
+void TypecdClient::NotifyOnCableWarning(
+    typecd::CableWarningType cable_warning_type) {
+  for (auto& observer : observer_list_)
+    observer.OnCableWarning(cable_warning_type);
+}
+
 TypecdClient::TypecdClient() {
   CHECK(!g_instance);
   g_instance = this;
diff --git a/chromeos/dbus/typecd/typecd_client.h b/chromeos/dbus/typecd/typecd_client.h
index 44aeedd..7f23369 100644
--- a/chromeos/dbus/typecd/typecd_client.h
+++ b/chromeos/dbus/typecd/typecd_client.h
@@ -8,6 +8,7 @@
 #include "base/component_export.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
+#include "third_party/cros_system_api/dbus/typecd/dbus-constants.h"
 
 namespace dbus {
 class Bus;
@@ -24,6 +25,8 @@
    public:
     ~Observer() override = default;
     virtual void OnThunderboltDeviceConnected(bool is_thunderbolt_only) = 0;
+    virtual void OnCableWarning(
+        typecd::CableWarningType cable_warning_type) = 0;
   };
 
   void AddObserver(Observer* observer);
@@ -50,6 +53,7 @@
   virtual ~TypecdClient();
 
   void NotifyOnThunderboltDeviceConnected(bool is_thunderbolt_only);
+  void NotifyOnCableWarning(typecd::CableWarningType cable_warning_type);
 
  private:
   base::ObserverList<Observer> observer_list_;
diff --git a/chromeos/network/cellular_esim_profile_handler_impl.cc b/chromeos/network/cellular_esim_profile_handler_impl.cc
index 33807fb..4147398 100644
--- a/chromeos/network/cellular_esim_profile_handler_impl.cc
+++ b/chromeos/network/cellular_esim_profile_handler_impl.cc
@@ -71,7 +71,7 @@
   if (!device_prefs_)
     return std::vector<CellularESimProfile>();
 
-  const base::ListValue* profiles_list =
+  const base::Value* profiles_list =
       device_prefs_->GetList(prefs::kESimProfiles);
   if (!profiles_list) {
     NET_LOG(ERROR) << "eSIM profiles pref is not a list";
@@ -193,7 +193,7 @@
 CellularESimProfileHandlerImpl::GetAutoRefreshedEuiccPathsFromPrefs() const {
   DCHECK(device_prefs_);
 
-  const base::ListValue* euicc_paths_from_prefs =
+  const base::Value* euicc_paths_from_prefs =
       device_prefs_->GetList(prefs::kESimRefreshedEuiccs);
   if (!euicc_paths_from_prefs) {
     NET_LOG(ERROR) << "Could not fetch refreshed EUICCs pref.";
diff --git a/chromeos/network/cellular_esim_uninstall_handler.cc b/chromeos/network/cellular_esim_uninstall_handler.cc
index af3179a..82fe994 100644
--- a/chromeos/network/cellular_esim_uninstall_handler.cc
+++ b/chromeos/network/cellular_esim_uninstall_handler.cc
@@ -22,22 +22,6 @@
 
 namespace chromeos {
 
-namespace {
-
-void OnRemoveStaleShillService(std::string service_path, bool success) {
-  if (success) {
-    NET_LOG(EVENT)
-        << "Successfully removed stale Shill eSIM configuration. service_path="
-        << service_path;
-  } else {
-    NET_LOG(ERROR)
-        << "Error removing stale Shill eSIM configuration. service_path="
-        << service_path;
-  }
-}
-
-}  // namespace
-
 CellularESimUninstallHandler::UninstallRequest::UninstallRequest(
     const std::string& iccid,
     const absl::optional<dbus::ObjectPath>& esim_profile_path,
@@ -50,10 +34,7 @@
 CellularESimUninstallHandler::UninstallRequest::~UninstallRequest() = default;
 
 CellularESimUninstallHandler::CellularESimUninstallHandler() = default;
-CellularESimUninstallHandler::~CellularESimUninstallHandler() {
-  cellular_esim_profile_handler_->RemoveObserver(this);
-  network_state_handler_->RemoveObserver(this, FROM_HERE);
-}
+CellularESimUninstallHandler::~CellularESimUninstallHandler() = default;
 
 void CellularESimUninstallHandler::Init(
     CellularInhibitor* cellular_inhibitor,
@@ -66,9 +47,6 @@
   network_configuration_handler_ = network_configuration_handler;
   network_connection_handler_ = network_connection_handler;
   network_state_handler_ = network_state_handler;
-  network_state_handler_->AddObserver(this, FROM_HERE);
-  cellular_esim_profile_handler_->AddObserver(this);
-  CheckStaleESimServices();
 }
 
 void CellularESimUninstallHandler::UninstallESim(
@@ -81,19 +59,6 @@
   ProcessPendingUninstallRequests();
 }
 
-void CellularESimUninstallHandler::OnESimProfileListUpdated() {
-  CheckStaleESimServices();
-}
-
-void CellularESimUninstallHandler::NetworkListChanged() {
-  CheckStaleESimServices();
-}
-
-void CellularESimUninstallHandler::DevicePropertiesUpdated(
-    const DeviceState* device_state) {
-  CheckStaleESimServices();
-}
-
 void CellularESimUninstallHandler::ProcessPendingUninstallRequests() {
   // No requests to process.
   if (uninstall_requests_.empty())
@@ -362,54 +327,6 @@
   CompleteCurrentRequest(UninstallESimResult::kRemoveServiceFailed);
 }
 
-void CellularESimUninstallHandler::CheckStaleESimServices() {
-  // Find all known eSIM ICCIDs.
-  base::flat_set<std::string> esim_iccids;
-  std::vector<CellularESimProfile> esim_profiles =
-      cellular_esim_profile_handler_->GetESimProfiles();
-  for (const CellularESimProfile& esim_profile : esim_profiles) {
-    // Skip pending and installing profiles since they can never have
-    // a corresponding Shill service.
-    if (esim_profile.state() == CellularESimProfile::State::kPending ||
-        esim_profile.state() == CellularESimProfile::State::kInstalling) {
-      continue;
-    }
-
-    esim_iccids.insert(esim_profile.iccid());
-  }
-
-  for (const NetworkState* network_state : GetESimCellularNetworks()) {
-    if (esim_iccids.contains(network_state->iccid()))
-      continue;
-
-    // If we have not yet refreshed profiles for this EUICC, do not attempt to
-    // remove the service. This ensures that we don't accidentally remove
-    // services for eSIM profile which are in the process of being refreshed.
-    // See b/186753024 for details.
-    if (!cellular_esim_profile_handler_->HasRefreshedProfilesForEuicc(
-            network_state->eid())) {
-      NET_LOG(DEBUG) << "No eSIM profile for Shill service with ICCID "
-                     << network_state->iccid() << ". Not counted as stale "
-                     << "because we have not yet refreshed profiles from the "
-                     << "associated EUICC";
-      continue;
-    }
-
-    // Skip if an uninstall request is already queued for this service.
-    if (HasQueuedRequest(network_state->iccid()))
-      continue;
-
-    NET_LOG(EVENT) << "Queueing removal for stale shill config. iccid="
-                   << network_state->iccid() << ", "
-                   << "network path=" << network_state->path();
-    uninstall_requests_.push_back(std::make_unique<UninstallRequest>(
-        network_state->iccid(), /*esim_profile_path=*/absl::nullopt,
-        /*euicc_path=*/absl::nullopt,
-        base::BindOnce(&OnRemoveStaleShillService, network_state->path())));
-  }
-  ProcessPendingUninstallRequests();
-}
-
 NetworkStateHandler::NetworkStateList
 CellularESimUninstallHandler::GetESimCellularNetworks() const {
   NetworkStateHandler::NetworkStateList network_list;
diff --git a/chromeos/network/cellular_esim_uninstall_handler.h b/chromeos/network/cellular_esim_uninstall_handler.h
index 8a8b6d6..e805d803 100644
--- a/chromeos/network/cellular_esim_uninstall_handler.h
+++ b/chromeos/network/cellular_esim_uninstall_handler.h
@@ -40,20 +40,18 @@
 //
 // Uninstallation requests are queued and run in order.
 //
-// This class also checks for stale Shill eSIM services (Shill services that no
-// longer have corresponding eSIM profile in Hermes), and removes their
-// configurations from Shill. This allows handling cases where the configuration
-// was not removed properly during uninstallation or the eSIM profile was
-// removed externally (e.g. Removable EUICC card).
-class COMPONENT_EXPORT(CHROMEOS_NETWORK) CellularESimUninstallHandler
-    : public CellularESimProfileHandler::Observer,
-      public NetworkStateHandlerObserver {
+// Note: This class doesn't check and remove stale Shill eSIM services anymore
+// because it might remove usable eSIM services incorrectly. This may cause some
+// issues where some stale networks showing in UI because its Shill
+// configuration doesn't get removed properly during the uninstallation.
+// TODO(b/210726568)
+class COMPONENT_EXPORT(CHROMEOS_NETWORK) CellularESimUninstallHandler {
  public:
   CellularESimUninstallHandler();
   CellularESimUninstallHandler(const CellularESimUninstallHandler&) = delete;
   CellularESimUninstallHandler& operator=(const CellularESimUninstallHandler&) =
       delete;
-  ~CellularESimUninstallHandler() override;
+  ~CellularESimUninstallHandler();
 
   void Init(CellularInhibitor* cellular_inhibitor,
             CellularESimProfileHandler* cellular_esim_profile_handler,
@@ -73,13 +71,6 @@
                      const dbus::ObjectPath& euicc_path,
                      UninstallRequestCallback callback);
 
-  // CellularESimProfileHandler::Observer:
-  void OnESimProfileListUpdated() override;
-
-  // NetworkStateHandlerObserver:
-  void NetworkListChanged() override;
-  void DevicePropertiesUpdated(const DeviceState* device_state) override;
-
  private:
   friend class CellularESimUninstallHandlerTest;
   FRIEND_TEST_ALL_PREFIXES(CellularESimUninstallHandlerTest, Success);
@@ -90,8 +81,6 @@
   FRIEND_TEST_ALL_PREFIXES(CellularESimUninstallHandlerTest, MultipleRequests);
   FRIEND_TEST_ALL_PREFIXES(CellularESimUninstallHandlerTest,
                            StubCellularNetwork);
-  FRIEND_TEST_ALL_PREFIXES(CellularESimUninstallHandlerTest,
-                           RemovesShillOnlyServices);
 
   enum class UninstallState {
     kIdle,
@@ -171,8 +160,6 @@
       const std::string& error_name,
       std::unique_ptr<base::DictionaryValue> error_data);
 
-  void CheckStaleESimServices();
-
   NetworkStateHandler::NetworkStateList GetESimCellularNetworks() const;
   bool HasQueuedRequest(const std::string& iccid) const;
 
diff --git a/chromeos/network/cellular_esim_uninstall_handler_unittest.cc b/chromeos/network/cellular_esim_uninstall_handler_unittest.cc
index 9c408ba5..9c9fb3e 100644
--- a/chromeos/network/cellular_esim_uninstall_handler_unittest.cc
+++ b/chromeos/network/cellular_esim_uninstall_handler_unittest.cc
@@ -340,40 +340,4 @@
   ExpectResult(CellularESimUninstallHandler::UninstallESimResult::kSuccess);
 }
 
-TEST_F(CellularESimUninstallHandlerTest, RemovesShillOnlyServices) {
-  Init();
-  EXPECT_TRUE(ESimServiceConfigExists(kTestNetworkServicePath));
-  EXPECT_TRUE(ESimServiceConfigExists(kTestNetworkServicePath2));
-
-  // Start without having refreshed profiles.
-  SetHasRefreshedProfiles(/*has_refreshed=*/false);
-
-  // Remove first profile without removing service. Both services should still
-  // exist, since removal of stale services only occurs if the EUICC has been
-  // refreshed.
-  EXPECT_TRUE(
-      HermesEuiccClient::Get()->GetTestInterface()->RemoveCarrierProfile(
-          dbus::ObjectPath(kDefaultEuiccPath),
-          dbus::ObjectPath(kTestCarrierProfilePath)));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(ESimServiceConfigExists(kTestNetworkServicePath));
-  EXPECT_TRUE(ESimServiceConfigExists(kTestNetworkServicePath2));
-
-  // Finish refreshing profiles.
-  SetHasRefreshedProfiles(/*has_refreshed=*/true);
-
-  // Remove the second profile without removing service. Both services should
-  // have been detected as "stale" and should now be removed.
-  EXPECT_TRUE(
-      HermesEuiccClient::Get()->GetTestInterface()->RemoveCarrierProfile(
-          dbus::ObjectPath(kDefaultEuiccPath),
-          dbus::ObjectPath(kTestCarrierProfilePath2)));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(ESimServiceConfigExists(kTestNetworkServicePath));
-  EXPECT_FALSE(ESimServiceConfigExists(kTestNetworkServicePath2));
-
-  ExpectResult(CellularESimUninstallHandler::UninstallESimResult::kSuccess,
-               /*expected_count=*/2);
-}
-
 }  // namespace chromeos
diff --git a/chromeos/network/device_state.cc b/chromeos/network/device_state.cc
index 20ff33c..f2bd2c9 100644
--- a/chromeos/network/device_state.cc
+++ b/chromeos/network/device_state.cc
@@ -55,11 +55,11 @@
   } else if (key == shill::kTechnologyFamilyProperty) {
     return GetStringValue(key, value, &technology_family_);
   } else if (key == shill::kFoundNetworksProperty) {
-    const base::ListValue* list = nullptr;
-    if (!value.GetAsList(&list))
+    if (!value.is_list())
       return false;
     CellularScanResults parsed_results;
-    if (!network_util::ParseCellularScanResults(*list, &parsed_results))
+    if (!network_util::ParseCellularScanResults(value.GetList(),
+                                                &parsed_results))
       return false;
     scan_results_.swap(parsed_results);
     return true;
diff --git a/chromeos/network/network_metadata_store.cc b/chromeos/network/network_metadata_store.cc
index 9d61faf..ed9ded5 100644
--- a/chromeos/network/network_metadata_store.cc
+++ b/chromeos/network/network_metadata_store.cc
@@ -358,8 +358,7 @@
     return;
   }
 
-  const base::DictionaryValue* dict =
-      pref_service->GetDictionary(kNetworkMetadataPref);
+  const base::Value* dict = pref_service->GetDictionary(kNetworkMetadataPref);
   if (!dict || !dict->FindKey(network_guid)) {
     return;
   }
diff --git a/chromeos/network/network_profile_handler.cc b/chromeos/network/network_profile_handler.cc
index b95f6d8..817e0b6 100644
--- a/chromeos/network/network_profile_handler.cc
+++ b/chromeos/network/network_profile_handler.cc
@@ -24,9 +24,10 @@
 
 namespace {
 
-bool ConvertListValueToStringVector(const base::ListValue& string_list,
-                                    std::vector<std::string>* result) {
-  for (const base::Value& i : string_list.GetList()) {
+bool ConvertListValueToStringVector(
+    const base::Value::ConstListView string_list,
+    std::vector<std::string>* result) {
+  for (const base::Value& i : string_list) {
     const std::string* str = i.GetIfString();
     if (!str)
       return false;
@@ -102,13 +103,11 @@
   if (name != shill::kProfilesProperty)
     return;
 
-  const base::ListValue* profiles_value = NULL;
-  value.GetAsList(&profiles_value);
-  DCHECK(profiles_value);
+  DCHECK(value.is_list());
 
   std::vector<std::string> new_profile_paths;
-  bool result = ConvertListValueToStringVector(*profiles_value,
-                                               &new_profile_paths);
+  bool result =
+      ConvertListValueToStringVector(value.GetList(), &new_profile_paths);
   DCHECK(result);
 
   VLOG(2) << "Profiles: " << profiles_.size();
diff --git a/chromeos/network/network_util.cc b/chromeos/network/network_util.cc
index 47e7c47..6b48324 100644
--- a/chromeos/network/network_util.cc
+++ b/chromeos/network/network_util.cc
@@ -137,11 +137,11 @@
   return result;
 }
 
-bool ParseCellularScanResults(const base::ListValue& list,
+bool ParseCellularScanResults(const base::Value::ConstListView list,
                               std::vector<CellularScanResult>* scan_results) {
   scan_results->clear();
-  scan_results->reserve(list.GetList().size());
-  for (const auto& value : list.GetList()) {
+  scan_results->reserve(list.size());
+  for (const auto& value : list) {
     const base::DictionaryValue* dict;
     if (!value.GetAsDictionary(&dict))
       return false;
diff --git a/chromeos/network/network_util.h b/chromeos/network/network_util.h
index ececce2..d230b81 100644
--- a/chromeos/network/network_util.h
+++ b/chromeos/network/network_util.h
@@ -115,7 +115,7 @@
 // CellularScanResult in |scan_results|. Returns false if parsing fails,
 // in which case the contents of |scan_results| will be undefined.
 COMPONENT_EXPORT(CHROMEOS_NETWORK)
-bool ParseCellularScanResults(const base::ListValue& list,
+bool ParseCellularScanResults(const base::Value::ConstListView list,
                               std::vector<CellularScanResult>* scan_results);
 
 // Parses |list|, which contains DictionaryValues and returns a vector of
diff --git a/chromeos/network/network_util_unittest.cc b/chromeos/network/network_util_unittest.cc
index 9ddd857..26be09b 100644
--- a/chromeos/network/network_util_unittest.cc
+++ b/chromeos/network/network_util_unittest.cc
@@ -109,38 +109,38 @@
 }
 
 TEST_F(NetworkUtilTest, ParseScanResults) {
-  base::ListValue list;
+  base::Value list(base::Value::Type::LIST);
   std::vector<CellularScanResult> scan_results;
 
   // Empty list value.
-  EXPECT_TRUE(ParseCellularScanResults(list, &scan_results));
+  EXPECT_TRUE(ParseCellularScanResults(list.GetList(), &scan_results));
 
   // List contains invalid item.
   list.Append(0);
-  EXPECT_FALSE(ParseCellularScanResults(list, &scan_results));
+  EXPECT_FALSE(ParseCellularScanResults(list.GetList(), &scan_results));
 
   // Scan result has no network id.
   list.ClearList();
-  auto dict_value = std::make_unique<base::DictionaryValue>();
-  dict_value->SetString(shill::kStatusProperty, "available");
-  list.Append(std::move(dict_value));
-  EXPECT_TRUE(ParseCellularScanResults(list, &scan_results));
+  base::Value dict_value_1(base::Value::Type::DICTIONARY);
+  dict_value_1.SetStringKey(shill::kStatusProperty, "available");
+  list.Append(std::move(dict_value_1));
+  EXPECT_TRUE(ParseCellularScanResults(list.GetList(), &scan_results));
   EXPECT_TRUE(scan_results.empty());
 
   // Mixed parse results.
-  dict_value = std::make_unique<base::DictionaryValue>();
-  dict_value->SetString(shill::kNetworkIdProperty, "000001");
-  dict_value->SetString(shill::kStatusProperty, "unknown");
-  dict_value->SetString(shill::kTechnologyProperty, "GSM");
-  list.Append(std::move(dict_value));
+  base::Value dict_value_2(base::Value::Type::DICTIONARY);
+  dict_value_2.SetStringKey(shill::kNetworkIdProperty, "000001");
+  dict_value_2.SetStringKey(shill::kStatusProperty, "unknown");
+  dict_value_2.SetStringKey(shill::kTechnologyProperty, "GSM");
+  list.Append(std::move(dict_value_2));
 
-  dict_value = std::make_unique<base::DictionaryValue>();
-  dict_value->SetString(shill::kNetworkIdProperty, "000002");
-  dict_value->SetString(shill::kStatusProperty, "available");
-  dict_value->SetString(shill::kLongNameProperty, "Long Name");
-  list.Append(std::move(dict_value));
+  base::Value dict_value_3(base::Value::Type::DICTIONARY);
+  dict_value_3.SetStringKey(shill::kNetworkIdProperty, "000002");
+  dict_value_3.SetStringKey(shill::kStatusProperty, "available");
+  dict_value_3.SetStringKey(shill::kLongNameProperty, "Long Name");
+  list.Append(std::move(dict_value_3));
 
-  EXPECT_TRUE(ParseCellularScanResults(list, &scan_results));
+  EXPECT_TRUE(ParseCellularScanResults(list.GetList(), &scan_results));
   EXPECT_EQ(static_cast<size_t>(2), scan_results.size());
   EXPECT_EQ("000001", scan_results[0].network_id);
   EXPECT_EQ("unknown", scan_results[0].status);
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index d7fcbd4..d4fcc29 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-98-4744.1-1639393599-benchmark-98.0.4758.16-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-99-4744.1-1639392235-benchmark-99.0.4768.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index d5a48ff3..6b956df 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-98-4744.1-1639397847-benchmark-98.0.4758.16-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-99-4744.1-1639397500-benchmark-99.0.4768.0-r1-redacted.afdo.xz
diff --git a/chromeos/services/device_sync/cryptauth_device_manager_impl.cc b/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
index a660f9b..1d1a03b7 100644
--- a/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
@@ -756,8 +756,8 @@
 }
 
 void CryptAuthDeviceManagerImpl::UpdateUnlockKeysFromPrefs() {
-  const base::ListValue* unlock_key_list =
-      pref_service_->GetList(prefs::kCryptAuthDeviceSyncUnlockKeys);
+  const base::ListValue* unlock_key_list = &base::Value::AsListValue(
+      *pref_service_->GetList(prefs::kCryptAuthDeviceSyncUnlockKeys));
   synced_devices_.clear();
   for (size_t i = 0; i < unlock_key_list->GetList().size(); ++i) {
     const base::DictionaryValue* unlock_key_dictionary;
diff --git a/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
index ff408a0..1e75fba 100644
--- a/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
@@ -184,7 +184,7 @@
     const PrefService& pref_service) {
   ExpectSyncedDevicesAreEqual(expected_devices, devices);
 
-  const base::ListValue* synced_devices_pref =
+  const base::Value* synced_devices_pref =
       pref_service.GetList(prefs::kCryptAuthDeviceSyncUnlockKeys);
   ASSERT_EQ(expected_devices.size(), synced_devices_pref->GetList().size());
   for (size_t i = 0; i < synced_devices_pref->GetList().size(); ++i) {
diff --git a/chromeos/services/device_sync/cryptauth_key_registry_impl.cc b/chromeos/services/device_sync/cryptauth_key_registry_impl.cc
index 42723b48..a396125 100644
--- a/chromeos/services/device_sync/cryptauth_key_registry_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_key_registry_impl.cc
@@ -42,7 +42,7 @@
 
 CryptAuthKeyRegistryImpl::CryptAuthKeyRegistryImpl(PrefService* pref_service)
     : CryptAuthKeyRegistry(), pref_service_(pref_service) {
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       pref_service_->GetDictionary(prefs::kCryptAuthKeyRegistry);
 
   for (const CryptAuthKeyBundle::Name& name : CryptAuthKeyBundle::AllNames()) {
diff --git a/chromeos/services/device_sync/cryptauth_key_registry_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_key_registry_impl_unittest.cc
index 34782968..03bdc0bd 100644
--- a/chromeos/services/device_sync/cryptauth_key_registry_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_key_registry_impl_unittest.cc
@@ -33,7 +33,7 @@
 
   // Verify that changing the in-memory key bundle map updates the pref.
   void VerifyPrefValue(const base::Value& expected_dict) {
-    const base::DictionaryValue* dict =
+    const base::Value* dict =
         pref_service_.GetDictionary(prefs::kCryptAuthKeyRegistry);
     ASSERT_TRUE(dict);
     EXPECT_EQ(expected_dict, *dict);
diff --git a/chromeos/services/multidevice_setup/global_state_feature_manager_impl.cc b/chromeos/services/multidevice_setup/global_state_feature_manager_impl.cc
index a8b237d..bab69fb 100644
--- a/chromeos/services/multidevice_setup/global_state_feature_manager_impl.cc
+++ b/chromeos/services/multidevice_setup/global_state_feature_manager_impl.cc
@@ -14,6 +14,7 @@
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "chromeos/components/multidevice/logging/logging.h"
@@ -355,6 +356,11 @@
   }
 
   SetIsFeatureEnabled(true);
+
+  if (managed_feature_ == mojom::Feature::kPhoneHubCameraRoll) {
+    base::UmaHistogramEnumeration("PhoneHub.CameraRoll.OptInEntryPoint",
+                                  mojom::CameraRollOptInEntryPoint::kSetupFlow);
+  }
 }
 
 bool GlobalStateFeatureManagerImpl::ShouldAttemptToEnableAfterHostVerified() {
diff --git a/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom b/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom
index 96e2b78..151a263 100644
--- a/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom
+++ b/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom
@@ -7,6 +7,16 @@
 import "chromeos/components/multidevice/mojom/multidevice_types.mojom";
 import "chromeos/services/device_sync/public/mojom/device_sync.mojom";
 
+// Enumeration of possible opt-in entry points for Phone Hub Camera Roll
+// feature. This enum is tied directly to a UMA enum defined in
+// tools/metrics/histograms/enums.xml. These values are persisted to logs.
+// Entries should not be renumbered and numeric values should never be reused.
+enum CameraRollOptInEntryPoint {
+  kSetupFlow = 0,
+  kOnboardingDialog = 1,
+  kSettings = 2,
+};
+
 // Enumeration of event types which can be dispatched. Only used for debugging
 // purposes.
 enum EventTypeForDebugging {
diff --git a/chromeos/strings/chromeos_strings_ar.xtb b/chromeos/strings/chromeos_strings_ar.xtb
index 01ebbbc..34ee5e3 100644
--- a/chromeos/strings/chromeos_strings_ar.xtb
+++ b/chromeos/strings/chromeos_strings_ar.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">‏ميزات نظام Chrome الجديدة</translation>
 <translation id="2619761439309613843">إعادة التحميل يوميًا</translation>
 <translation id="2620436844016719705">النظام</translation>
+<translation id="2695514675076754339">إدخال رمز فتح القفل المكوّن من 8 أحرف</translation>
 <translation id="2740531572673183784">حسنًا</translation>
 <translation id="2751739896257479635">‏مصادقة المرحلة الثانية عبر EAP</translation>
 <translation id="2783010256799387990">تم الاجتياز</translation>
diff --git a/chromeos/strings/chromeos_strings_bn.xtb b/chromeos/strings/chromeos_strings_bn.xtb
index 761aaa7..dc0f851 100644
--- a/chromeos/strings/chromeos_strings_bn.xtb
+++ b/chromeos/strings/chromeos_strings_bn.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">Chrome OS-এ নতুন কী আছে</translation>
 <translation id="2619761439309613843">প্রতিদিন রিফ্রেশ করা হবে</translation>
 <translation id="2620436844016719705">সিস্টেম</translation>
+<translation id="2695514675076754339">৮-অক্ষরের আনলক কোড লিখুন</translation>
 <translation id="2740531572673183784">ঠিক আছে</translation>
 <translation id="2751739896257479635">EAP ফেজ ২ যাচাইকরণ</translation>
 <translation id="2783010256799387990">পাস করেছে</translation>
diff --git a/chromeos/strings/chromeos_strings_cs.xtb b/chromeos/strings/chromeos_strings_cs.xtb
index 7d36b87a..6ac5d64 100644
--- a/chromeos/strings/chromeos_strings_cs.xtb
+++ b/chromeos/strings/chromeos_strings_cs.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">Novinky v Chrome OS</translation>
 <translation id="2619761439309613843">Denní obnovení</translation>
 <translation id="2620436844016719705">Systém</translation>
+<translation id="2695514675076754339">Zadejte osmiznakový odemykací kód</translation>
 <translation id="2740531572673183784">OK</translation>
 <translation id="2751739896257479635">Ověření EAP Phase 2</translation>
 <translation id="2783010256799387990">V POŘÁDKU</translation>
diff --git a/chromeos/strings/chromeos_strings_fa.xtb b/chromeos/strings/chromeos_strings_fa.xtb
index 3445c79..7fa26fe4 100644
--- a/chromeos/strings/chromeos_strings_fa.xtb
+++ b/chromeos/strings/chromeos_strings_fa.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">‏ویژگی‌های جدید Chrome OS</translation>
 <translation id="2619761439309613843">بازخوانی روزانه</translation>
 <translation id="2620436844016719705">سیستم</translation>
+<translation id="2695514675076754339">کد قفل‌گشایی ۸ رقمی را وارد کنید</translation>
 <translation id="2740531572673183784">تأیید</translation>
 <translation id="2751739896257479635">‏اصالت‌سنجی مرحله ۲ EAP</translation>
 <translation id="2783010256799387990">موفق</translation>
diff --git a/chromeos/strings/chromeos_strings_gu.xtb b/chromeos/strings/chromeos_strings_gu.xtb
index 30226f0b..0c6ae0c 100644
--- a/chromeos/strings/chromeos_strings_gu.xtb
+++ b/chromeos/strings/chromeos_strings_gu.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">Chrome OSમાં નવું શું છે</translation>
 <translation id="2619761439309613843">દરરોજ રિફ્રેશ કરો</translation>
 <translation id="2620436844016719705">સિસ્ટમ</translation>
+<translation id="2695514675076754339">8 અક્ષરનો અનલૉક કોડ દાખલ કરો</translation>
 <translation id="2740531572673183784">બરાબર, સમજાઇ ગયું</translation>
 <translation id="2751739896257479635">EAP તબક્કા 2 માટેનું પ્રમાણીકરણ</translation>
 <translation id="2783010256799387990">પરીક્ષણ પાસ કર્યુ</translation>
diff --git a/chromeos/strings/chromeos_strings_hy.xtb b/chromeos/strings/chromeos_strings_hy.xtb
index df63b92..5266e293 100644
--- a/chromeos/strings/chromeos_strings_hy.xtb
+++ b/chromeos/strings/chromeos_strings_hy.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">Ինչ է փոխվել Chrome OS-ում</translation>
 <translation id="2619761439309613843">Ամենօրյա թարմացում</translation>
 <translation id="2620436844016719705">Համակարգ</translation>
+<translation id="2695514675076754339">Մուտքագրեք ապակողպման 8 նիշանոց կոդը</translation>
 <translation id="2740531572673183784">Եղավ</translation>
 <translation id="2751739896257479635">EAP նույնականացման փուլ 2</translation>
 <translation id="2783010256799387990">ԱՆՑԱԾ Է</translation>
diff --git a/chromeos/strings/chromeos_strings_lo.xtb b/chromeos/strings/chromeos_strings_lo.xtb
index 9297b37..efb0571 100644
--- a/chromeos/strings/chromeos_strings_lo.xtb
+++ b/chromeos/strings/chromeos_strings_lo.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">ມີຫຍັງໃໝ່ກັບ Chrome OS</translation>
 <translation id="2619761439309613843">ການໂຫຼດຂໍ້ມູນໃໝ່ປະຈຳວັນ</translation>
 <translation id="2620436844016719705">ລະ​ບົບ</translation>
+<translation id="2695514675076754339">ໃສ່ລະຫັດປົດລັອກ 8 ຕົວອັກສອນ</translation>
 <translation id="2740531572673183784">ຕົກລົງ</translation>
 <translation id="2751739896257479635">ການຮັບຮອງຄວາມຖືກຕ້ອງ EAP ໄລຍະທີ 2</translation>
 <translation id="2783010256799387990">ຜ່ານແລ້ວ</translation>
diff --git a/chromeos/strings/chromeos_strings_mr.xtb b/chromeos/strings/chromeos_strings_mr.xtb
index 87390667..e5c7752 100644
--- a/chromeos/strings/chromeos_strings_mr.xtb
+++ b/chromeos/strings/chromeos_strings_mr.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">Chrome OS मध्ये नवीन</translation>
 <translation id="2619761439309613843">दैनिक रिफ्रेश</translation>
 <translation id="2620436844016719705">सिस्टम</translation>
+<translation id="2695514675076754339">८ वर्णांचा अनलॉक कोड एंटर करा</translation>
 <translation id="2740531572673183784">ठीक आहे</translation>
 <translation id="2751739896257479635">EAP टप्पा 2 ऑथेंटिकेशन</translation>
 <translation id="2783010256799387990">पास झाले</translation>
diff --git a/chromeos/strings/chromeos_strings_ms.xtb b/chromeos/strings/chromeos_strings_ms.xtb
index 388bfa8..1b1d584 100644
--- a/chromeos/strings/chromeos_strings_ms.xtb
+++ b/chromeos/strings/chromeos_strings_ms.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">Item Baharu OS Chrome</translation>
 <translation id="2619761439309613843">Muat Semula Harian</translation>
 <translation id="2620436844016719705">Sistem</translation>
+<translation id="2695514675076754339">Masukkan kod buka kunci 8 aksara</translation>
 <translation id="2740531572673183784">Ok</translation>
 <translation id="2751739896257479635">Pengesahan Fasa 2 EAP</translation>
 <translation id="2783010256799387990">LULUS</translation>
diff --git a/chromeos/strings/chromeos_strings_ne.xtb b/chromeos/strings/chromeos_strings_ne.xtb
index a5b3ec1..71acb0e 100644
--- a/chromeos/strings/chromeos_strings_ne.xtb
+++ b/chromeos/strings/chromeos_strings_ne.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">Chrome OS मा नयाँ के छ?</translation>
 <translation id="2619761439309613843">दैनिक पुनः ताजा गर्नुहोस्</translation>
 <translation id="2620436844016719705">सिस्टम</translation>
+<translation id="2695514675076754339">८ वर्णको अनलक कोड हाल्नुहोस्</translation>
 <translation id="2740531572673183784">ठिक छ</translation>
 <translation id="2751739896257479635">EAP दोस्रो चरणको प्रमाणीकरण</translation>
 <translation id="2783010256799387990">पास भयो</translation>
diff --git a/chromeos/strings/chromeos_strings_pa.xtb b/chromeos/strings/chromeos_strings_pa.xtb
index c56a2a5..f40d08e 100644
--- a/chromeos/strings/chromeos_strings_pa.xtb
+++ b/chromeos/strings/chromeos_strings_pa.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">Chrome OS 'ਚ ਨਵਾਂ ਕੀ ਹੈ</translation>
 <translation id="2619761439309613843">ਰੋਜ਼ਾਨਾ ਰਿਫ੍ਰੈਸ਼ ਕਰੋ</translation>
 <translation id="2620436844016719705">ਸਿਸਟਮ</translation>
+<translation id="2695514675076754339">8-ਅੱਖਰ-ਚਿੰਨ੍ਹਾਂ ਦਾ ਅਣਲਾਕ ਕੋਡ ਦਾਖਲ ਕਰੋ</translation>
 <translation id="2740531572673183784">ਠੀਕ</translation>
 <translation id="2751739896257479635">EAP ਫੇਜ਼ 2 ਪ੍ਰਮਾਣੀਕਰਨ</translation>
 <translation id="2783010256799387990">ਪਾਸ ਕੀਤਾ</translation>
diff --git a/chromeos/strings/chromeos_strings_sk.xtb b/chromeos/strings/chromeos_strings_sk.xtb
index b71ca5b5..2ccc86f 100644
--- a/chromeos/strings/chromeos_strings_sk.xtb
+++ b/chromeos/strings/chromeos_strings_sk.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">Chrome OS – čo je nové</translation>
 <translation id="2619761439309613843">Obnovovať denne</translation>
 <translation id="2620436844016719705">Systém</translation>
+<translation id="2695514675076754339">Zadajte odomykací kód s ôsmimi znakmi</translation>
 <translation id="2740531572673183784">OK</translation>
 <translation id="2751739896257479635">Overenie totožnosti EAP – 2. fáza:</translation>
 <translation id="2783010256799387990">ÚSPEŠNÉ</translation>
diff --git a/chromeos/strings/chromeos_strings_sw.xtb b/chromeos/strings/chromeos_strings_sw.xtb
index f4d10c8..6b740a8 100644
--- a/chromeos/strings/chromeos_strings_sw.xtb
+++ b/chromeos/strings/chromeos_strings_sw.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">Mapya katika Chrome OS</translation>
 <translation id="2619761439309613843">Onyesha Upya Kila Siku</translation>
 <translation id="2620436844016719705">Mfumo</translation>
+<translation id="2695514675076754339">Weka msimbo wa kufungua wenye herufi nane</translation>
 <translation id="2740531572673183784">Sawa</translation>
 <translation id="2751739896257479635">Uthibitisho wa EAP wa awamu ya pili</translation>
 <translation id="2783010256799387990">LIMEFAULU</translation>
diff --git a/chromeos/strings/chromeos_strings_vi.xtb b/chromeos/strings/chromeos_strings_vi.xtb
index 421c68f..dc7d5df5 100644
--- a/chromeos/strings/chromeos_strings_vi.xtb
+++ b/chromeos/strings/chromeos_strings_vi.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">Chrome OS có gì mới</translation>
 <translation id="2619761439309613843">Làm mới hàng ngày</translation>
 <translation id="2620436844016719705">Hệ thống</translation>
+<translation id="2695514675076754339">Nhập mã mở khoá gồm 8 ký tự</translation>
 <translation id="2740531572673183784">Ok</translation>
 <translation id="2751739896257479635">Xác thực EAP giai đoạn 2</translation>
 <translation id="2783010256799387990">ĐÃ ĐẠT</translation>
diff --git a/chromeos/strings/chromeos_strings_zu.xtb b/chromeos/strings/chromeos_strings_zu.xtb
index a86908b..9fbe6488 100644
--- a/chromeos/strings/chromeos_strings_zu.xtb
+++ b/chromeos/strings/chromeos_strings_zu.xtb
@@ -117,6 +117,7 @@
 <translation id="2585245331261708204">Yini entsha nge-Chrome OS</translation>
 <translation id="2619761439309613843">Ukuvuselela kwansuku zonke</translation>
 <translation id="2620436844016719705">Isistimu</translation>
+<translation id="2695514675076754339">Faka ikhodi yokuvula enezinhlamvu eziyi-8</translation>
 <translation id="2740531572673183784">Ok</translation>
 <translation id="2751739896257479635">Ukufakazela ubuqiniso kabili kwesigaba se-EAP</translation>
 <translation id="2783010256799387990">KUPHUMELELE</translation>
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index 0fc4030..c5ac07a 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -103,6 +103,12 @@
 
   # https://crbug.com/1279285: Flaky.
   "policy.AllowWakeLocks",
+
+  # https://crbug.com/1281255
+  "nacl.Pnacl",
+
+  # https://crbug.com/1281802
+  "graphics.TraceReplay",
 ]
 
 # To disable a specific test in lacros_all_tast_tests, add it the following
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index 3e425b0b..0dff8fc 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -192,14 +192,15 @@
   return render_frame_host_->GetIsolationInfoForSubresources();
 }
 
-void ContentAutofillDriver::FillOrPreviewForm(
+base::flat_map<FieldGlobalId, ServerFieldType>
+ContentAutofillDriver::FillOrPreviewForm(
     int query_id,
     mojom::RendererFormDataAction action,
     const FormData& data,
     const url::Origin& triggered_origin,
     const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map) {
-  GetAutofillRouter().FillOrPreviewForm(this, query_id, action, data,
-                                        triggered_origin, field_type_map);
+  return GetAutofillRouter().FillOrPreviewForm(
+      this, query_id, action, data, triggered_origin, field_type_map);
 }
 
 void ContentAutofillDriver::FillOrPreviewFormImpl(
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h
index d49601e..00a4df5 100644
--- a/components/autofill/content/browser/content_autofill_driver.h
+++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -167,12 +167,13 @@
   // These events are forwarded to ContentAutofillRouter.
   // Their implementations (*Impl()) call into the renderer via
   // mojom::AutofillAgent.
-  void FillOrPreviewForm(int query_id,
-                         mojom::RendererFormDataAction action,
-                         const FormData& data,
-                         const url::Origin& triggered_origin,
-                         const base::flat_map<FieldGlobalId, ServerFieldType>&
-                             field_type_map) override;
+  base::flat_map<FieldGlobalId, ServerFieldType> FillOrPreviewForm(
+      int query_id,
+      mojom::RendererFormDataAction action,
+      const FormData& data,
+      const url::Origin& triggered_origin,
+      const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map)
+      override;
   void SendAutofillTypePredictionsToRenderer(
       const std::vector<FormStructure*>& forms) override;
   void RendererShouldAcceptDataListSuggestion(
diff --git a/components/autofill/content/browser/content_autofill_router.cc b/components/autofill/content/browser/content_autofill_router.cc
index 0b3732c7..542e8bf6 100644
--- a/components/autofill/content/browser/content_autofill_router.cc
+++ b/components/autofill/content/browser/content_autofill_router.cc
@@ -560,16 +560,17 @@
 // The reason is that browser forms may be outdated and hence refer to frames
 // that do not exist anymore.
 
-void ContentAutofillRouter::FillOrPreviewForm(
+base::flat_map<FieldGlobalId, ServerFieldType>
+ContentAutofillRouter::FillOrPreviewForm(
     ContentAutofillDriver* source,
     int query_id,
     mojom::RendererFormDataAction action,
     const FormData& data,
     const url::Origin& triggered_origin,
-    const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map) {
+    base::flat_map<FieldGlobalId, ServerFieldType> field_type_map) {
   if (!base::FeatureList::IsEnabled(features::kAutofillAcrossIframes)) {
     source->FillOrPreviewFormImpl(query_id, action, data);
-    return;
+    return field_type_map;
   }
 
   some_rfh_for_debugging_ = source->render_frame_host()->GetGlobalId();
@@ -578,6 +579,12 @@
       form_forest_.GetRendererFormsOfBrowserForm(data, triggered_origin,
                                                  field_type_map);
   for (const FormData& renderer_form : renderer_forms) {
+    // Erase fields that were not filled due to the security model from the
+    // to-be-returned |field_type_map|.
+    for (const FormFieldData& field : renderer_form.fields) {
+      if (field.value.empty())
+        field_type_map.erase(field.global_id());
+    }
     // Sending empty fill data to the renderer is semantically a no-op but
     // causes some further mojo calls.
     if (base::ranges::all_of(renderer_form.fields, &std::u16string::empty,
@@ -588,6 +595,7 @@
       target->FillOrPreviewFormImpl(kCrossFrameFill, action, renderer_form);
     }
   }
+  return field_type_map;
 }
 
 void ContentAutofillRouter::SendAutofillTypePredictionsToRenderer(
diff --git a/components/autofill/content/browser/content_autofill_router.h b/components/autofill/content/browser/content_autofill_router.h
index ec9b9ca..2d8b109 100644
--- a/components/autofill/content/browser/content_autofill_router.h
+++ b/components/autofill/content/browser/content_autofill_router.h
@@ -216,13 +216,13 @@
                             const FormFieldData& field);
 
   // Routing of events called by the browser:
-  void FillOrPreviewForm(
+  base::flat_map<FieldGlobalId, ServerFieldType> FillOrPreviewForm(
       ContentAutofillDriver* source_driver,
       int query_id,
       mojom::RendererFormDataAction action,
       const FormData& data,
       const url::Origin& triggered_origin,
-      const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map);
+      base::flat_map<FieldGlobalId, ServerFieldType> field_type_map);
   void SendAutofillTypePredictionsToRenderer(
       ContentAutofillDriver* source_driver,
       const std::vector<FormDataPredictions>& type_predictions);
diff --git a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.css b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.css
deleted file mode 100644
index c5059c2..0000000
--- a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.css
+++ /dev/null
@@ -1,256 +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.
- */
-
-html {
-  scroll-behavior: smooth;
-}
-
-.sticky-bar {
-  background-color: white;
-  border-bottom: 1px solid black;
-  color: black;
-  overflow: auto;
-  padding-bottom: 1.5ex;
-  position: sticky;
-  top: 0;
-}
-
-#log-display-config {
-  display: none; /* only visible for Autofill, not for Password Manager */
-  font-size: 120%;
-  padding: 1ex;
-}
-
-#log-display-config label {
-  padding-inline-end: 1em;
-}
-
-.fake-button {
-  background-color: lightgray;
-  border: 1px solid black;
-  margin-inline-end: 1em;
-  padding: .5ex;
-}
-
-#logging-note {
-  font-style: italic;
-}
-
-#logging-note-incognito {
-  font-style: italic;
-}
-
-/* Initially, nothing is visible, to avoid flicker. */
-#log-entries,
-#logging-note,
-#logging-note-incognito {
-  display: none;
-}
-
-/* Visibility settings for non-Incognito tabs. */
-[data-incognito=false] #log-entries,
-[data-incognito=false] #logging-note {
-  display: block;
-}
-
-/* Visibility settings for Incognito tabs. */
-[data-incognito=true] #logging-note-incognito {
-  display: block;
-}
-
-#version-info {
-  margin: 3px;
-  padding: 3px;
-}
-
-.version {
-  font-family: monospace;
-  max-width: 430px;
-  padding-inline-start: 5px;
-  vertical-align: top;
-  word-break: break-word;
-}
-
-.label {
-  font-family: monospace;
-  font-weight: 200;
-  vertical-align: top;
-}
-
-.log-entry,
-.marker {
-  padding: 3px;
-}
-
-.marker {
-  background-color: red;
-  font-size: 200%;
-  overflow-wrap: break-word;
-  white-space: normal;
-  word-wrap: break-word;
-}
-
-.marker::before {
-  content: 'Position marked: ';
-}
-
-/*
- * Colors can be taken from
- * https://material.io/design/color/#tools-for-picking-colors
- * Pick the rows of entries labeled with 100
- */
-
-.log-entry[scope='Context'] {
-  background-color: #F5F5F5;
-}
-
-.log-entry[scope='Parsing'] {
-  background-color: #FFECB3;
-}
-
-.log-entry[scope='AbortParsing'] {
-  background-color: #FFCDD2;
-}
-
-.log-entry[scope='Filling'] {
-  background-color: #D1C4E9;
-}
-
-.log-entry[scope='Submission'] {
-  background-color: #BBDEFB;
-}
-
-.log-entry[scope='AutofillServer'] {
-  background-color: #D7CCC8;
-}
-
-.log-entry[scope='Metrics'] {
-  background-color: #B2EBF2;
-}
-
-.log-entry[scope='AddressProfileFormImport'] {
-  background-color: #BFFBF2;
-}
-
-.log-entry[scope='CreditCardUploadStatus'] {
-  background-color: #4DB6AC;
-}
-
-.log-entry[scope='CardUploadDecision'] {
-  background-color: #4DD0E1;
-}
-
-.log-entry[scope='Rationalization'] {
-  background-color: #F8BBD0;
-}
-
-/*
- * Checkboxes add/remove hide-<Scope> classes to the #log-entries. Hiding of the
- * relevant <div>'s and adjacent <hr>'s is implemented by these classes.
- */
-
-.hide-Context .log-entry[scope='Context'],
-.hide-Context .log-entry[scope='Context'] + hr {
-  display: none;
-}
-
-.hide-Parsing .log-entry[scope='Parsing'],
-.hide-Parsing .log-entry[scope='Parsing'] + hr {
-  display: none;
-}
-
-.hide-AbortParsing .log-entry[scope='AbortParsing'],
-.hide-AbortParsing .log-entry[scope='AbortParsing'] + hr {
-  display: none;
-}
-
-.hide-Filling .log-entry[scope='Filling'],
-.hide-Filling .log-entry[scope='Filling'] + hr {
-  display: none;
-}
-
-.hide-Submission .log-entry[scope='Submission'],
-.hide-Submission .log-entry[scope='Submission'] + hr {
-  display: none;
-}
-
-.hide-AutofillServer .log-entry[scope='AutofillServer'],
-.hide-AutofillServer .log-entry[scope='AutofillServer'] + hr {
-  display: none;
-}
-
-.hide-Metrics .log-entry[scope='Metrics'],
-.hide-Metrics .log-entry[scope='Metrics'] + hr {
-  display: none;
-}
-
-.hide-AddressProfileFormImport .log-entry[scope='AddressProfileFormImport'],
-.hide-AddressProfileFormImport .log-entry[scope='AddressProfileFormImport'] + hr {
-  display: none;
-}
-
-.hide-CreditCardUploadStatus .log-entry[scope='CreditCardUploadStatus'],
-.hide-CreditCardUploadStatus .log-entry[scope='CreditCardUploadStatus'] + hr {
-  display: none;
-}
-
-.hide-CardUploadDecision .log-entry[scope='CardUploadDecision'],
-.hide-CardUploadDecision .log-entry[scope='CardUploadDecision'] + hr {
-  display: none;
-}
-
-.form {
-  border: 1px black solid;
-  margin: 3px;
-  padding: 3px;
-}
-
-.form td {
-  vertical-align: text-top;
-}
-
-.profile_import_from_form_section {
-  border: 1px black solid;
-  margin: 3px;
-  padding: 3px;
-}
-
-.profile_import_from_form_section td {
-  vertical-align: text-top;
-}
-
-.country_data {
-  border: 1px black solid;
-  margin: 3px;
-  padding: 3px;
-}
-
-.country_data td {
-  vertical-align: text-top;
-}
-
-.modal-dialog {
-  background-color: rgb(255, 255, 255);
-  border: 1px solid rgb(0, 0, 0);
-  display: block;
-  height: 100px;
-  left: 10%;
-  overflow: auto;
-  position: fixed;
-  right: 10%;
-  top: 10%;
-  width: 80%;
-  z-index: 1;
-}
-
-.modal-dialog-content {
-  padding: 20px;
-}
-
-.modal-dialog-close-button {
-  bottom: 20px;
-  position: absolute;
-  right: 20px;
-}
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 053bce98..3f3c3310 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
@@ -7,7 +7,264 @@
 <meta charset="utf-8">
 <script type="module" src="autofill_and_password_manager_internals.js"></script>
 <link rel="stylesheet" href="chrome://resources/css/chrome_shared.css">
-<link rel="stylesheet" href="autofill_and_password_manager_internals.css">
+<!--
+  The style sheets are inlined to get a prettier export if the user presses
+  Ctrl/Cmd + S to save the site or presses the download button.
+-->
+<style>
+html {
+  scroll-behavior: smooth;
+}
+
+.sticky-bar {
+  background-color: white;
+  border-bottom: 1px solid black;
+  color: black;
+  overflow: auto;
+  padding-bottom: 1.5ex;
+  position: sticky;
+  top: 0;
+}
+
+#log-display-config {
+  display: none; /* only visible for Autofill, not for Password Manager */
+  font-size: 120%;
+  padding: 1ex;
+}
+
+#log-display-config label {
+  padding-inline-end: 1em;
+}
+
+.fake-button {
+  background-color: lightgray;
+  border: 1px solid black;
+  margin-inline-end: 1em;
+  padding: .5ex;
+}
+
+#logging-note {
+  font-style: italic;
+}
+
+#logging-note-incognito {
+  font-style: italic;
+}
+
+/* Initially, nothing is visible, to avoid flicker. */
+#log-entries,
+#logging-note,
+#logging-note-incognito {
+  display: none;
+}
+
+/* Visibility settings for non-Incognito tabs. */
+[data-incognito=false] #log-entries,
+[data-incognito=false] #logging-note {
+  display: block;
+}
+
+/* Visibility settings for Incognito tabs. */
+[data-incognito=true] #logging-note-incognito {
+  display: block;
+}
+
+#version-info {
+  margin: 3px;
+  padding: 3px;
+}
+
+.version {
+  font-family: monospace;
+  max-width: 430px;
+  padding-inline-start: 5px;
+  vertical-align: top;
+  word-break: break-word;
+}
+
+.label {
+  font-family: monospace;
+  font-weight: 200;
+  vertical-align: top;
+}
+
+.log-entry,
+.marker {
+  padding: 3px;
+}
+
+.marker {
+  background-color: red;
+  font-size: 200%;
+  overflow-wrap: break-word;
+  white-space: normal;
+  word-wrap: break-word;
+}
+
+.marker::before {
+  content: 'Position marked: ';
+}
+
+/*
+ * Colors can be taken from
+ * https://material.io/design/color/#tools-for-picking-colors
+ * Pick the rows of entries labeled with 100
+ */
+
+.log-entry[scope='Context'] {
+  background-color: #F5F5F5;
+}
+
+.log-entry[scope='Parsing'] {
+  background-color: #FFECB3;
+}
+
+.log-entry[scope='AbortParsing'] {
+  background-color: #FFCDD2;
+}
+
+.log-entry[scope='Filling'] {
+  background-color: #D1C4E9;
+}
+
+.log-entry[scope='Submission'] {
+  background-color: #BBDEFB;
+}
+
+.log-entry[scope='AutofillServer'] {
+  background-color: #D7CCC8;
+}
+
+.log-entry[scope='Metrics'] {
+  background-color: #B2EBF2;
+}
+
+.log-entry[scope='AddressProfileFormImport'] {
+  background-color: #BFFBF2;
+}
+
+.log-entry[scope='CreditCardUploadStatus'] {
+  background-color: #4DB6AC;
+}
+
+.log-entry[scope='CardUploadDecision'] {
+  background-color: #4DD0E1;
+}
+
+.log-entry[scope='Rationalization'] {
+  background-color: #F8BBD0;
+}
+
+/*
+ * Checkboxes add/remove hide-<Scope> classes to the #log-entries. Hiding of the
+ * relevant <div>'s and adjacent <hr>'s is implemented by these classes.
+ */
+
+.hide-Context .log-entry[scope='Context'],
+.hide-Context .log-entry[scope='Context'] + hr {
+  display: none;
+}
+
+.hide-Parsing .log-entry[scope='Parsing'],
+.hide-Parsing .log-entry[scope='Parsing'] + hr {
+  display: none;
+}
+
+.hide-AbortParsing .log-entry[scope='AbortParsing'],
+.hide-AbortParsing .log-entry[scope='AbortParsing'] + hr {
+  display: none;
+}
+
+.hide-Filling .log-entry[scope='Filling'],
+.hide-Filling .log-entry[scope='Filling'] + hr {
+  display: none;
+}
+
+.hide-Submission .log-entry[scope='Submission'],
+.hide-Submission .log-entry[scope='Submission'] + hr {
+  display: none;
+}
+
+.hide-AutofillServer .log-entry[scope='AutofillServer'],
+.hide-AutofillServer .log-entry[scope='AutofillServer'] + hr {
+  display: none;
+}
+
+.hide-Metrics .log-entry[scope='Metrics'],
+.hide-Metrics .log-entry[scope='Metrics'] + hr {
+  display: none;
+}
+
+.hide-AddressProfileFormImport .log-entry[scope='AddressProfileFormImport'],
+.hide-AddressProfileFormImport .log-entry[scope='AddressProfileFormImport'] + hr {
+  display: none;
+}
+
+.hide-CreditCardUploadStatus .log-entry[scope='CreditCardUploadStatus'],
+.hide-CreditCardUploadStatus .log-entry[scope='CreditCardUploadStatus'] + hr {
+  display: none;
+}
+
+.hide-CardUploadDecision .log-entry[scope='CardUploadDecision'],
+.hide-CardUploadDecision .log-entry[scope='CardUploadDecision'] + hr {
+  display: none;
+}
+
+.form {
+  border: 1px black solid;
+  margin: 3px;
+  padding: 3px;
+}
+
+.form td {
+  vertical-align: text-top;
+}
+
+.profile_import_from_form_section {
+  border: 1px black solid;
+  margin: 3px;
+  padding: 3px;
+}
+
+.profile_import_from_form_section td {
+  vertical-align: text-top;
+}
+
+.country_data {
+  border: 1px black solid;
+  margin: 3px;
+  padding: 3px;
+}
+
+.country_data td {
+  vertical-align: text-top;
+}
+
+.modal-dialog {
+  background-color: rgb(255, 255, 255);
+  border: 1px solid rgb(0, 0, 0);
+  display: block;
+  height: 100px;
+  left: 10%;
+  overflow: auto;
+  position: fixed;
+  right: 10%;
+  top: 10%;
+  width: 80%;
+  z-index: 1;
+}
+
+.modal-dialog-content {
+  padding: 20px;
+}
+
+.modal-dialog-close-button {
+  bottom: 20px;
+  position: absolute;
+  right: 20px;
+}
+
+</style>
 </head>
 <body>
 <div>
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 bd58b70..c0d9743 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
@@ -300,6 +300,12 @@
     window.URL.revokeObjectURL(url);
     a.remove();
   });
+// <if expr="is_ios">
+  // Hide this until downloading a file works on iOS, see
+  // https://bugs.webkit.org/show_bug.cgi?id=167341
+  // https://bugs.chromium.org/p/chromium/issues/detail?id=1252380
+  downloadFakeButton.style = 'display: none';
+// </if>
 }
 
 document.addEventListener('DOMContentLoaded', function(event) {
diff --git a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals_ios.html b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals_ios.html
deleted file mode 100644
index e88900f..0000000
--- a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals_ios.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!doctype html>
-<html>
-<head>
-<!-- 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. -->
-<meta charset="utf-8">
-<script type="module" src="autofill_and_password_manager_internals.js"></script>
-<link rel="stylesheet" href="chrome://resources/css/chrome_shared.css">
-<link rel="stylesheet" href="autofill_and_password_manager_internals.css">
-</head>
-<body>
-<div>
-  <h1 id="h1-title"></h1>
-  <div id="log-display-config">
-    <span id="marker-fake-button">Add Marker</span>
-    <input type="checkbox" id="enable-autoscroll" checked><label for="enable-autoscroll">Enable autoscroll</label>
-  </div>
-</div>
-<div id="logging-note"></div>
-<div id="logging-note-incognito"></div>
-<div id="version-info">
-  <table>
-    <tr>
-      <td class="label">Version:</td>
-      <td class="version"><span>$i18n{version}</span>
-        (<span>$i18n{official}</span>)
-        <span>$i18n{version_modifier}</span></td>
-    </tr>
-    <tr>
-      <td class="label">Revision:</td>
-      <td class="version"><span>$i18n{cl}</span></td>
-    </tr>
-    <tr>
-      <td class="label">User Agent:</td>
-      <td class="version"><span>$i18n{useragent}</span></td>
-    </tr>
-    <tr>
-      <td class="label">App Locale:</td>
-      <td class="version"><span>$i18n{app_locale}</span></td>
-    </tr>
-    <tr>
-      <td class="label">Variations:</td>
-      <td class="version" id="variations-list"></td>
-    </tr>
-  </table>
-</div>
-<div id="log-entries">
-</div>
-</body>
-</html>
diff --git a/components/autofill/core/browser/autofill_driver.h b/components/autofill/core/browser/autofill_driver.h
index 84abf9d..1860516 100644
--- a/components/autofill/core/browser/autofill_driver.h
+++ b/components/autofill/core/browser/autofill_driver.h
@@ -66,14 +66,25 @@
   GetOrCreateCreditCardInternalAuthenticator() = 0;
 #endif
 
-  // Forwards |data| to the renderer.
+  // Forwards |data| to the renderer which shall preview or fill the values of
+  // |data|'s fields into the relevant DOM elements.
+  //
   // |query_id| is the id of the renderer's original request for the data.
+  //
   // |action| is the action the renderer should perform with the |data|.
+  //
   // |triggered_origin| is the origin of the field on which Autofill was
-  // triggered, and |field_type_map| are the type predictions; these two
-  // parameters can be taken into account to decide which fields to fill across
-  // frames. This method is a no-op if the renderer is not currently available.
-  virtual void FillOrPreviewForm(
+  // triggered, and |field_type_map| contains the type predictions of the fields
+  // that may be previewed or filled; these two parameters can be taken into
+  // account to decide which fields to preview or fill across frames.
+  //
+  // Returns a map from actually previewed or filled fields to their type. This
+  // is a sub-map of |field_type_map|: it does not contain those fields from
+  // |field_type_map| which shall not be filled due to the security policy for
+  // cross-frame previewing and filling.
+  //
+  // This method is a no-op if the renderer is not currently available.
+  virtual base::flat_map<FieldGlobalId, ServerFieldType> FillOrPreviewForm(
       int query_id,
       mojom::RendererFormDataAction action,
       const FormData& data,
diff --git a/components/autofill/core/browser/autofill_form_test_utils.cc b/components/autofill/core/browser/autofill_form_test_utils.cc
index dc5778c..5f63478 100644
--- a/components/autofill/core/browser/autofill_form_test_utils.cc
+++ b/components/autofill/core/browser/autofill_form_test_utils.cc
@@ -4,6 +4,7 @@
 
 #include "components/autofill/core/browser/autofill_form_test_utils.h"
 
+#include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -91,11 +92,13 @@
   form_data.url = GURL(test_form_attributes.url);
   form_data.action = GURL(test_form_attributes.action);
   form_data.name = test_form_attributes.name.data();
-  static int field_count = 0;
-  if (test_form_attributes.unique_renderer_id)
-    form_data.unique_renderer_id = *test_form_attributes.unique_renderer_id;
+  form_data.host_frame =
+      test_form_attributes.host_frame.value_or(GetLocalFrameToken());
+  form_data.unique_renderer_id =
+      test_form_attributes.unique_renderer_id.value_or(MakeFormRendererId());
   if (test_form_attributes.main_frame_origin)
     form_data.main_frame_origin = *test_form_attributes.main_frame_origin;
+
   for (const FieldDataDescription& field_description :
        test_form_attributes.fields) {
     FormFieldData field = CreateFieldByRole(field_description.role);
@@ -105,6 +108,10 @@
         field_description.select_options.size() > 0) {
       field.options = field_description.select_options;
     }
+    field.host_frame =
+        field_description.host_frame.value_or(form_data.host_frame);
+    field.unique_renderer_id =
+        field_description.unique_renderer_id.value_or(MakeFieldRendererId());
     field.is_focusable = field_description.is_focusable;
     if (!field_description.autocomplete_attribute.empty()) {
       field.autocomplete_attribute =
@@ -118,7 +125,8 @@
       field.value = *field_description.value;
     if (field_description.is_autofilled)
       field.is_autofilled = *field_description.is_autofilled;
-    field.unique_renderer_id = FieldRendererId(field_count++);
+    field.origin =
+        field_description.origin.value_or(form_data.main_frame_origin);
     field.should_autocomplete = field_description.should_autocomplete;
     form_data.fields.push_back(field);
   }
diff --git a/components/autofill/core/browser/autofill_form_test_utils.h b/components/autofill/core/browser/autofill_form_test_utils.h
index 9dae1d4..2bf698d 100644
--- a/components/autofill/core/browser/autofill_form_test_utils.h
+++ b/components/autofill/core/browser/autofill_form_test_utils.h
@@ -40,6 +40,8 @@
 template <typename = void>
 struct FieldDataDescription {
   ServerFieldType role = ServerFieldType::EMPTY_TYPE;
+  absl::optional<LocalFrameToken> host_frame;
+  absl::optional<FieldRendererId> unique_renderer_id;
   bool is_focusable = true;
   const base::StringPiece16 label = kLabelText;
   const base::StringPiece16 name = kNameText;
@@ -48,6 +50,7 @@
   const base::StringPiece form_control_type = "text";
   bool should_autocomplete = true;
   absl::optional<bool> is_autofilled;
+  absl::optional<url::Origin> origin;
   std::vector<SelectOption> select_options = {};
 };
 
@@ -56,6 +59,7 @@
 struct TestFormAttributes {
   const base::StringPiece description_for_logging;
   std::vector<FieldDataDescription<>> fields;
+  absl::optional<LocalFrameToken> host_frame;
   absl::optional<FormRendererId> unique_renderer_id;
   const base::StringPiece16 name = u"TestForm";
   const base::StringPiece url = kFormUrl;
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index 37a3d0f..4e1ffd0 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -75,8 +75,12 @@
 
 }  // namespace
 
-LocalFrameToken GetLocalFrameToken() {
-  return LocalFrameToken(base::UnguessableToken::Deserialize(98765, 43210));
+LocalFrameToken GetLocalFrameToken(RandomizeFrame randomize) {
+  if (*randomize) {
+    return LocalFrameToken(base::UnguessableToken::Create());
+  } else {
+    return LocalFrameToken(base::UnguessableToken::Deserialize(98765, 43210));
+  }
 }
 
 FormRendererId MakeFormRendererId() {
@@ -89,14 +93,12 @@
   return FieldRendererId(counter++);
 }
 
-// Creates new, pairwise distinct FormGlobalIds.
-FormGlobalId MakeFormGlobalId() {
-  return {GetLocalFrameToken(), MakeFormRendererId()};
+FormGlobalId MakeFormGlobalId(RandomizeFrame randomize) {
+  return {GetLocalFrameToken(randomize), MakeFormRendererId()};
 }
 
-// Creates new, pairwise distinct FieldGlobalIds.
-FieldGlobalId MakeFieldGlobalId() {
-  return {GetLocalFrameToken(), MakeFieldRendererId()};
+FieldGlobalId MakeFieldGlobalId(RandomizeFrame randomize) {
+  return {GetLocalFrameToken(randomize), MakeFieldRendererId()};
 }
 
 void SetFormGroupValues(FormGroup& form_group,
diff --git a/components/autofill/core/browser/autofill_test_utils.h b/components/autofill/core/browser/autofill_test_utils.h
index 6d528eb..a403a8b 100644
--- a/components/autofill/core/browser/autofill_test_utils.h
+++ b/components/autofill/core/browser/autofill_test_utils.h
@@ -68,8 +68,12 @@
 // Convenience declaration for multiple FormGroup values.
 using FormGroupValues = std::vector<FormGroupValue>;
 
-// Creates a non-empty LocalFrameToken (no variation among different calls).
-LocalFrameToken GetLocalFrameToken();
+using RandomizeFrame = base::StrongAlias<struct RandomizeFrameTag, bool>;
+
+// Creates non-empty LocalFrameToken. If `randomize` is true, the
+// LocalFrameToken is generated randomly, otherwise it is stable.
+LocalFrameToken GetLocalFrameToken(
+    RandomizeFrame randomize = RandomizeFrame(false));
 
 // Creates new, pairwise distinct FormRendererIds.
 FormRendererId MakeFormRendererId();
@@ -77,11 +81,15 @@
 // Creates new, pairwise distinct FieldRendererIds.
 FieldRendererId MakeFieldRendererId();
 
-// Creates new, pairwise distinct FormGlobalIds.
-FormGlobalId MakeFormGlobalId();
+// Creates new, pairwise distinct FormGlobalIds. If `randomize` is true, the
+// LocalFrameToken is generated randomly, otherwise it is stable.
+FormGlobalId MakeFormGlobalId(
+    RandomizeFrame randomize_frame = RandomizeFrame(false));
 
-// Creates new, pairwise distinct FieldGlobalIds.
-FieldGlobalId MakeFieldGlobalId();
+// Creates new, pairwise distinct FieldGlobalIds. If `randomize` is true, the
+// LocalFrameToken is generated randomly, otherwise it is stable.
+FieldGlobalId MakeFieldGlobalId(
+    RandomizeFrame randomize_frame = RandomizeFrame(false));
 
 // Helper function to set values and verification statuses to a form group.
 void SetFormGroupValues(FormGroup& form_group,
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index 79c9b39..53d590d 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -1062,11 +1062,6 @@
     return;
   }
 
-  if (!is_preview) {
-    credit_card_form_event_logger_->OnDidFillSuggestion(
-        credit_card_, *form_structure, *autofill_field, sync_state_);
-  }
-
   FillOrPreviewDataModelForm(action, query_id, form, field, &credit_card_,
                              /*optional_cvc=*/nullptr, form_structure,
                              autofill_field);
@@ -1082,11 +1077,6 @@
   AutofillField* autofill_field = nullptr;
   if (!GetCachedFormAndField(form, field, &form_structure, &autofill_field))
     return;
-  if (action == mojom::RendererFormDataAction::kFill) {
-    address_form_event_logger_->OnDidFillSuggestion(
-        profile, *form_structure, *autofill_field, sync_state_);
-  }
-
   FillOrPreviewDataModelForm(action, query_id, form, field, &profile,
                              /*cvc=*/nullptr, form_structure, autofill_field);
 }
@@ -1466,12 +1456,6 @@
     return;
   }
 
-  // The originally selected masked card is |credit_card_|. So we must log
-  // |credit_card_| as opposed to |credit_card| to correctly indicate that the
-  // user filled the form using a masked card suggestion.
-  credit_card_form_event_logger_->OnDidFillSuggestion(
-      credit_card_, *form_structure, *autofill_field, sync_state_);
-
   DCHECK(credit_card);
 
   // If synced down card is a virtual card, let the client know so that it can
@@ -1706,13 +1690,20 @@
   bool could_attempt_refill = filling_context != nullptr &&
                               !filling_context->attempted_refill && !is_refill;
 
-  // Count the number of times the value of a specific type was filled into the
-  // form.
-  base::flat_map<ServerFieldType, size_t> type_filling_count;
-  // See FillOrPreviewForm().
-  base::flat_map<FieldGlobalId, ServerFieldType> field_type_map;
-  type_filling_count.reserve(form_structure->field_count());
-  field_type_map.reserve(form_structure->field_count());
+  // Counts the number of times a type was seen in the section to be filled.
+  // This is used to limit the maximum number fills per value.
+  base::flat_map<ServerFieldType, size_t> type_count;
+  type_count.reserve(form_structure->field_count());
+
+  // Maps those fields to their field type that BrowserAutofillManager can and
+  // wants to fill. This is passed on to ContentAutofillRouter, which may
+  // disallow filling some fields according to the security policy for filling
+  // across frames (e.g., credit card numbers are not allowed to be filled
+  // across origins). See AutofillDriver::FillOrPreviewForm() for details.
+  base::flat_map<FieldGlobalId, ServerFieldType>
+      field_types_to_be_filled_before_security_policy;
+  field_types_to_be_filled_before_security_policy.reserve(
+      form_structure->field_count());
 
   for (size_t i = 0; i < form_structure->field_count(); ++i) {
     std::string field_number = base::StringPrintf("Field %zu", i);
@@ -1813,8 +1804,7 @@
 
     // A field with a specific type is only allowed to be filled a limited
     // number of times given by |TypeValueFormFillingLimit(field_type)|.
-    if (++type_filling_count[field_type] >
-        TypeValueFormFillingLimit(field_type)) {
+    if (++type_count[field_type] > TypeValueFormFillingLimit(field_type)) {
       buffer << Tr{} << field_number
              << "Skipped: field-type filling-limit reached";
       continue;
@@ -1838,13 +1828,18 @@
     const std::u16string kEmptyCvc{};
     std::string failure_to_fill;  // Reason for failing to fill.
 
-    // Fill the non-empty value from |profile_or_credit_card| into the result
-    // vector, which will be sent to the renderer.
-    FillFieldWithValue(cached_field, profile_or_credit_card, &result.fields[i],
-                       should_notify, optional_cvc ? *optional_cvc : kEmptyCvc,
-                       data_util::DetermineGroups(*form_structure), action,
-                       &failure_to_fill);
-    field_type_map[result.fields[i].global_id()] = field_type;
+    // Fill the non-empty value from |profile_or_credit_card| into the |result|
+    // form, which will be sent to the renderer. FillFieldWithValue() may also
+    // fill a field if it had been autofilled or manually filled before, and
+    // also returns true in such a case.
+    bool is_newly_autofilled = FillFieldWithValue(
+        cached_field, profile_or_credit_card, &result.fields[i], should_notify,
+        optional_cvc ? *optional_cvc : kEmptyCvc,
+        data_util::DetermineGroups(*form_structure), action, &failure_to_fill);
+    if (is_newly_autofilled) {
+      FieldGlobalId field_id = result.fields[i].global_id();
+      field_types_to_be_filled_before_security_policy[field_id] = field_type;
+    }
 
     bool has_value_after = !result.fields[i].value.empty();
     bool is_autofilled_after = result.fields[i].is_autofilled;
@@ -1852,7 +1847,7 @@
     buffer << Tr{} << field_number
            << base::StringPrintf(
                   "Fillable - has value: %d->%d; autofilled: %d->%d. %s",
-                  has_value_before, is_autofilled_before, has_value_after,
+                  has_value_before, has_value_after, is_autofilled_before,
                   is_autofilled_after, failure_to_fill.c_str());
 
     if (!cached_field->IsVisible() && result.fields[i].is_autofilled)
@@ -1866,17 +1861,41 @@
   if (autofilled_form_signatures_.size() > kMaxRecentFormSignaturesToRemember)
     autofilled_form_signatures_.pop_back();
 
-  // Note that this may invalidate |profile_or_credit_card|.
-  if (action == mojom::RendererFormDataAction::kFill && !is_refill)
-    personal_data_->RecordUseOf(profile_or_credit_card);
-
   if (log_manager()) {
     log_manager()->Log() << LoggingScope::kFilling
                          << LogMessage::kSendFillingData << Br{}
                          << std::move(buffer);
   }
-  driver()->FillOrPreviewForm(query_id, action, result, field.origin,
-                              field_type_map);
+
+  base::flat_map<FieldGlobalId, ServerFieldType>
+      field_types_filled_after_security_policy;
+  field_types_filled_after_security_policy = driver()->FillOrPreviewForm(
+      query_id, action, result, field.origin,
+      field_types_to_be_filled_before_security_policy);
+
+  // Call OnDidFillSuggestion() to log the metrics.
+  if (action == mojom::RendererFormDataAction::kFill && !is_refill) {
+    if (is_credit_card) {
+      // The originally selected masked card is `credit_card_`. So we must log
+      // `credit_card_` as opposed to
+      // `absl::get<CreditCard*>(profile_or_credit_card)` to correctly indicate
+      // whether the user filled the form using a masked card suggestion.
+      credit_card_form_event_logger_->OnDidFillSuggestion(
+          credit_card_, *form_structure, *autofill_field,
+          field_types_to_be_filled_before_security_policy,
+          field_types_filled_after_security_policy, sync_state_);
+    }
+
+    if (!is_credit_card) {
+      address_form_event_logger_->OnDidFillSuggestion(
+          *absl::get<const AutofillProfile*>(profile_or_credit_card),
+          *form_structure, *autofill_field, sync_state_);
+    }
+  }
+
+  // Note that this may invalidate |profile_or_credit_card|.
+  if (action == mojom::RendererFormDataAction::kFill && !is_refill)
+    personal_data_->RecordUseOf(profile_or_credit_card);
 }
 
 std::unique_ptr<FormStructure> BrowserAutofillManager::ValidateSubmittedForm(
@@ -2370,7 +2389,7 @@
   }
 }
 
-void BrowserAutofillManager::FillFieldWithValue(
+bool BrowserAutofillManager::FillFieldWithValue(
     AutofillField* autofill_field,
     absl::variant<const AutofillProfile*, const CreditCard*>
         profile_or_credit_card,
@@ -2380,8 +2399,10 @@
     uint32_t profile_form_bitmask,
     mojom::RendererFormDataAction action,
     std::string* failure_to_fill) {
-  if (field_filler_.FillFormField(*autofill_field, profile_or_credit_card,
-                                  field_data, cvc, action, failure_to_fill)) {
+  bool filled_field =
+      field_filler_.FillFormField(*autofill_field, profile_or_credit_card,
+                                  field_data, cvc, action, failure_to_fill);
+  if (filled_field) {
     if (failure_to_fill)
       *failure_to_fill = "Decided to fill";
     // Mark the cached field as autofilled, so that we can detect when a
@@ -2407,6 +2428,7 @@
                                                  app_locale_));
     }
   }
+  return filled_field;
 }
 
 void BrowserAutofillManager::SetFillingContext(
diff --git a/components/autofill/core/browser/browser_autofill_manager.h b/components/autofill/core/browser/browser_autofill_manager.h
index 2424b26..a5b949bc5 100644
--- a/components/autofill/core/browser/browser_autofill_manager.h
+++ b/components/autofill/core/browser/browser_autofill_manager.h
@@ -609,7 +609,17 @@
       size_t current_index,
       const ServerFieldTypeSet& upload_types);
 
-  void FillFieldWithValue(
+  // Calls FieldFiller::FillFormField().
+  //
+  // If the field was newly filled, sets `autofill_field->is_autofilled` and
+  // `field_data->is_autofilled` both to true (otherwise leaves them unchanged).
+  //
+  // Also logs metrics and, if `should_notify` is true, calls
+  // AutofillClient::DidFillOrPreviewField().
+  //
+  // Returns true if the field has been filled, false otherwise. This is
+  // independent of whether the field was filled or autofilled before.
+  bool FillFieldWithValue(
       AutofillField* autofill_field,
       absl::variant<const AutofillProfile*, const CreditCard*>
           profile_or_credit_card,
diff --git a/components/autofill/core/browser/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
index ef683322..2435fbf 100644
--- a/components/autofill/core/browser/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
@@ -320,7 +320,7 @@
   MockAutofillDriver& operator=(const MockAutofillDriver&) = delete;
 
   // Mock methods to enable testability.
-  MOCK_METHOD(void,
+  MOCK_METHOD((base::flat_map<FieldGlobalId, ServerFieldType>),
               FillOrPreviewForm,
               (int query_id,
                mojom::RendererFormDataAction action,
@@ -528,7 +528,8 @@
                                           FormData* response_data) {
     EXPECT_CALL(*autofill_driver_, FillOrPreviewForm(_, _, _, _, _))
         .WillOnce((DoAll(testing::SaveArg<0>(response_query_id),
-                         testing::SaveArg<2>(response_data))));
+                         testing::SaveArg<2>(response_data),
+                         testing::ReturnArg<4>())));
     FillAutofillFormData(input_query_id, input_form, input_field, unique_id);
   }
 
@@ -542,7 +543,8 @@
       FormData* response_data) {
     EXPECT_CALL(*autofill_driver_, FillOrPreviewForm(_, _, _, _, _))
         .WillOnce((DoAll(testing::SaveArg<0>(response_query_id),
-                         testing::SaveArg<2>(response_data))));
+                         testing::SaveArg<2>(response_data),
+                         testing::ReturnArg<4>())));
     browser_autofill_manager_->FillOrPreviewVirtualCardInformation(
         action, guid, input_query_id, input_form, input_field);
   }
@@ -2653,7 +2655,8 @@
   FormData response_data;
   EXPECT_CALL(*autofill_driver_, FillOrPreviewForm(_, _, _, _, _))
       .WillOnce((DoAll(testing::SaveArg<0>(&response_query_id),
-                       testing::SaveArg<2>(&response_data))));
+                       testing::SaveArg<2>(&response_data),
+                       testing::ReturnArg<4>())));
   browser_autofill_manager_->FillOrPreviewDataModelForm(
       mojom::RendererFormDataAction::kFill, kDefaultPageID, form,
       form.fields.front(), profile, nullptr, form_structure, autofill_field);
diff --git a/components/autofill/core/browser/field_filler.h b/components/autofill/core/browser/field_filler.h
index f6707a5..df2e159 100644
--- a/components/autofill/core/browser/field_filler.h
+++ b/components/autofill/core/browser/field_filler.h
@@ -32,7 +32,9 @@
   // values. If |action| indicates that the value will be used for the
   // autofill preview (aka. suggestion) state, the data to be filled may be
   // obfuscated.
-  // Returns |true| if the field has been filled, false otherwise. If
+  //
+  // Returns |true| if the field has been filled, false otherwise. This is
+  // independent of whether the field was filled or autofilled before. If
   // |failure_to_fill| is not null, errors are reported to that string.
   bool FillFormField(const AutofillField& field,
                      absl::variant<const AutofillProfile*, const CreditCard*>
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.cc b/components/autofill/core/browser/metrics/autofill_metrics.cc
index a75ae7e..ba2c58ed 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics.cc
@@ -201,8 +201,11 @@
     AutofillMetrics::MeasurementTime measurement_time) {
   base::StringPiece measurement_time_string;
   switch (measurement_time) {
-    case AutofillMetrics::MeasurementTime::kFillTime:
-      measurement_time_string = "AtFillTime";
+    case AutofillMetrics::MeasurementTime::kFillTimeBeforeSecurityPolicy:
+      measurement_time_string = "AtFillTimeBeforeSecurityPolicy";
+      break;
+    case AutofillMetrics::MeasurementTime::kFillTimeAfterSecurityPolicy:
+      measurement_time_string = "AtFillTimeAfterSecurityPolicy";
       break;
     case AutofillMetrics::MeasurementTime::kSubmissionTime:
       measurement_time_string = "AtSubmissionTime";
@@ -2445,7 +2448,8 @@
 }
 
 // static
-void AutofillMetrics::LogCreditCardSeamlessFills(
+absl::optional<AutofillMetrics::CreditCardSeamlessFillMetric>
+AutofillMetrics::LogCreditCardSeamlessFills(
     const ServerFieldTypeSet& autofilled_types,
     MeasurementTime measurement_time) {
   bool name = autofilled_types.contains(CREDIT_CARD_NAME_FULL) ||
@@ -2459,7 +2463,8 @@
                autofilled_types.contains(CREDIT_CARD_EXP_4_DIGIT_YEAR)));
   bool cvc = autofilled_types.contains(CREDIT_CARD_VERIFICATION_CODE);
   if (!name && !number && !exp && !cvc)
-    return;
+    return absl::nullopt;
+
   CreditCardSeamlessFillMetric emit;
   if (name && number && exp && cvc) {
     emit = CreditCardSeamlessFillMetric::kFullFill;
@@ -2474,10 +2479,12 @@
   } else {
     emit = CreditCardSeamlessFillMetric::kPartialFill;
   }
+
   base::UmaHistogramEnumeration(
       AppendMeasurementTimeToMetricName("Autofill.CreditCard.SeamlessFills",
                                         measurement_time),
       emit);
+  return emit;
 }
 
 // static
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.h b/components/autofill/core/browser/metrics/autofill_metrics.h
index 5bc419d8..9f14168 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.h
+++ b/components/autofill/core/browser/metrics/autofill_metrics.h
@@ -43,7 +43,8 @@
 class AutofillMetrics {
  public:
   enum class MeasurementTime {
-    kFillTime,
+    kFillTimeBeforeSecurityPolicy,
+    kFillTimeAfterSecurityPolicy,
     kSubmissionTime,
   };
 
@@ -1196,6 +1197,8 @@
   // +----------------------------+------+--------+----------+-----+
   // | kPartialFill               |           otherwise            |
   // +-------------------------------------------------------------+
+  //
+  // Keep consistent with FORM_EVENT_CREDIT_CARD_SEAMLESSNESS_*.
   enum class CreditCardSeamlessFillMetric {
     kFullFill = 0,
     kOptionalNameMissing = 1,
@@ -1778,9 +1781,10 @@
   // Logs the Autofill.CreditCard.SeamlessFills metric. See the enum for
   // details. Note that this function does not check whether the form contains a
   // credit card field.
-  static void LogCreditCardSeamlessFills(
-      const ServerFieldTypeSet& autofilled_types,
-      MeasurementTime measurement_time);
+  // Returns the emitted metric, if any.
+  static absl::optional<CreditCardSeamlessFillMetric>
+  LogCreditCardSeamlessFills(const ServerFieldTypeSet& autofilled_types,
+                             MeasurementTime measurement_time);
 
   // Logs the Autofill.CreditCard.NumberFills metric.
   static void LogCreditCardNumberFills(
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
index 19cc9d5..8b8b528 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "base/base64.h"
+#include "base/containers/fixed_flat_map.h"
 #include "base/feature_list.h"
 #include "base/ios/ios_util.h"
 #include "base/memory/raw_ptr.h"
@@ -29,6 +30,7 @@
 #include "components/autofill/core/browser/autofill_external_delegate.h"
 #include "components/autofill/core/browser/autofill_form_test_utils.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/metrics/form_events/address_form_event_logger.h"
 #include "components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h"
 #include "components/autofill/core/browser/metrics/form_events/form_events.h"
@@ -12527,4 +12529,236 @@
       1);
 }
 
+// Base class for cross-frame filling metrics, in particular for
+// Autofill.CreditCard.SeamlessFills.* and Autofill.CreditCard.NumberFills.*.
+class AutofillMetricsCrossFrameFormTest : public AutofillMetricsTest {
+ public:
+  AutofillMetricsCrossFrameFormTest() {
+    scoped_feature_list_.InitAndEnableFeature(features::kAutofillAcrossIframes);
+  }
+  ~AutofillMetricsCrossFrameFormTest() override = default;
+
+  void SetUp() override {
+    AutofillMetricsTest::SetUp();
+
+    RecreateCreditCards(true /* include_local_credit_card */,
+                        false /* include_masked_server_credit_card */,
+                        false /* include_full_server_credit_card */,
+                        false /* masked_card_is_enrolled_for_virtual_card */);
+
+    url::Origin main_origin = url::Origin::Create(GURL("https://main.com/"));
+    url::Origin other_origin = url::Origin::Create(GURL("https://other.com/"));
+    form_ = test::GetFormData(
+        {.description_for_logging = "CrossFrameFillingMetrics",
+         .fields =
+             {
+                 {.label = u"Cardholder name",
+                  .name = u"card_name",
+                  .is_autofilled = false},
+                 {.label = u"CCNumber",
+                  .name = u"ccnumber",
+                  .is_autofilled = false,
+                  .origin = other_origin},
+                 {.label = u"ExpDate",
+                  .name = u"expdate",
+                  .is_autofilled = false},
+                 {.label = u"CVC",
+                  .name = u"cvc",
+                  .is_autofilled = false,
+                  .origin = other_origin},
+             },
+         .unique_renderer_id = test::MakeFormRendererId(),
+         .main_frame_origin = main_origin});
+
+    ASSERT_EQ(form_.main_frame_origin, form_.fields[0].origin);
+    ASSERT_EQ(form_.main_frame_origin, form_.fields[2].origin);
+    ASSERT_NE(form_.main_frame_origin, form_.fields[1].origin);
+    ASSERT_NE(form_.main_frame_origin, form_.fields[3].origin);
+    ASSERT_EQ(form_.fields[1].origin, form_.fields[3].origin);
+
+    // Mock a simplified security model which allows to filter (only) fields
+    // from the same origin.
+    autofill_driver_->SetFieldTypeMapFilter(base::BindRepeating(
+        [](AutofillMetricsCrossFrameFormTest* self,
+           const url::Origin& triggered_origin, FieldGlobalId field,
+           ServerFieldType) {
+          return triggered_origin == self->GetFieldById(field).origin;
+        },
+        this));
+  }
+
+  void SeeForm() {
+    std::vector<ServerFieldType> field_types = {
+        CREDIT_CARD_NAME_FULL, CREDIT_CARD_NUMBER,
+        CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, CREDIT_CARD_VERIFICATION_CODE};
+    browser_autofill_manager_->AddSeenForm(form_, field_types, field_types);
+    browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/{form_},
+                                           /*removed_forms=*/{});
+  }
+
+  const CreditCard& credit_card() {
+    return *browser_autofill_manager_->credit_card_access_manager()
+                ->GetCreditCardsToSuggest()
+                .front();
+  }
+
+  // Any call to FillForm() should be followed by a SetFormValues() call to
+  // mimic its effect on |form_|.
+  void FillForm(const FormFieldData& triggering_field) {
+    browser_autofill_manager_->FillCreditCardForm(0, form_, triggering_field,
+                                                  credit_card(), u"123");
+  }
+
+  // Sets the field values of |form_| according to the parameters.
+  //
+  // Since this test suite doesn't use mocks, we can't intercept the autofilled
+  // form. Therefore, after each manual fill or autofill, we shall call
+  // SetFormValues()
+  void SetFormValues(const ServerFieldTypeSet& fill_field_types,
+                     bool is_autofilled,
+                     bool is_user_typed) {
+    auto type_to_index = base::MakeFixedFlatMap<ServerFieldType, size_t>(
+        {{CREDIT_CARD_NAME_FULL, 0},
+         {CREDIT_CARD_NUMBER, 1},
+         {CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 2},
+         {CREDIT_CARD_VERIFICATION_CODE, 3}});
+
+    for (ServerFieldType fill_type : fill_field_types) {
+      auto* index_it = type_to_index.find(fill_type);
+      ASSERT_NE(index_it, type_to_index.end());
+      FormFieldData& field = form_.fields[index_it->second];
+      field.value = fill_type != CREDIT_CARD_VERIFICATION_CODE
+                        ? credit_card().GetRawInfo(fill_type)
+                        : u"123";
+      field.is_autofilled = is_autofilled;
+      field.properties_mask = (field.properties_mask & ~kUserTyped) |
+                              (is_user_typed ? kUserTyped : 0);
+    }
+  }
+
+  void SubmitForm() {
+    browser_autofill_manager_->OnFormSubmitted(
+        form_, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+  }
+
+  FormFieldData& GetFieldById(FieldGlobalId field) {
+    auto it =
+        base::ranges::find(form_.fields, field, &FormFieldData::global_id);
+    CHECK(it != form_.fields.end());
+    return *it;
+  }
+
+  void CommitMetrics() { browser_autofill_manager_.reset(); }
+
+  // Fillable form.
+  base::test::ScopedFeatureList scoped_feature_list_;
+  FormData form_;
+};
+
+// Tests that Autofill.CreditCard.SeamlessFills.* is not emitted for manual
+// fills and that Autofill.CreditCard.NumberFills.AtSubmissionTime emits false.
+TEST_F(AutofillMetricsCrossFrameFormTest,
+       DoNotLogCreditCardSeamlessFillsMetricIfNotAutofilled) {
+  base::HistogramTester histogram_tester;
+  SeeForm();
+
+  // Fake manual fill.
+  SetFormValues(
+      {CREDIT_CARD_NAME_FULL, CREDIT_CARD_NUMBER,
+       CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, CREDIT_CARD_VERIFICATION_CODE},
+      /*is_autofilled=*/false, /*is_user_typed=*/true);
+
+  // Fake autofill: this fills nothing because all fields have been manually
+  // filled.
+  FillForm(FormFieldData());
+  SubmitForm();
+  CommitMetrics();
+
+  histogram_tester.ExpectTotalCount(
+      "Autofill.CreditCard.NumberFills.AtFillTimeBeforeSecurityPolicy", 0);
+  histogram_tester.ExpectTotalCount(
+      "Autofill.CreditCard.SeamlessFills.AtFillTimeBeforeSecurityPolicy", 0);
+
+  histogram_tester.ExpectTotalCount(
+      "Autofill.CreditCard.NumberFills.AtFillTimeAfterSecurityPolicy", 0);
+  histogram_tester.ExpectTotalCount(
+      "Autofill.CreditCard.SeamlessFills.AtFillTimeAfterSecurityPolicy", 0);
+
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CreditCard.NumberFills.AtSubmissionTime", false, 1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CreditCard.NumberFills.AtSubmissionTime", true, 0);
+  histogram_tester.ExpectTotalCount(
+      "Autofill.CreditCard.SeamlessFills.AtSubmissionTime", 0);
+}
+
+// Tests that Autofill.CreditCard.SeamlessFills.* and
+// Autofill.CreditCard.NumberFills.* are emitted.
+TEST_F(AutofillMetricsCrossFrameFormTest,
+       LogCreditCardSeamlessFillsMetricIfAutofilled) {
+  base::HistogramTester histogram_tester;
+  SeeForm();
+
+  // Fake autofill: this is a kFullFill before the security policy, but only a
+  // kPartialFill after the security policy because only the NAME and the
+  // EXP_DATE fields are allowed to be filled.
+  FillForm(form_.fields[0]);
+  SetFormValues({CREDIT_CARD_NAME_FULL, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+                /*is_autofilled=*/true, /*is_user_typed=*/false);
+
+  // Fake autofill: this is a kPartialFill before the security policy and after
+  // the security policy because only the NUMBER and CVC fields are
+  // un-autofilled.
+  FillForm(form_.fields[1]);
+  SetFormValues({CREDIT_CARD_NUMBER, CREDIT_CARD_VERIFICATION_CODE},
+                /*is_autofilled=*/true, /*is_user_typed=*/false);
+
+  SubmitForm();
+  CommitMetrics();
+
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CreditCard.NumberFills.AtFillTimeBeforeSecurityPolicy", false,
+      0);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CreditCard.NumberFills.AtFillTimeBeforeSecurityPolicy", true,
+      2);
+  histogram_tester.ExpectTotalCount(
+      "Autofill.CreditCard.SeamlessFills.AtFillTimeBeforeSecurityPolicy", 2);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CreditCard.SeamlessFills.AtFillTimeBeforeSecurityPolicy",
+      static_cast<size_t>(
+          AutofillMetrics::CreditCardSeamlessFillMetric::kFullFill),
+      1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CreditCard.SeamlessFills.AtFillTimeBeforeSecurityPolicy",
+      static_cast<size_t>(
+          AutofillMetrics::CreditCardSeamlessFillMetric::kPartialFill),
+      1);
+
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CreditCard.NumberFills.AtFillTimeAfterSecurityPolicy", false,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CreditCard.NumberFills.AtFillTimeAfterSecurityPolicy", true, 1);
+  histogram_tester.ExpectTotalCount(
+      "Autofill.CreditCard.SeamlessFills.AtFillTimeAfterSecurityPolicy", 2);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CreditCard.SeamlessFills.AtFillTimeAfterSecurityPolicy",
+      static_cast<size_t>(
+          AutofillMetrics::CreditCardSeamlessFillMetric::kPartialFill),
+      2);
+
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CreditCard.NumberFills.AtSubmissionTime", false, 0);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CreditCard.NumberFills.AtSubmissionTime", true, 1);
+  histogram_tester.ExpectTotalCount(
+      "Autofill.CreditCard.SeamlessFills.AtSubmissionTime", 1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CreditCard.SeamlessFills.AtSubmissionTime",
+      static_cast<size_t>(
+          AutofillMetrics::CreditCardSeamlessFillMetric::kFullFill),
+      1);
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc b/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
index f36a727..ecb0d70 100644
--- a/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
+++ b/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
@@ -11,12 +11,25 @@
 #include "base/metrics/user_metrics_action.h"
 #include "base/ranges/algorithm.h"
 #include "components/autofill/core/browser/form_data_importer.h"
+#include "components/autofill/core/browser/metrics/form_events/form_events.h"
 #include "components/autofill/core/browser/payments/credit_card_access_manager.h"
 #include "components/autofill/core/browser/validation.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
 
 namespace autofill {
 
+namespace {
+
+ServerFieldTypeSet GetFieldTypeSet(
+    const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map) {
+  ServerFieldTypeSet set;
+  for (const auto& p : field_type_map)
+    set.insert(p.second);
+  return set;
+}
+
+}  // namespace
+
 CreditCardFormEventLogger::CreditCardFormEventLogger(
     bool is_in_main_frame,
     AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger,
@@ -99,6 +112,10 @@
     const CreditCard& credit_card,
     const FormStructure& form,
     const AutofillField& field,
+    const base::flat_map<FieldGlobalId, ServerFieldType>&
+        field_types_to_be_filled_before_security_policy,
+    const base::flat_map<FieldGlobalId, ServerFieldType>&
+        field_types_filled_after_security_policy,
     AutofillSyncSigninState sync_state) {
   CreditCard::RecordType record_type = credit_card.record_type();
   sync_state_ = sync_state;
@@ -107,6 +124,47 @@
       record_type,
       /*is_for_credit_card=*/true, form, field);
 
+  AutofillMetrics::LogCreditCardNumberFills(
+      GetFieldTypeSet(field_types_to_be_filled_before_security_policy),
+      AutofillMetrics::MeasurementTime::kFillTimeBeforeSecurityPolicy);
+  AutofillMetrics::LogCreditCardNumberFills(
+      GetFieldTypeSet(field_types_filled_after_security_policy),
+      AutofillMetrics::MeasurementTime::kFillTimeAfterSecurityPolicy);
+
+  AutofillMetrics::LogCreditCardSeamlessFills(
+      GetFieldTypeSet(field_types_to_be_filled_before_security_policy),
+      AutofillMetrics::MeasurementTime::kFillTimeBeforeSecurityPolicy);
+  absl::optional<AutofillMetrics::CreditCardSeamlessFillMetric>
+      credit_card_seamlessness = AutofillMetrics::LogCreditCardSeamlessFills(
+          GetFieldTypeSet(field_types_filled_after_security_policy),
+          AutofillMetrics::MeasurementTime::kFillTimeAfterSecurityPolicy);
+
+  if (credit_card_seamlessness) {
+    FormEvent e = NUM_FORM_EVENTS;
+    switch (*credit_card_seamlessness) {
+      using M = AutofillMetrics::CreditCardSeamlessFillMetric;
+      case M::kFullFill:
+        e = FORM_EVENT_CREDIT_CARD_SEAMLESSNESS_FULL_FILL;
+        break;
+      case M::kOptionalNameMissing:
+        e = FORM_EVENT_CREDIT_CARD_SEAMLESSNESS_OPTIONAL_NAME_MISSING;
+        break;
+      case M::kFullFillButExpDateMissing:
+        e = FORM_EVENT_CREDIT_CARD_SEAMLESSNESS_FULL_FILL_BUT_EXPDATE_MISSING;
+        break;
+      case M::kOptionalNameAndCvcMissing:
+        e = FORM_EVENT_CREDIT_CARD_SEAMLESSNESS_OPTIONAL_NAME_AND_CVC_MISSING;
+        break;
+      case M::kOptionalCvcMissing:
+        e = FORM_EVENT_CREDIT_CARD_SEAMLESSNESS_OPTIONAL_CVC_MISSING;
+        break;
+      case M::kPartialFill:
+        e = FORM_EVENT_CREDIT_CARD_SEAMLESSNESS_PARTIAL_FILL;
+        break;
+    }
+    Log(e, form);
+  }
+
   switch (record_type) {
     case CreditCard::LOCAL_CARD:
       Log(FORM_EVENT_LOCAL_SUGGESTION_FILLED, form);
diff --git a/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h b/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h
index a0a2eef..6067384d 100644
--- a/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h
+++ b/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h
@@ -64,6 +64,10 @@
   void OnDidFillSuggestion(const CreditCard& credit_card,
                            const FormStructure& form,
                            const AutofillField& field,
+                           const base::flat_map<FieldGlobalId, ServerFieldType>&
+                               field_types_to_be_filled_before_security_policy,
+                           const base::flat_map<FieldGlobalId, ServerFieldType>&
+                               field_types_filled_after_security_policy,
                            AutofillSyncSigninState sync_state);
 
   // Logging what type of authentication flow was prompted.
diff --git a/components/autofill/core/browser/metrics/form_events/form_events.h b/components/autofill/core/browser/metrics/form_events/form_events.h
index 4a08713..3a953e7 100644
--- a/components/autofill/core/browser/metrics/form_events/form_events.h
+++ b/components/autofill/core/browser/metrics/form_events/form_events.h
@@ -113,6 +113,14 @@
   // Same as above, but recorded only once per page load.
   FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD_ONCE,
 
+  // See AutofillMetrics::CreditCardSeamlessFillMetric for details.
+  FORM_EVENT_CREDIT_CARD_SEAMLESSNESS_FULL_FILL,
+  FORM_EVENT_CREDIT_CARD_SEAMLESSNESS_OPTIONAL_NAME_MISSING,
+  FORM_EVENT_CREDIT_CARD_SEAMLESSNESS_OPTIONAL_CVC_MISSING,
+  FORM_EVENT_CREDIT_CARD_SEAMLESSNESS_OPTIONAL_NAME_AND_CVC_MISSING,
+  FORM_EVENT_CREDIT_CARD_SEAMLESSNESS_FULL_FILL_BUT_EXPDATE_MISSING,
+  FORM_EVENT_CREDIT_CARD_SEAMLESSNESS_PARTIAL_FILL,
+
   NUM_FORM_EVENTS,
 };
 
diff --git a/components/autofill/core/browser/test_autofill_driver.cc b/components/autofill/core/browser/test_autofill_driver.cc
index c9a6902b..aa0828b8f 100644
--- a/components/autofill/core/browser/test_autofill_driver.cc
+++ b/components/autofill/core/browser/test_autofill_driver.cc
@@ -58,12 +58,21 @@
 }
 #endif
 
-void TestAutofillDriver::FillOrPreviewForm(
+base::flat_map<FieldGlobalId, ServerFieldType>
+TestAutofillDriver::FillOrPreviewForm(
     int query_id,
     mojom::RendererFormDataAction action,
     const FormData& form_data,
     const url::Origin& triggered_origin,
-    const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map) {}
+    const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map) {
+  base::flat_map<FieldGlobalId, ServerFieldType> result = field_type_map;
+  if (field_type_map_filter_) {
+    base::EraseIf(result, [&](const auto& p) {
+      return !field_type_map_filter_.Run(triggered_origin, p.first, p.second);
+    });
+  }
+  return result;
+}
 
 void TestAutofillDriver::PropagateAutofillPredictions(
     const std::vector<FormStructure*>& forms) {
@@ -120,6 +129,12 @@
   isolation_info_ = isolation_info;
 }
 
+void TestAutofillDriver::SetFieldTypeMapFilter(
+    base::RepeatingCallback<
+        bool(const url::Origin&, FieldGlobalId, ServerFieldType)> callback) {
+  field_type_map_filter_ = callback;
+}
+
 void TestAutofillDriver::SetSharedURLLoaderFactory(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
   test_shared_loader_factory_ = url_loader_factory;
diff --git a/components/autofill/core/browser/test_autofill_driver.h b/components/autofill/core/browser/test_autofill_driver.h
index 5dd56b0..1d3f9c1 100644
--- a/components/autofill/core/browser/test_autofill_driver.h
+++ b/components/autofill/core/browser/test_autofill_driver.h
@@ -45,12 +45,15 @@
   webauthn::InternalAuthenticator* GetOrCreateCreditCardInternalAuthenticator()
       override;
 #endif
-  void FillOrPreviewForm(int query_id,
-                         mojom::RendererFormDataAction action,
-                         const FormData& data,
-                         const url::Origin& triggered_origin,
-                         const base::flat_map<FieldGlobalId, ServerFieldType>&
-                             field_type_map) override;
+  // The return value contains the members (field, type) of `field_type_map` for
+  // which `field_type_filter_.Run(triggered_origin, field, type)` is true.
+  base::flat_map<FieldGlobalId, ServerFieldType> FillOrPreviewForm(
+      int query_id,
+      mojom::RendererFormDataAction action,
+      const FormData& data,
+      const url::Origin& triggered_origin,
+      const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map)
+      override;
   void PropagateAutofillPredictions(
       const std::vector<autofill::FormStructure*>& forms) override;
   void HandleParsedForms(const std::vector<const FormData*>& forms) override;
@@ -81,6 +84,11 @@
   void SetIsInMainFrame(bool is_in_main_frame);
   void SetIsolationInfo(const net::IsolationInfo& isolation_info);
 
+  // The filter that determines the return value of FillOrPreviewForm().
+  void SetFieldTypeMapFilter(
+      base::RepeatingCallback<
+          bool(const url::Origin&, FieldGlobalId, ServerFieldType)> callback);
+
   void SetSharedURLLoaderFactory(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
 #if !defined(OS_IOS)
@@ -93,6 +101,9 @@
   bool is_incognito_ = false;
   bool is_in_main_frame_ = false;
   net::IsolationInfo isolation_info_;
+  base::RepeatingCallback<
+      bool(const url::Origin&, FieldGlobalId, ServerFieldType)>
+      field_type_map_filter_;
 
 #if !defined(OS_IOS)
   std::unique_ptr<webauthn::InternalAuthenticator> test_authenticator_;
diff --git a/components/autofill/ios/browser/autofill_driver_ios.h b/components/autofill/ios/browser/autofill_driver_ios.h
index f25fa74..99c5e2c 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.h
+++ b/components/autofill/ios/browser/autofill_driver_ios.h
@@ -48,12 +48,13 @@
   ui::AXTreeID GetAxTreeId() const override;
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   bool RendererIsAvailable() override;
-  void FillOrPreviewForm(int query_id,
-                         mojom::RendererFormDataAction action,
-                         const FormData& data,
-                         const url::Origin& triggered_origin,
-                         const base::flat_map<FieldGlobalId, ServerFieldType>&
-                             field_type_map) override;
+  base::flat_map<FieldGlobalId, ServerFieldType> FillOrPreviewForm(
+      int query_id,
+      mojom::RendererFormDataAction action,
+      const FormData& data,
+      const url::Origin& triggered_origin,
+      const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map)
+      override;
   void PropagateAutofillPredictions(
       const std::vector<autofill::FormStructure*>& forms) override;
   void HandleParsedForms(const std::vector<const FormData*>& forms) override;
diff --git a/components/autofill/ios/browser/autofill_driver_ios.mm b/components/autofill/ios/browser/autofill_driver_ios.mm
index 4fcf8c4..b791befd 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.mm
+++ b/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -97,17 +97,18 @@
   return true;
 }
 
-void AutofillDriverIOS::FillOrPreviewForm(
+base::flat_map<FieldGlobalId, ServerFieldType>
+AutofillDriverIOS::FillOrPreviewForm(
     int query_id,
     mojom::RendererFormDataAction action,
     const FormData& data,
     const url::Origin& triggered_origin,
     const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map) {
   web::WebFrame* web_frame = web::GetWebFrameWithId(web_state_, web_frame_id_);
-  if (!web_frame) {
-    return;
+  if (web_frame) {
+    [bridge_ fillFormData:data inFrame:web_frame];
   }
-  [bridge_ fillFormData:data inFrame:web_frame];
+  return field_type_map;
 }
 
 void AutofillDriverIOS::PropagateAutofillPredictions(
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/NativeBackgroundTask.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/NativeBackgroundTask.java
index 6a42eea..7aee8e6 100644
--- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/NativeBackgroundTask.java
+++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/NativeBackgroundTask.java
@@ -15,7 +15,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Base class implementing {@link BackgroundTask} that adds native initialization, ensuring that
@@ -48,7 +47,7 @@
     private boolean mRunningInMinimalBrowserMode;
 
     /** Make sure that we do not double record task finished metric */
-    private AtomicBoolean mFinishMetricRecorded = new AtomicBoolean(false);
+    private boolean mFinishMetricRecorded;
 
     /** Loads native and handles initialization. */
     private NativeBackgroundTaskDelegate mDelegate;
@@ -72,8 +71,10 @@
         mTaskId = taskParameters.getTaskId();
 
         TaskFinishedCallback wrappedCallback = needsReschedule -> {
-            recordTaskFinishedMetric();
-            callback.taskFinished(needsReschedule);
+            PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> {
+                recordTaskFinishedMetric();
+                callback.taskFinished(needsReschedule);
+            });
         };
 
         // WrappedCallback will only be called when the work is done or in onStopTask. If the task
@@ -236,7 +237,9 @@
     }
 
     private void recordTaskFinishedMetric() {
-        if (!mFinishMetricRecorded.getAndSet(true)) {
+        ThreadUtils.assertOnUiThread();
+        if (!mFinishMetricRecorded) {
+            mFinishMetricRecorded = true;
             getUmaReporter().reportNativeTaskFinished(mTaskId, mRunningInMinimalBrowserMode);
         }
     }
diff --git a/components/cdm/browser/media_drm_storage_impl.cc b/components/cdm/browser/media_drm_storage_impl.cc
index e0974c4..94dde34 100644
--- a/components/cdm/browser/media_drm_storage_impl.cc
+++ b/components/cdm/browser/media_drm_storage_impl.cc
@@ -461,9 +461,8 @@
 
 // Returns the origin ID for |origin|, if it exists. Will return an empty value
 // if the origin ID can not be found in |storage_dict|.
-base::UnguessableToken GetOriginIdForOrigin(
-    const base::DictionaryValue* storage_dict,
-    const url::Origin& origin) {
+base::UnguessableToken GetOriginIdForOrigin(const base::Value* storage_dict,
+                                            const url::Origin& origin) {
   DCHECK(storage_dict);
 
   const base::Value* origin_dict = storage_dict->FindKeyOfType(
@@ -526,7 +525,7 @@
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
     // Check if the preference has an existing origin ID.
-    const base::DictionaryValue* storage_dict =
+    const base::Value* storage_dict =
         pref_service->GetDictionary(prefs::kMediaDrmStorage);
     base::UnguessableToken origin_id =
         GetOriginIdForOrigin(storage_dict, origin);
@@ -612,7 +611,7 @@
     const PrefService* pref_service) {
   DCHECK(pref_service);
 
-  const base::DictionaryValue* storage_dict =
+  const base::Value* storage_dict =
       pref_service->GetDictionary(prefs::kMediaDrmStorage);
   if (!storage_dict)
     return std::set<GURL>();
@@ -634,7 +633,7 @@
     base::Time end) {
   DCHECK(pref_service);
 
-  const base::DictionaryValue* storage_dict =
+  const base::Value* storage_dict =
       pref_service->GetDictionary(prefs::kMediaDrmStorage);
   if (!storage_dict)
     return {};
diff --git a/components/cdm/browser/media_drm_storage_impl_unittest.cc b/components/cdm/browser/media_drm_storage_impl_unittest.cc
index df61f8f..fb61cad1 100644
--- a/components/cdm/browser/media_drm_storage_impl_unittest.cc
+++ b/components/cdm/browser/media_drm_storage_impl_unittest.cc
@@ -122,7 +122,7 @@
     base::RunLoop().RunUntilIdle();
 
     // Verify the origin dictionary is created.
-    const base::DictionaryValue* storage_dict =
+    const base::Value* storage_dict =
         pref_service_->GetDictionary(prefs::kMediaDrmStorage);
     EXPECT_TRUE(storage_dict->FindKey(kTestOrigin));
 
@@ -290,7 +290,7 @@
   base::RunLoop().RunUntilIdle();
 
   // Verify the origin dictionary is created.
-  const base::DictionaryValue* storage_dict =
+  const base::Value* storage_dict =
       pref_service_->GetDictionary(prefs::kMediaDrmStorage);
   EXPECT_TRUE(storage_dict->FindKey(kTestOrigin));
 }
diff --git a/components/certificate_transparency/BUILD.gn b/components/certificate_transparency/BUILD.gn
index e560bd9..49c7a7e 100644
--- a/components/certificate_transparency/BUILD.gn
+++ b/components/certificate_transparency/BUILD.gn
@@ -12,7 +12,7 @@
   ]
 }
 
-static_library("certificate_transparency") {
+component("certificate_transparency") {
   sources = [
     "chrome_ct_policy_enforcer.cc",
     "chrome_ct_policy_enforcer.h",
@@ -26,7 +26,10 @@
     "pref_names.h",
   ]
 
+  defines = [ "IS_CERTIFICATE_TRANSPARENCY_IMPL" ]
+
   deps = [
+    ":proto",
     "//base",
     "//components/base32",
     "//components/certificate_transparency/data:ct_log_list",
@@ -36,8 +39,6 @@
     "//net",
     "//url",
   ]
-
-  public_deps = [ ":proto" ]
 }
 
 source_set("unit_tests") {
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer.h b/components/certificate_transparency/chrome_ct_policy_enforcer.h
index 807578c..90e66dc 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer.h
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer.h
@@ -10,6 +10,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/component_export.h"
 #include "base/memory/raw_ptr.h"
 #include "base/time/clock.h"
 #include "base/time/time.h"
@@ -17,7 +18,7 @@
 
 namespace certificate_transparency {
 
-struct OperatorHistoryEntry {
+struct COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY) OperatorHistoryEntry {
   // Name of the current operator for the log.
   std::string current_operator_;
   // Vector of previous operators (if any) for the log, represented as pairs of
@@ -37,7 +38,8 @@
 // for the set of known, qualified logs - either through a reliable binary
 // updating mechanism or through out-of-band delivery. See
 // //net/docs/certificate-transparency.md for more details.
-class ChromeCTPolicyEnforcer : public net::CTPolicyEnforcer {
+class COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY) ChromeCTPolicyEnforcer
+    : public net::CTPolicyEnforcer {
  public:
   // |logs| is a list of Certificate Transparency logs.  Data about each log is
   // needed to apply Chrome's policies. |disqualified_logs| is a map of log ID
diff --git a/components/certificate_transparency/chrome_require_ct_delegate.h b/components/certificate_transparency/chrome_require_ct_delegate.h
index 90b6b83..8b3fed9b 100644
--- a/components/certificate_transparency/chrome_require_ct_delegate.h
+++ b/components/certificate_transparency/chrome_require_ct_delegate.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/component_export.h"
 #include "base/memory/ref_counted.h"
 #include "components/url_matcher/url_matcher.h"
 #include "net/base/hash_value.h"
@@ -32,7 +33,7 @@
 // To support Enterprise configuration, additional requirements or exceptions
 // can be provided via |UpdateCTPolicies()|, which uses the configuration
 // syntax documented in pref_names.h for each of the options.
-class ChromeRequireCTDelegate
+class COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY) ChromeRequireCTDelegate
     : public net::TransportSecurityState::RequireCTDelegate {
  public:
   explicit ChromeRequireCTDelegate();
diff --git a/components/certificate_transparency/ct_features.h b/components/certificate_transparency/ct_features.h
index 79774e1e..1738fcd 100644
--- a/components/certificate_transparency/ct_features.h
+++ b/components/certificate_transparency/ct_features.h
@@ -5,11 +5,13 @@
 #ifndef COMPONENTS_CERTIFICATE_TRANSPARENCY_CT_FEATURES_H_
 #define COMPONENTS_CERTIFICATE_TRANSPARENCY_CT_FEATURES_H_
 
+#include "base/component_export.h"
 #include "base/feature_list.h"
 
 namespace certificate_transparency {
 namespace features {
 
+COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
 extern const base::Feature kCertificateTransparencyComponentUpdater;
 
 // If enabled, the 2022 CT policy which removes the one Google log
@@ -17,12 +19,14 @@
 // the number of embedded SCTs required for certificates with a lifetime over
 // 180 days (from 2 to 3) will be used for any certificate issued after February
 // 1, 2022.
+COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
 extern const base::Feature kCertificateTransparency2022Policy;
 
 // If enabled, the 2022 CT policy which removes the one Google log
 // requirement, introduces log operator diversity requirements, and increases
 // the number of embedded SCTs required for certificates with a lifetime over
 // 180 days (from 2 to 3) will be used for all certificates.
+COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
 extern const base::Feature kCertificateTransparency2022PolicyAllCerts;
 
 }  // namespace features
diff --git a/components/certificate_transparency/ct_known_logs.h b/components/certificate_transparency/ct_known_logs.h
index 3ba1d10..f6864cd 100644
--- a/components/certificate_transparency/ct_known_logs.h
+++ b/components/certificate_transparency/ct_known_logs.h
@@ -7,6 +7,7 @@
 
 #include <vector>
 
+#include "base/component_export.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/string_piece.h"
 #include "base/time/time.h"
@@ -39,16 +40,18 @@
 };
 
 // Returns the time at which the log list was last updated.
-base::Time GetLogListTimestamp();
+COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY) base::Time GetLogListTimestamp();
 
 // Returns information about all known logs, which includes those that are
 // presently qualified for inclusion and logs which were previously qualified,
 // but have since been disqualified. To determine the status of a given log
 // (via its log ID), use |GetDisqualifiedLogs()|.
+COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
 std::vector<CTLogInfo> GetKnownLogs();
 
 // Returns the log IDs of all logs that are operated by Google, sorted.  The log
 // ID is the SHA-256 hash of the log's |log_key|.
+COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
 std::vector<std::string> GetLogsOperatedByGoogle();
 
 // Returns pairs of (log ID, disqualification date) for all disqualified logs,
@@ -59,6 +62,7 @@
 // Any SCTs that are embedded in certificates issued after the disqualification
 // date should not be trusted, nor contribute to any uniqueness or freshness
 // requirements.
+COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
 std::vector<std::pair<std::string, base::TimeDelta>> GetDisqualifiedLogs();
 
 }  // namespace certificate_transparency
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 8125e47..957d363 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
 {
-  "version": "4.61",
-  "log_list_timestamp": "2021-12-17T01:34:48Z",
+  "version": "4.64",
+  "log_list_timestamp": "2021-12-20T01:34:42Z",
   "operators": [
     {
       "name": "Google",
diff --git a/components/certificate_transparency/pref_names.h b/components/certificate_transparency/pref_names.h
index b7153c5..75a2c4d8 100644
--- a/components/certificate_transparency/pref_names.h
+++ b/components/certificate_transparency/pref_names.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_CERTIFICATE_TRANSPARENCY_PREF_NAMES_H_
 #define COMPONENTS_CERTIFICATE_TRANSPARENCY_PREF_NAMES_H_
 
+#include "base/component_export.h"
+
 class PrefRegistrySimple;
 
 namespace certificate_transparency {
@@ -12,16 +14,17 @@
 
 // Registers the preferences related to Certificate Transparency policy
 // in the given pref registry.
+COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
 void RegisterPrefs(PrefRegistrySimple* registry);
 
 // The set of hosts (as URLBlacklist-syntax filters) for which Certificate
 // Transparency is required to be present.
-extern const char kCTRequiredHosts[];
+COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY) extern const char kCTRequiredHosts[];
 
 // The set of hosts (as URLBlacklist-syntax filters) for which Certificate
 // Transparency information is allowed to be absent, even if it would
 // otherwise be required (e.g. as part of security policy).
-extern const char kCTExcludedHosts[];
+COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY) extern const char kCTExcludedHosts[];
 
 // The set of subjectPublicKeyInfo hashes in the form of
 // <hash-name>"/"<base64-hash-value>. If a certificate matches this SPKI, then
@@ -35,7 +38,7 @@
 // 2) The matching certificate contains one or more organizationName
 //    attributes in the Subject, and those attributes are identical in
 //    ordering, number of values, and byte-for-byte equality of values.
-extern const char kCTExcludedSPKIs[];
+COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY) extern const char kCTExcludedSPKIs[];
 
 // The set of subjectPublicKeyInfo hashes in the form of
 // <hash-name>"/"<base64-hash-value>. If a certificate matches this SPKI, then
@@ -45,6 +48,7 @@
 // 2) The SPKI listed is not actively trusted in the current version of the
 //    ChromiumOS or Android root stores.
 //    (see '"legacy": true' in root_stores.json)
+COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
 extern const char kCTExcludedLegacySPKIs[];
 
 }  // namespace prefs
diff --git a/components/component_updater/component_installer.cc b/components/component_updater/component_installer.cc
index 4c08b85..2ff6189a 100644
--- a/components/component_updater/component_installer.cc
+++ b/components/component_updater/component_installer.cc
@@ -40,6 +40,10 @@
 #include "components/update_client/utils.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
+#if defined(OS_APPLE)
+#include "base/mac/backup_util.h"
+#endif
+
 namespace component_updater {
 
 namespace {
@@ -164,6 +168,12 @@
   DCHECK(!base::PathExists(unpack_path));
   DCHECK(base::PathExists(local_install_path));
 
+#if defined(OS_APPLE)
+  // Since components can be large and can be re-downloaded when needed, they
+  // are excluded from backups.
+  base::mac::SetBackupExclusion(local_install_path);
+#endif
+
   const Result result =
       installer_policy_->OnCustomInstall(local_manifest, local_install_path);
   if (result.error)
diff --git a/components/components_chromium_strings.grd b/components/components_chromium_strings.grd
index f627c9c..0d7eddf 100644
--- a/components/components_chromium_strings.grd
+++ b/components/components_chromium_strings.grd
@@ -36,7 +36,7 @@
       <output filename="components_chromium_strings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="components_chromium_strings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="components_chromium_strings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="components_chromium_strings_am.pak" type="data_package" lang="am" />
diff --git a/components/components_google_chrome_strings.grd b/components/components_google_chrome_strings.grd
index 652475a..71dcb21 100644
--- a/components/components_google_chrome_strings.grd
+++ b/components/components_google_chrome_strings.grd
@@ -36,7 +36,7 @@
       <output filename="components_google_chrome_strings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="components_google_chrome_strings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="components_google_chrome_strings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="components_google_chrome_strings_am.pak" type="data_package" lang="am" />
diff --git a/components/components_locale_settings.grd b/components/components_locale_settings.grd
index 5f19a473..ba55129 100644
--- a/components/components_locale_settings.grd
+++ b/components/components_locale_settings.grd
@@ -34,7 +34,7 @@
       <output filename="components_locale_settings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="components_locale_settings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="components_locale_settings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="components_locale_settings_am.pak" type="data_package" lang="am" />
diff --git a/components/components_strings.grd b/components/components_strings.grd
index 5f3971e0..5dc186a 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -36,7 +36,7 @@
       <output filename="components_strings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="components_strings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="components_strings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="components_strings_am.pak" type="data_package" lang="am" />
diff --git a/components/consent_auditor/consent_auditor_impl_unittest.cc b/components/consent_auditor/consent_auditor_impl_unittest.cc
index 668a71e9..0a48200 100644
--- a/components/consent_auditor/consent_auditor_impl_unittest.cc
+++ b/components/consent_auditor/consent_auditor_impl_unittest.cc
@@ -172,7 +172,7 @@
   consent_auditor()->RecordLocalConsent("feature1", kFeature1Description,
                                         kFeature1Confirmation);
   ASSERT_TRUE(pref_service()->HasPrefPath(prefs::kLocalConsentsDictionary));
-  const base::DictionaryValue* consents =
+  const base::Value* consents =
       pref_service()->GetDictionary(prefs::kLocalConsentsDictionary);
   ASSERT_TRUE(consents);
   std::string description;
diff --git a/components/content_settings/core/browser/content_settings_pref.cc b/components/content_settings/core/browser/content_settings_pref.cc
index 8a31604..72c8a202 100644
--- a/components/content_settings/core/browser/content_settings_pref.cc
+++ b/components/content_settings/core/browser/content_settings_pref.cc
@@ -263,7 +263,7 @@
 
   // The returned value could be nullptr if the pref has never been set.
   const base::DictionaryValue* all_settings_dictionary =
-      prefs_->GetDictionary(pref_name_);
+      &base::Value::AsDictionaryValue(*prefs_->GetDictionary(pref_name_));
   if (!all_settings_dictionary)
     return;
 
diff --git a/components/cronet/cronet_prefs_manager.cc b/components/cronet/cronet_prefs_manager.cc
index 8e768af..c6b56303 100644
--- a/components/cronet/cronet_prefs_manager.cc
+++ b/components/cronet/cronet_prefs_manager.cc
@@ -182,8 +182,8 @@
   std::unique_ptr<base::DictionaryValue> GetDictionaryValue() override {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
     UMA_HISTOGRAM_EXACT_LINEAR("NQE.Prefs.ReadCount", 1, 2);
-    return pref_service_->GetDictionary(kNetworkQualitiesPref)
-        ->CreateDeepCopy();
+    return base::DictionaryValue::From(base::Value::ToUniquePtrValue(
+        pref_service_->GetDictionary(kNetworkQualitiesPref)->Clone()));
   }
 
  private:
diff --git a/components/cronet/host_cache_persistence_manager.cc b/components/cronet/host_cache_persistence_manager.cc
index dcaf6c10..7c1777af 100644
--- a/components/cronet/host_cache_persistence_manager.cc
+++ b/components/cronet/host_cache_persistence_manager.cc
@@ -56,7 +56,7 @@
     return;
 
   net_log_.BeginEvent(net::NetLogEventType::HOST_CACHE_PREF_READ);
-  const base::ListValue* pref_value = pref_service_->GetList(pref_name_);
+  const base::Value* pref_value = pref_service_->GetList(pref_name_);
   bool success = cache_->RestoreFromListValue(*pref_value);
   net_log_.AddEntryWithBoolParams(net::NetLogEventType::HOST_CACHE_PREF_READ,
                                   net::NetLogEventPhase::END, "success",
diff --git a/components/custom_handlers/protocol_handler_registry.cc b/components/custom_handlers/protocol_handler_registry.cc
index 81d7111..bdd5ea97 100644
--- a/components/custom_handlers/protocol_handler_registry.cc
+++ b/components/custom_handlers/protocol_handler_registry.cc
@@ -621,7 +621,7 @@
     return result;
   }
 
-  const base::ListValue* handlers = prefs->GetList(pref_name);
+  const base::Value* handlers = prefs->GetList(pref_name);
   if (handlers) {
     for (const auto& dict : handlers->GetList()) {
       if (!dict.is_dict())
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
index 3801482a..29117fd 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
@@ -170,7 +170,7 @@
 
 // Records the key-value pairs in the dictionary in a sparse histogram.
 void RecordDictionaryToHistogram(const std::string& histogram_name,
-                                 const base::DictionaryValue* dictionary) {
+                                 const base::Value* dictionary) {
   base::HistogramBase* histogram = base::SparseHistogram::FactoryGet(
       histogram_name, base::HistogramBase::kUmaTargetedHistogramFlag);
   for (auto entry : dictionary->DictItems()) {
@@ -395,9 +395,9 @@
 }
 
 void DataReductionProxyCompressionStats::InitListPref(const char* pref) {
-  std::unique_ptr<base::ListValue> pref_value =
-      pref_service_->GetList(pref)->CreateDeepCopy();
-  list_pref_map_[pref] = std::move(pref_value);
+  base::Value pref_value = pref_service_->GetList(pref)->Clone();
+  list_pref_map_[pref] = base::ListValue::From(
+      base::Value::ToUniquePtrValue(std::move(pref_value)));
 }
 
 int64_t DataReductionProxyCompressionStats::GetInt64(const char* pref_path) {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc
index 61dcd11..d45ccd464 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc
@@ -186,7 +186,8 @@
   // |simple_pref_service| for |pref|.
   void VerifyPrefListWasWritten(const char* pref) {
     const base::ListValue* delayed_list = compression_stats_->GetList(pref);
-    const base::ListValue* written_list = pref_service()->GetList(pref);
+    const base::ListValue* written_list =
+        &base::Value::AsListValue(*pref_service()->GetList(pref));
     ASSERT_EQ(delayed_list->GetList().size(), written_list->GetList().size());
     size_t count = delayed_list->GetList().size();
 
@@ -374,9 +375,11 @@
   void VerifyDictionaryPref(const std::string& pref,
                             int key,
                             int expected_value) const {
-    const base::DictionaryValue* dict =
+    const base::Value* dict =
         compression_stats_->pref_service_->GetDictionary(pref);
-    EXPECT_EQ(expected_value != 0, dict->HasKey(base::NumberToString(key)));
+
+    const base::Value* value = dict->FindKey(base::NumberToString(key));
+    EXPECT_EQ(expected_value != 0, !!value);
     if (expected_value) {
       EXPECT_EQ(expected_value,
                 dict->FindKey(base::NumberToString(key))->GetInt());
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs_unittest.cc
index 455de14..b749cf6 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs_unittest.cc
@@ -49,7 +49,7 @@
   void VerifyList(const char* pref_name,
                   int64_t starting_value,
                   PrefService* pref_service) {
-    const base::ListValue* list_value = pref_service->GetList(pref_name);
+    const base::Value* list_value = pref_service->GetList(pref_name);
     base::Value::ConstListView list_view = list_value->GetList();
     for (int64_t i = 0; i < 10L; ++i) {
       std::string string_value;
diff --git a/components/data_use_measurement/core/data_use_tracker_prefs.cc b/components/data_use_measurement/core/data_use_tracker_prefs.cc
index 1a1a498..af6c42169 100644
--- a/components/data_use_measurement/core/data_use_tracker_prefs.cc
+++ b/components/data_use_measurement/core/data_use_tracker_prefs.cc
@@ -69,8 +69,7 @@
   if (!pref_service_)
     return;
 
-  const base::DictionaryValue* user_pref_dict =
-      pref_service_->GetDictionary(pref_name);
+  const base::Value* user_pref_dict = pref_service_->GetDictionary(pref_name);
   const base::Time current_date = GetCurrentMeasurementDate();
   const base::Time last_date = current_date - base::Days(60);
 
@@ -107,8 +106,7 @@
   DictionaryPrefUpdate pref_updater(pref_service_, pref_name);
   std::string todays_key = GetCurrentMeasurementDateAsString();
 
-  const base::DictionaryValue* user_pref_dict =
-      pref_service_->GetDictionary(pref_name);
+  const base::Value* user_pref_dict = pref_service_->GetDictionary(pref_name);
   double todays_traffic = user_pref_dict->FindDoubleKey(todays_key).value_or(0);
   pref_updater->SetDouble(
       todays_key,
diff --git a/components/embedder_support/origin_trials/component_updater_utils_unittest.cc b/components/embedder_support/origin_trials/component_updater_utils_unittest.cc
index 6216df3..0843dc1 100644
--- a/components/embedder_support/origin_trials/component_updater_utils_unittest.cc
+++ b/components/embedder_support/origin_trials/component_updater_utils_unittest.cc
@@ -96,7 +96,7 @@
     ASSERT_TRUE(local_state()->HasPrefPath(
         embedder_support::prefs::kOriginTrialDisabledFeatures));
 
-    const base::ListValue* disabled_feature_list = local_state()->GetList(
+    const base::Value* disabled_feature_list = local_state()->GetList(
         embedder_support::prefs::kOriginTrialDisabledFeatures);
     ASSERT_TRUE(disabled_feature_list);
 
@@ -130,7 +130,7 @@
     ASSERT_TRUE(local_state()->HasPrefPath(
         embedder_support::prefs::kOriginTrialDisabledTokens));
 
-    const base::ListValue* disabled_token_list = local_state()->GetList(
+    const base::Value* disabled_token_list = local_state()->GetList(
         embedder_support::prefs::kOriginTrialDisabledTokens);
     ASSERT_TRUE(disabled_token_list);
 
diff --git a/components/enterprise/content/clipboard_restriction_service.cc b/components/enterprise/content/clipboard_restriction_service.cc
index c30c99a..766edfc 100644
--- a/components/enterprise/content/clipboard_restriction_service.cc
+++ b/components/enterprise/content/clipboard_restriction_service.cc
@@ -69,7 +69,7 @@
     return;
   }
 
-  const base::DictionaryValue* settings = pref_service_->GetDictionary(
+  const base::Value* settings = pref_service_->GetDictionary(
       enterprise::content::kCopyPreventionSettings);
   const base::Value* enable = settings->FindListKey(
       enterprise::content::kCopyPreventionSettingsEnableFieldName);
diff --git a/components/exo/pointer_unittest.cc b/components/exo/pointer_unittest.cc
index dae28a1..89e62df 100644
--- a/components/exo/pointer_unittest.cc
+++ b/components/exo/pointer_unittest.cc
@@ -26,6 +26,7 @@
 #include "components/exo/test/exo_test_base.h"
 #include "components/exo/test/exo_test_data_exchange_delegate.h"
 #include "components/exo/test/exo_test_helper.h"
+#include "components/exo/test/shell_surface_builder.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/service/surfaces/surface.h"
 #include "components/viz/service/surfaces/surface_manager.h"
@@ -136,6 +137,63 @@
   }
 };
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+class PointerConstraintTest : public PointerTest {
+ public:
+  PointerConstraintTest() = default;
+
+  PointerConstraintTest(const PointerConstraintTest&) = delete;
+  PointerConstraintTest& operator=(const PointerConstraintTest&) = delete;
+
+  void SetUp() override {
+    PointerTest::SetUp();
+    feature_list_.InitAndEnableFeature(chromeos::features::kExoPointerLock);
+
+    shell_surface_ = test::ShellSurfaceBuilder({10, 10}).BuildShellSurface();
+    surface_ = shell_surface_->surface_for_testing();
+    seat_ = std::make_unique<Seat>();
+    pointer_ = std::make_unique<Pointer>(&delegate_, seat_.get());
+
+    focus_client_ =
+        aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
+    focus_client_->FocusWindow(surface_->window());
+
+    generator_ = std::make_unique<ui::test::EventGenerator>(
+        ash::Shell::GetPrimaryRootWindow());
+
+    EXPECT_CALL(delegate_, CanAcceptPointerEventsForSurface(surface_))
+        .WillRepeatedly(testing::Return(true));
+
+    EXPECT_CALL(constraint_delegate_, GetConstrainedSurface())
+        .WillRepeatedly(testing::Return(surface_));
+  }
+
+  void TearDown() override {
+    // Many objects need to be destroyed before teardown for various reasons.
+    seat_.reset();
+    shell_surface_.reset();
+    surface_ = nullptr;
+
+    // Some tests generate mouse events which Pointer::OnMouseEvent() handles
+    // during the run loop. That routine accesses WMHelper. So, make sure any
+    // such pending tasks finish before TearDown() destroys the WMHelper.
+    base::RunLoop().RunUntilIdle();
+
+    PointerTest::TearDown();
+  }
+
+  std::unique_ptr<ui::test::EventGenerator> generator_;
+  std::unique_ptr<Pointer> pointer_;
+  std::unique_ptr<Seat> seat_;
+  MockPointerConstraintDelegate constraint_delegate_;
+  MockPointerDelegate delegate_;
+  std::unique_ptr<ShellSurface> shell_surface_;
+  Surface* surface_;
+  base::test::ScopedFeatureList feature_list_;
+  aura::client::FocusClient* focus_client_;
+};
+#endif
+
 TEST_F(PointerTest, SetCursor) {
   std::unique_ptr<Surface> surface(new Surface);
   std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
@@ -1134,212 +1192,102 @@
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-TEST_F(PointerTest, ConstrainPointer) {
-  auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>();
-  scoped_feature_list->InitAndEnableFeature(
-      chromeos::features::kExoPointerLock);
+TEST_F(PointerConstraintTest, ConstrainPointer) {
+  EXPECT_TRUE(pointer_->ConstrainPointer(&constraint_delegate_));
 
-  auto surface = std::make_unique<Surface>();
-  auto shell_surface = std::make_unique<ShellSurface>(surface.get());
-  gfx::Size buffer_size(10, 10);
-  auto buffer = std::make_unique<Buffer>(
-      exo_test_helper()->CreateGpuMemoryBuffer(buffer_size));
-  surface->Attach(buffer.get());
-  surface->Commit();
+  EXPECT_CALL(delegate_, OnPointerEnter(surface_, gfx::PointF(), 0));
+  EXPECT_CALL(delegate_, OnPointerFrame());
+  generator_->MoveMouseTo(surface_->window()->GetBoundsInScreen().origin());
 
-  MockPointerDelegate delegate;
-  MockPointerConstraintDelegate constraint_delegate;
-  Seat seat;
-  auto pointer = std::make_unique<Pointer>(&delegate, &seat);
-  aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow())
-      ->FocusWindow(surface->window());
-  ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
+  EXPECT_CALL(delegate_, OnPointerMotion(testing::_, testing::_)).Times(0);
+  generator_->MoveMouseTo(surface_->window()->GetBoundsInScreen().origin() +
+                          gfx::Vector2d(-1, -1));
 
-  EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
+  auto child_shell_surface = test::ShellSurfaceBuilder({15, 15})
+                                 .SetParent(shell_surface_.get())
+                                 .SetDisableMovement()
+                                 .SetCanMinimize(false)
+                                 .BuildShellSurface();
+  Surface* child_surface = child_shell_surface->surface_for_testing();
+  EXPECT_CALL(delegate_, CanAcceptPointerEventsForSurface(child_surface))
       .WillRepeatedly(testing::Return(true));
 
-  EXPECT_CALL(constraint_delegate, GetConstrainedSurface())
-      .WillRepeatedly(testing::Return(surface.get()));
-  EXPECT_TRUE(pointer->ConstrainPointer(&constraint_delegate));
+  generator_->MoveMouseTo(
+      child_surface->window()->GetBoundsInScreen().origin());
 
-  EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0));
-  EXPECT_CALL(delegate, OnPointerFrame());
-  generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin());
-
-  EXPECT_CALL(delegate, OnPointerMotion(testing::_, testing::_)).Times(0);
-  generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin() +
-                        gfx::Vector2d(-1, -1));
-
-  auto child_surface = std::make_unique<Surface>();
-  auto child_shell_surface = std::make_unique<ShellSurface>(
-      child_surface.get(), gfx::Point(), /*can_minimize=*/false,
-      ash::desks_util::GetActiveDeskContainerId());
-  child_shell_surface->DisableMovement();
-  child_shell_surface->SetParent(shell_surface.get());
-  gfx::Size child_buffer_size(15, 15);
-  auto child_buffer = std::make_unique<Buffer>(
-      exo_test_helper()->CreateGpuMemoryBuffer(child_buffer_size));
-  child_surface->Attach(child_buffer.get());
-  child_surface->Commit();
-
-  EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(child_surface.get()))
-      .WillRepeatedly(testing::Return(true));
-
-  generator.MoveMouseTo(child_surface->window()->GetBoundsInScreen().origin());
-
-  EXPECT_CALL(delegate, OnPointerLeave(surface.get()));
-  EXPECT_CALL(delegate, OnPointerEnter(child_surface.get(), gfx::PointF(), 0));
-  EXPECT_CALL(delegate, OnPointerFrame());
+  EXPECT_CALL(delegate_, OnPointerLeave(surface_));
+  EXPECT_CALL(delegate_, OnPointerEnter(child_surface, gfx::PointF(), 0));
+  EXPECT_CALL(delegate_, OnPointerFrame());
   // Moving the cursor to a different surface should change the focus when
   // the pointer is unconstrained.
-  pointer->UnconstrainPointer();
-  generator.MoveMouseTo(child_surface->window()->GetBoundsInScreen().origin());
+  pointer_->UnconstrainPointer();
+  generator_->MoveMouseTo(
+      child_surface->window()->GetBoundsInScreen().origin());
 
-  EXPECT_CALL(delegate, OnPointerDestroying(pointer.get()));
-  pointer.reset();
+  EXPECT_CALL(delegate_, OnPointerDestroying(pointer_.get()));
+  pointer_.reset();
 }
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-TEST_F(PointerTest, ConstrainPointerFailsWhenSurfaceIsNotActive) {
-  auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>();
-  scoped_feature_list->InitAndEnableFeature(
-      chromeos::features::kExoPointerLock);
-
-  auto surface = std::make_unique<Surface>();
-  auto shell_surface = std::make_unique<ShellSurface>(surface.get());
-  gfx::Size buffer_size(10, 10);
-  auto buffer = std::make_unique<Buffer>(
-      exo_test_helper()->CreateGpuMemoryBuffer(buffer_size));
-  surface->Attach(buffer.get());
-  surface->Commit();
-
-  MockPointerDelegate delegate;
-  MockPointerConstraintDelegate constraint_delegate;
-  Seat seat;
-  auto pointer = std::make_unique<Pointer>(&delegate, &seat);
-  aura::client::FocusClient* focus_client =
-      aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
-  ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
-
-  EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
-      .WillRepeatedly(testing::Return(true));
-
-  EXPECT_CALL(constraint_delegate, GetConstrainedSurface())
-      .WillRepeatedly(testing::Return(surface.get()));
-
-  auto second_surface = std::make_unique<Surface>();
+TEST_F(PointerConstraintTest, ConstrainPointerFailsWhenSurfaceIsNotActive) {
   auto second_shell_surface =
-      std::make_unique<ShellSurface>(second_surface.get());
-  auto second_buffer = std::make_unique<Buffer>(
-      exo_test_helper()->CreateGpuMemoryBuffer(buffer_size));
-  second_surface->Attach(second_buffer.get());
-  second_surface->Commit();
+      test::ShellSurfaceBuilder({10, 10}).BuildShellSurface();
+  Surface* second_surface = second_shell_surface->surface_for_testing();
 
-  EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(second_surface.get()))
+  EXPECT_CALL(delegate_, CanAcceptPointerEventsForSurface(second_surface))
       .WillRepeatedly(testing::Return(true));
 
   // Setting the focused window also makes it activated.
-  focus_client->FocusWindow(second_surface->window());
-  EXPECT_FALSE(pointer->ConstrainPointer(&constraint_delegate));
+  focus_client_->FocusWindow(second_surface->window());
+  EXPECT_FALSE(pointer_->ConstrainPointer(&constraint_delegate_));
 
-  focus_client->FocusWindow(surface->window());
-  EXPECT_TRUE(pointer->ConstrainPointer(&constraint_delegate));
+  focus_client_->FocusWindow(surface_->window());
+  EXPECT_TRUE(pointer_->ConstrainPointer(&constraint_delegate_));
 
-  EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0));
-  EXPECT_CALL(delegate, OnPointerFrame());
-  generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin());
+  EXPECT_CALL(delegate_, OnPointerEnter(surface_, gfx::PointF(), 0));
+  EXPECT_CALL(delegate_, OnPointerFrame());
+  generator_->MoveMouseTo(surface_->window()->GetBoundsInScreen().origin());
 
-  pointer->UnconstrainPointer();
+  pointer_->UnconstrainPointer();
 
-  EXPECT_CALL(delegate, OnPointerDestroying(pointer.get()));
-  pointer.reset();
+  EXPECT_CALL(delegate_, OnPointerDestroying(pointer_.get()));
+  pointer_.reset();
 }
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-TEST_F(PointerTest, UnconstrainPointerWhenSurfaceIsDestroyed) {
-  auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>();
-  scoped_feature_list->InitAndEnableFeature(
-      chromeos::features::kExoPointerLock);
+TEST_F(PointerConstraintTest, UnconstrainPointerWhenSurfaceIsDestroyed) {
+  EXPECT_TRUE(pointer_->ConstrainPointer(&constraint_delegate_));
 
-  auto surface = std::make_unique<Surface>();
-  auto shell_surface = std::make_unique<ShellSurface>(surface.get());
-  gfx::Size buffer_size(10, 10);
-  auto buffer = std::make_unique<Buffer>(
-      exo_test_helper()->CreateGpuMemoryBuffer(buffer_size));
-  surface->Attach(buffer.get());
-  surface->Commit();
-
-  MockPointerDelegate delegate;
-  MockPointerConstraintDelegate constraint_delegate;
-  Seat seat;
-  auto pointer = std::make_unique<Pointer>(&delegate, &seat);
-  aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow())
-      ->FocusWindow(surface->window());
-  ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
-
-  EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
-      .WillRepeatedly(testing::Return(true));
-
-  EXPECT_CALL(constraint_delegate, GetConstrainedSurface())
-      .WillRepeatedly(testing::Return(surface.get()));
-  EXPECT_TRUE(pointer->ConstrainPointer(&constraint_delegate));
-
-  EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0));
-  EXPECT_CALL(delegate, OnPointerFrame());
-  generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin());
+  EXPECT_CALL(delegate_, OnPointerEnter(surface_, gfx::PointF(), 0));
+  EXPECT_CALL(delegate_, OnPointerFrame());
+  generator_->MoveMouseTo(surface_->window()->GetBoundsInScreen().origin());
 
   // Constraint should be broken if surface is destroyed.
-  EXPECT_CALL(constraint_delegate, OnConstraintBroken());
-  EXPECT_CALL(delegate, OnPointerLeave(surface.get()));
-  EXPECT_CALL(delegate, OnPointerFrame());
-  pointer->OnSurfaceDestroying(surface.get());
+  EXPECT_CALL(constraint_delegate_, OnConstraintBroken());
+  EXPECT_CALL(delegate_, OnPointerLeave(surface_));
+  EXPECT_CALL(delegate_, OnPointerFrame());
+  shell_surface_.reset();
 
-  EXPECT_CALL(delegate, OnPointerDestroying(pointer.get()));
-  pointer.reset();
+  EXPECT_CALL(delegate_, OnPointerDestroying(pointer_.get()));
+  pointer_.reset();
 }
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-TEST_F(PointerTest, UnconstrainPointerWhenWindowLosesFocus) {
-  auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>();
-  scoped_feature_list->InitAndEnableFeature(
-      chromeos::features::kExoPointerLock);
+TEST_F(PointerConstraintTest, UnconstrainPointerWhenWindowLosesFocus) {
+  EXPECT_TRUE(pointer_->ConstrainPointer(&constraint_delegate_));
 
-  auto surface = std::make_unique<Surface>();
-  auto shell_surface = std::make_unique<ShellSurface>(surface.get());
-  gfx::Size buffer_size(10, 10);
-  auto buffer = std::make_unique<Buffer>(
-      exo_test_helper()->CreateGpuMemoryBuffer(buffer_size));
-  surface->Attach(buffer.get());
-  surface->Commit();
+  EXPECT_CALL(delegate_, OnPointerEnter(surface_, gfx::PointF(), 0));
+  EXPECT_CALL(delegate_, OnPointerFrame());
+  generator_->MoveMouseTo(surface_->window()->GetBoundsInScreen().origin());
 
-  MockPointerDelegate delegate;
-  MockPointerConstraintDelegate constraint_delegate;
-  Seat seat;
-  auto pointer = std::make_unique<Pointer>(&delegate, &seat);
-  aura::client::FocusClient* focus_client =
-      aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
-  focus_client->FocusWindow(surface->window());
-  ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
+  EXPECT_CALL(constraint_delegate_, OnConstraintBroken());
+  focus_client_->FocusWindow(nullptr);
 
-  EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
-      .WillRepeatedly(testing::Return(true));
-
-  EXPECT_CALL(constraint_delegate, GetConstrainedSurface())
-      .WillRepeatedly(testing::Return(surface.get()));
-  EXPECT_TRUE(pointer->ConstrainPointer(&constraint_delegate));
-
-  EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0));
-  EXPECT_CALL(delegate, OnPointerFrame());
-  generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin());
-
-  EXPECT_CALL(constraint_delegate, OnConstraintBroken());
-  focus_client->FocusWindow(nullptr);
-
-  EXPECT_CALL(delegate, OnPointerDestroying(pointer.get()));
-  pointer.reset();
+  EXPECT_CALL(delegate_, OnPointerDestroying(pointer_.get()));
+  pointer_.reset();
 }
 #endif
 
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc
index ec411a5..2706fa1 100644
--- a/components/feature_engagement/public/feature_list.cc
+++ b/components/feature_engagement/public/feature_list.cc
@@ -47,6 +47,11 @@
     &kIPHDownloadInfoBarDownloadContinuingFeature,
     &kIPHDownloadInfoBarDownloadsAreFasterFeature,
     &kIPHEphemeralTabFeature,
+    &kIPHFeatureNotificationGuideDefaultBrowserNotificationShownFeature,
+    &kIPHFeatureNotificationGuideSignInNotificationShownFeature,
+    &kIPHFeatureNotificationGuideIncognitoTabNotificationShownFeature,
+    &kIPHFeatureNotificationGuideNTPSuggestionCardNotificationShownFeature,
+    &kIPHFeatureNotificationGuideVoiceSearchNotificationShownFeature,
     &kIPHFeedCardMenuFeature,
     &kIPHHomepagePromoCardFeature,
     &kIPHIdentityDiscFeature,
diff --git a/components/feedback/BUILD.gn b/components/feedback/BUILD.gn
index c9388cc4..22d1516 100644
--- a/components/feedback/BUILD.gn
+++ b/components/feedback/BUILD.gn
@@ -18,6 +18,7 @@
     "feedback_uploader.h",
     "feedback_util.cc",
     "feedback_util.h",
+    "pii_types.h",
     "redaction_tool.cc",
     "redaction_tool.h",
     "system_logs/system_logs_fetcher.cc",
diff --git a/components/feedback/pii_types.h b/components/feedback/pii_types.h
new file mode 100644
index 0000000..3e00f41
--- /dev/null
+++ b/components/feedback/pii_types.h
@@ -0,0 +1,64 @@
+// 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 COMPONENTS_FEEDBACK_PII_TYPES_H_
+#define COMPONENTS_FEEDBACK_PII_TYPES_H_
+
+namespace feedback {
+
+// PII (Personally Identifiable Information) types that can be detected in the
+// debug data.
+enum class PIIType {
+  // Android App Storage paths. The path starts with either
+  // /home/root/<hash>/data/data/<package_name>/ or
+  // /home/root/<hash>/data/user_de/<number>/<package_name>/, the path
+  // components following <package_name>/ are app specific and will be
+  // considered as PII sensitive data.
+  kAndroidAppStoragePath,
+  // BSSID (Basic Service Set Identifier) of a wifi service.
+  kBSSID,
+  // Unique identifier of the cell of the Cell tower object that's used by
+  // ModemManager.
+  kCellID,
+  // Email addresses.
+  kEmail,
+  // GAIA (Google Accounts and ID Administration) ID. Gaia ID is a 64-bit
+  // integer.
+  kGaiaID,
+  // Hexadecimal strings of length 32, 40 and 64 are considered to be hashes.
+  kHash,
+  // IPP (Internet Printing Protocol) Addresses.
+  kIPPAddress,
+  // IP (Internet Protocol) address. Stores data in two versions: IPv4 (e.g.
+  // 127.0.0.1) or IPv6 (e.g. 2001:0db8:85a3:0000:0000:8a2e:0370:7334).
+  kIPAddress,
+  // The Location Area Code (LAC) for GSM and WCDMA networks of the Cell tower
+  // object that's used by ModemManager.
+  kLocationAreaCode,
+  // MAC address is a unique identifier assigned to a network interface
+  // controller (NIC) for use as a network address in communications within a
+  // network segment (e.g 00:00:5e:00:53:af). MAC addresses with general meaning
+  // like ARP failure result MAC and Broadcast MAC won't be treated as PII
+  // sensitive data and won't be included in this category.
+  kMACAddress,
+  // Window titles that appear in UI hierarchy.
+  kUIHierarchyWindowTitles,
+  // URLs that can appear in logs.
+  kURL,
+  // Universal Unique Identifiers (UUIDs). UUID can also be given by 'blkid',
+  // 'lvs' and 'pvs' tools.
+  kUUID,
+  // Serial numbers.
+  kSerial,
+  // SSID (Service Set Identifier) of wifi networks can be detected in the logs
+  // provided by wpa_supplicant and shill.
+  kSSID,
+  // Volume labels presented in the 'blkid' tool, and as part of removable
+  // media paths shown in various logs such as cros-disks (in syslog).
+  kVolumeLabel,
+};
+
+}  // namespace feedback
+
+#endif  // COMPONENTS_FEEDBACK_PII_TYPES_H_
\ No newline at end of file
diff --git a/components/feedback/redaction_tool.cc b/components/feedback/redaction_tool.cc
index 02498a5..39821ea 100644
--- a/components/feedback/redaction_tool.cc
+++ b/components/feedback/redaction_tool.cc
@@ -4,6 +4,7 @@
 
 #include "components/feedback/redaction_tool.h"
 
+#include <set>
 #include <utility>
 #include <vector>
 
@@ -14,6 +15,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/chromeos_buildflags.h"
+#include "components/feedback/pii_types.h"
 #include "net/base/ip_address.h"
 #include "third_party/re2/src/re2/re2.h"
 
@@ -47,17 +49,19 @@
 // (?:regex) denotes non-capturing parentheses group.
 CustomPatternWithAlias kCustomPatternsWithContext[] = {
     // ModemManager
-    {"CellID", "(\\bCell ID: ')([0-9a-fA-F]+)(')"},
-    {"LocAC", "(\\bLocation area code: ')([0-9a-fA-F]+)(')"},
+    {"CellID", "(\\bCell ID: ')([0-9a-fA-F]+)(')", PIIType::kCellID},
+    {"LocAC", "(\\bLocation area code: ')([0-9a-fA-F]+)(')",
+     PIIType::kLocationAreaCode},
 
     // wpa_supplicant
-    {"SSID", "(?i-s)(\\bssid[= ]')(.+)(')"},
-    {"SSID", "(?i-s)(\\bssid[= ]\")(.+)(\")"},
-    {"SSID", "(\\* SSID=)(.+)($)"},
-    {"SSIDHex", "(?-s)(\\bSSID - hexdump\\(len=[0-9]+\\): )(.+)()"},
+    {"SSID", "(?i-s)(\\bssid[= ]')(.+)(')", PIIType::kSSID},
+    {"SSID", "(?i-s)(\\bssid[= ]\")(.+)(\")", PIIType::kSSID},
+    {"SSID", "(\\* SSID=)(.+)($)", PIIType::kSSID},
+    {"SSIDHex", "(?-s)(\\bSSID - hexdump\\(len=[0-9]+\\): )(.+)()",
+     PIIType::kSSID},
 
     // shill
-    {"SSID", "(?-s)(\\[SSID=)(.+?)(\\])"},
+    {"SSID", "(?-s)(\\[SSID=)(.+?)(\\])", PIIType::kSSID},
 
     // Serial numbers. The actual serial number itself can include any alphanum
     // char as well as dashes, periods, colons, slashes and unprintable ASCII
@@ -66,34 +70,37 @@
     // many other cases that we don't want to redact.
     {"Serial",
      "(?i-s)(\\bserial\\s*_?(?:number)?['\"]?\\s*[:=|]\\s*['\"]?)"
-     "([0-9a-zA-Z\\-.:\\/\\\\\\x00-\\x09\\x0B-\\x1F]+)(\\b)"},
-    {"Serial", "( Serial Number )(\\d+)(\\b)"},
+     "([0-9a-zA-Z\\-.:\\/\\\\\\x00-\\x09\\x0B-\\x1F]+)(\\b)",
+     PIIType::kSerial},
+    {"Serial", "( Serial Number )(\\d+)(\\b)", PIIType::kSerial},
 
     // GAIA IDs
-    {"GAIA", R"xxx((\"?\bgaia_id\"?[=:]['\"])(\d+)(\b['\"]))xxx"},
-    {"GAIA", R"xxx((\{id: )(\d+)(, email:))xxx"},
+    {"GAIA", R"xxx((\"?\bgaia_id\"?[=:]['\"])(\d+)(\b['\"]))xxx",
+     PIIType::kGaiaID},
+    {"GAIA", R"xxx((\{id: )(\d+)(, email:))xxx", PIIType::kGaiaID},
 
     // UUIDs given by the 'blkid' tool. These don't necessarily look like
     // standard UUIDs, so treat them specially.
-    {"UUID", R"xxx((UUID=")([0-9a-zA-Z-]+)("))xxx"},
+    {"UUID", R"xxx((UUID=")([0-9a-zA-Z-]+)("))xxx", PIIType::kUUID},
     // Also cover UUIDs given by the 'lvs' and 'pvs' tools, which similarly
     // don't necessarily look like standard UUIDs.
-    {"UUID", R"xxx(("[lp]v_uuid":")([0-9a-zA-Z-]+)("))xxx"},
+    {"UUID", R"xxx(("[lp]v_uuid":")([0-9a-zA-Z-]+)("))xxx", PIIType::kUUID},
 
     // Volume labels presented in the 'blkid' tool, and as part of removable
     // media paths shown in various logs such as cros-disks (in syslog).
     // There isn't a well-defined format for these. For labels in blkid,
     // capture everything between the open and closing quote.
-    {"Volume Label", R"xxx((LABEL=")([^"]+)("))xxx"},
+    {"Volume Label", R"xxx((LABEL=")([^"]+)("))xxx", PIIType::kVolumeLabel},
     // For paths, this is harder. The only restricted characters are '/' and
     // NUL, so use a simple heuristic. cros-disks generally quotes paths using
     // single-quotes, so capture everything until a quote character. For lsblk,
     // capture everything until the end of the line, since the mount path is the
     // last field.
-    {"Volume Label", R"xxx((/media/removable/)(.+?)(['"/\n]|$))xxx"},
+    {"Volume Label", R"xxx((/media/removable/)(.+?)(['"/\n]|$))xxx",
+     PIIType::kVolumeLabel},
 
     // IPP (Internet Printing Protocol) Addresses
-    {"IPP Address", R"xxx((ipp:\/\/)(.+?)(\/ipp))xxx"},
+    {"IPP Address", R"xxx((ipp:\/\/)(.+?)(\/ipp))xxx", PIIType::kIPPAddress},
 };
 
 bool MaybeUnmapAddress(net::IPAddress* addr) {
@@ -360,18 +367,19 @@
 // The |kCustomPatternWithoutContext| array defines further patterns to match
 // and redact. Each pattern consists of a single capturing group.
 CustomPatternWithAlias kCustomPatternsWithoutContext[] = {
-    {"URL", "(?i)(" IRI ")"},
+    {"URL", "(?i)(" IRI ")", PIIType::kURL},
     // Email Addresses need to come after URLs because they can be part
     // of a query parameter.
-    {"email", "(?i)([0-9a-z._%+-]+@[a-z0-9.-]+\\.[a-z]{2,6})"},
+    {"email", "(?i)([0-9a-z._%+-]+@[a-z0-9.-]+\\.[a-z]{2,6})", PIIType::kEmail},
     // IP filter rules need to come after URLs so that they don't disturb the
     // URL pattern in case the IP address is part of a URL.
-    {"IPv4", "(?i)(" IPV4ADDRESS ")"},
-    {"IPv6", "(?i)(" IPV6ADDRESS ")"},
+    {"IPv4", "(?i)(" IPV4ADDRESS ")", PIIType::kIPAddress},
+    {"IPv6", "(?i)(" IPV6ADDRESS ")", PIIType::kIPAddress},
     // Universal Unique Identifiers (UUIDs).
     {"UUID",
      "(?i)([0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-"
-     "[0-9a-zA-Z]{12})"},
+     "[0-9a-zA-Z]{12})",
+     PIIType::kUUID},
 };
 
 // Like RE2's FindAndConsume, searches for the first occurrence of |pattern| in
@@ -445,15 +453,33 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::AssertLongCPUWorkAllowed();
 
-  std::string redacted = RedactMACAddresses(input);
-  redacted = RedactAndroidAppStoragePaths(std::move(redacted));
+  std::string redacted = RedactMACAddresses(input, nullptr);
+  redacted = RedactAndroidAppStoragePaths(std::move(redacted), nullptr);
   redacted = RedactCustomPatterns(std::move(redacted));
   // Do hashes last since they may appear in URLs and they also prevent us from
   // properly recognizing the Android storage paths.
-  redacted = RedactHashes(std::move(redacted));
+  redacted = RedactHashes(std::move(redacted), nullptr);
   return redacted;
 }
 
+std::map<PIIType, std::set<std::string>> RedactionTool::Detect(
+    const std::string& input) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  base::AssertLongCPUWorkAllowed();
+
+  std::map<PIIType, std::set<std::string>> detected;
+
+  RedactMACAddresses(input, &detected);
+  // This function will add to |detected| only on Chrome OS as Android app
+  // storage paths are only detected for Chrome OS.
+  RedactAndroidAppStoragePaths(input, &detected);
+  DetectWithCustomPatterns(input, &detected);
+  // Do hashes last since they may appear in URLs and they also prevent us from
+  // properly recognizing the Android storage paths.
+  RedactHashes(input, &detected);
+  return detected;
+}
+
 RE2* RedactionTool::GetRegExp(const std::string& pattern) {
   if (regexp_cache_.find(pattern) == regexp_cache_.end()) {
     RE2::Options options;
@@ -468,7 +494,9 @@
   return regexp_cache_[pattern].get();
 }
 
-std::string RedactionTool::RedactMACAddresses(const std::string& input) {
+std::string RedactionTool::RedactMACAddresses(
+    const std::string& input,
+    std::map<PIIType, std::set<std::string>>* detected) {
   // This regular expression finds the next MAC address. It splits the data into
   // an OUI (Organizationally Unique Identifier) part and a NIC (Network
   // Interface Controller) specific part. We also match on dash and underscore
@@ -506,7 +534,9 @@
                                            oui_string.c_str(), mac_id);
       mac_addresses_[mac] = replacement_mac;
     }
-
+    if (detected != nullptr) {
+      (*detected)[PIIType::kMACAddress].insert(mac);
+    }
     skipped.AppendToString(&result);
     result += replacement_mac;
   }
@@ -515,7 +545,9 @@
   return result;
 }
 
-std::string RedactionTool::RedactHashes(const std::string& input) {
+std::string RedactionTool::RedactHashes(
+    const std::string& input,
+    std::map<PIIType, std::set<std::string>>* detected) {
   // This will match hexadecimal strings from length 32 to 64 that have a word
   // boundary at each end. We then check to make sure they are one of our valid
   // hash lengths before replacing.
@@ -559,6 +591,9 @@
           "<HASH:%s %zd>", hash_prefix_string.c_str(), hashes_.size());
       hashes_[hash] = replacement_hash;
     }
+    if (detected != nullptr) {
+      (*detected)[PIIType::kHash].insert(hash);
+    }
 
     result += replacement_hash;
   }
@@ -568,7 +603,8 @@
 }
 
 std::string RedactionTool::RedactAndroidAppStoragePaths(
-    const std::string& input) {
+    const std::string& input,
+    std::map<PIIType, std::set<std::string>>* detected) {
   // We only use this on Chrome OS and there's differences in the API for
   // FilePath on Windows which prevents this from compiling, so only enable this
   // code for Chrome OS.
@@ -614,6 +650,10 @@
       if (component.length() > 1)
         result += '_';
     }
+    if (detected != nullptr) {
+      (*detected)[PIIType::kAndroidAppStoragePath].insert(
+          app_specific.as_string());
+    }
   }
 
   text.AppendToString(&result);
@@ -624,20 +664,30 @@
 }
 
 std::string RedactionTool::RedactCustomPatterns(std::string input) {
-  for (size_t i = 0; i < base::size(kCustomPatternsWithContext); i++) {
-    input =
-        RedactCustomPatternWithContext(input, kCustomPatternsWithContext[i]);
+  for (const auto& pattern : kCustomPatternsWithContext) {
+    input = RedactCustomPatternWithContext(input, pattern, nullptr);
   }
-  for (size_t i = 0; i < base::size(kCustomPatternsWithoutContext); i++) {
-    input = RedactCustomPatternWithoutContext(input,
-                                              kCustomPatternsWithoutContext[i]);
+  for (const auto& pattern : kCustomPatternsWithoutContext) {
+    input = RedactCustomPatternWithoutContext(input, pattern, nullptr);
   }
   return input;
 }
 
+void RedactionTool::DetectWithCustomPatterns(
+    std::string input,
+    std::map<PIIType, std::set<std::string>>* detected) {
+  for (const auto& pattern : kCustomPatternsWithContext) {
+    RedactCustomPatternWithContext(input, pattern, detected);
+  }
+  for (const auto& pattern : kCustomPatternsWithoutContext) {
+    RedactCustomPatternWithoutContext(input, pattern, detected);
+  }
+}
+
 std::string RedactionTool::RedactCustomPatternWithContext(
     const std::string& input,
-    const CustomPatternWithAlias& pattern) {
+    const CustomPatternWithAlias& pattern,
+    std::map<PIIType, std::set<std::string>>* detected) {
   RE2* re = GetRegExp(pattern.pattern);
   DCHECK_EQ(3, re->NumberOfCapturingGroups());
   std::map<std::string, std::string>* identifier_space =
@@ -664,7 +714,9 @@
     } else {
       replacement_id = (*identifier_space)[matched_id_as_string];
     }
-
+    if (detected != nullptr) {
+      (*detected)[pattern.pii_type].insert(matched_id_as_string);
+    }
     skipped.AppendToString(&result);
     pre_matched_id.AppendToString(&result);
     result += replacement_id;
@@ -720,7 +772,8 @@
 
 std::string RedactionTool::RedactCustomPatternWithoutContext(
     const std::string& input,
-    const CustomPatternWithAlias& pattern) {
+    const CustomPatternWithAlias& pattern,
+    std::map<PIIType, std::set<std::string>>* detected) {
   RE2* re = GetRegExp(pattern.pattern);
   DCHECK_EQ(1, re->NumberOfCapturingGroups());
 
@@ -761,9 +814,15 @@
             replacement_id.empty() ? pattern.alias : replacement_id.c_str(),
             base::NumberToString(identifier_space->size() + 1).c_str());
         (*identifier_space)[matched_id_as_string] = replacement_id;
+        if (detected != nullptr) {
+          (*detected)[pattern.pii_type].insert(matched_id_as_string);
+        }
       }
     } else {
       replacement_id = (*identifier_space)[matched_id_as_string];
+      if (detected != nullptr) {
+        (*detected)[pattern.pii_type].insert(matched_id_as_string);
+      }
     }
 
     skipped.AppendToString(&result);
diff --git a/components/feedback/redaction_tool.h b/components/feedback/redaction_tool.h
index b144c1a..0607a74 100644
--- a/components/feedback/redaction_tool.h
+++ b/components/feedback/redaction_tool.h
@@ -7,12 +7,14 @@
 
 #include <map>
 #include <memory>
+#include <set>
 #include <string>
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/sequence_checker.h"
 #include "base/task/sequenced_task_runner.h"
+#include "components/feedback/pii_types.h"
 
 namespace re2 {
 class RE2;
@@ -27,6 +29,8 @@
   // A RE2 regexp used in the replacing logic. Matches will be replaced by the
   // alias reference described above.
   const char* pattern;
+  // PII category of the data that will be detected using this pattern.
+  PIIType pii_type;
 };
 
 // Formerly known as AnonymizerTool, RedactionTool provides functions for
@@ -52,21 +56,51 @@
   // thread.
   std::string Redact(const std::string& input);
 
+  // Return a map of [PII-sensitive data type -> set of data] that are detected
+  // in |input|.
+  std::map<PIIType, std::set<std::string>> Detect(const std::string& input);
+
  private:
   friend class RedactionToolTest;
 
   re2::RE2* GetRegExp(const std::string& pattern);
 
-  std::string RedactMACAddresses(const std::string& input);
-  std::string RedactAndroidAppStoragePaths(const std::string& input);
-  std::string RedactHashes(const std::string& input);
+  // Redacts MAC addresses from |input| and returns the redacted string. Adds
+  // the redacted MAC addresses to |detected| if |detected| is not nullptr.
+  std::string RedactMACAddresses(
+      const std::string& input,
+      std::map<PIIType, std::set<std::string>>* detected);
+  // Redacts Android app storage paths from |input| and returns the redacted
+  // string. Adds the redacted app storage paths to |detected| if |detected| is
+  // not nullptr. This function returns meaningpul output only on Chrome OS.
+  std::string RedactAndroidAppStoragePaths(
+      const std::string& input,
+      std::map<PIIType, std::set<std::string>>* detected);
+  // Redacts hashes from |input| and returns the redacted string. Adds the
+  // redacted hashes to |detected| if |detected| is not nullptr.
+  std::string RedactHashes(const std::string& input,
+                           std::map<PIIType, std::set<std::string>>* detected);
   std::string RedactCustomPatterns(std::string input);
+
+  // Detects PII sensitive data in |input| using custom patterns. Adds the
+  // detected PII sensitive data to corresponding PII type key in |detected|.
+  void DetectWithCustomPatterns(
+      std::string input,
+      std::map<PIIType, std::set<std::string>>* detected);
+  // Redacts PII sensitive data that matches |pattern| from |input| and returns
+  // the redacted string. Adds the redacted PII sensitive data to |detected| if
+  // |detected| is not nullptr.
   std::string RedactCustomPatternWithContext(
       const std::string& input,
-      const CustomPatternWithAlias& pattern);
+      const CustomPatternWithAlias& pattern,
+      std::map<PIIType, std::set<std::string>>* detected);
+  // Redacts PII sensitive data that matches |pattern| from |input| and returns
+  // the redacted string. Adds the redacted PII sensitive data to |detected| if
+  // |detected| is not nullptr.
   std::string RedactCustomPatternWithoutContext(
       const std::string& input,
-      const CustomPatternWithAlias& pattern);
+      const CustomPatternWithAlias& pattern,
+      std::map<PIIType, std::set<std::string>>* detected);
 
   // Null-terminated list of first party extension IDs. We need to have this
   // passed into us because we can't refer to the code where these are defined.
diff --git a/components/feedback/redaction_tool_unittest.cc b/components/feedback/redaction_tool_unittest.cc
index eacb264..7d4721a 100644
--- a/components/feedback/redaction_tool_unittest.cc
+++ b/components/feedback/redaction_tool_unittest.cc
@@ -5,27 +5,190 @@
 #include "components/feedback/redaction_tool.h"
 
 #include <gtest/gtest.h>
+#include <set>
 
 #include "base/strings/string_util.h"
 #include "build/chromeos_buildflags.h"
+#include "components/feedback/pii_types.h"
 
 namespace feedback {
 
 const char kFakeFirstPartyID[] = "nkoccljplnhpfnfiajclkommnmllphnl";
 const char* const kFakeFirstPartyExtensionIDs[] = {kFakeFirstPartyID, nullptr};
 
+// For better readability, put all the pre/post redaction strings in an array
+// of pairs, and then convert that to two strings which become the input and
+// output of the redactor.
+const std::pair<std::string, std::string> kStringsWithRedactions[] = {
+    {"aaaaaaaa [SSID=123aaaaaa]aaaaa",  // SSID.
+     "aaaaaaaa [SSID=<SSID: 1>]aaaaa"},
+    {"aaaaaaaahttp://tets.comaaaaaaa",  // URL.
+     "aaaaaaaa<URL: 1>"},
+    {"aaaaaemail@example.comaaa",  // Email address.
+     "<email: 1>"},
+    {"example@@1234",  // No PII, it is not invalid email address.
+     "example@@1234"},
+    {"255.255.155.2",  // IP address.
+     "<IPv4: 1>"},
+    {"255.255.155.255",  // IP address.
+     "<IPv4: 2>"},
+    {"127.0.0.1",  // IPv4 loopback.
+     "<127.0.0.0/8: 3>"},
+    {"127.255.0.1",  // IPv4 loopback.
+     "<127.0.0.0/8: 4>"},
+    {"0.0.0.0",  // Any IPv4.
+     "<0.0.0.0/8: 5>"},
+    {"0.255.255.255",  // Any IPv4.
+     "<0.0.0.0/8: 6>"},
+    {"10.10.10.100",  // IPv4 private class A.
+     "<10.0.0.0/8: 7>"},
+    {"10.10.10.100",  // Intentional duplicate.
+     "<10.0.0.0/8: 7>"},
+    {"10.10.10.101",  // IPv4 private class A.
+     "<10.0.0.0/8: 8>"},
+    {"10.255.255.255",  // IPv4 private class A.
+     "<10.0.0.0/8: 9>"},
+    {"172.16.0.0",  // IPv4 private class B.
+     "<172.16.0.0/12: 10>"},
+    {"172.31.255.255",  // IPv4 private class B.
+     "<172.16.0.0/12: 11>"},
+    {"172.11.5.5",  // IP address.
+     "<IPv4: 12>"},
+    {"172.111.5.5",  // IP address.
+     "<IPv4: 13>"},
+    {"192.168.0.0",  // IPv4 private class C.
+     "<192.168.0.0/16: 14>"},
+    {"192.168.255.255",  // IPv4 private class C.
+     "<192.168.0.0/16: 15>"},
+    {"192.169.2.120",  // IP address.
+     "<IPv4: 16>"},
+    {"169.254.0.1",  // Link local.
+     "<169.254.0.0/16: 17>"},
+    {"169.200.0.1",  // IP address.
+     "<IPv4: 18>"},
+    {"fe80::",  // Link local.
+     "<fe80::/10: 1>"},
+    {"fe80::ffff",  // Link local.
+     "<fe80::/10: 2>"},
+    {"febf:ffff::ffff",  // Link local.
+     "<fe80::/10: 3>"},
+    {"fecc::1111",  // IP address.
+     "<IPv6: 4>"},
+    {"224.0.0.24",  // Multicast.
+     "<224.0.0.0/4: 19>"},
+    {"240.0.0.0",  // IP address.
+     "<IPv4: 20>"},
+    {"255.255.255.255",  // Broadcast.
+     "255.255.255.255"},
+    {"100.115.92.92",  // ChromeOS.
+     "100.115.92.92"},
+    {"100.115.91.92",  // IP address.
+     "<IPv4: 21>"},
+    {"1.1.1.1",  // DNS
+     "1.1.1.1"},
+    {"8.8.8.8",  // DNS
+     "8.8.8.8"},
+    {"8.8.4.4",  // DNS
+     "8.8.4.4"},
+    {"8.8.8.4",  // IP address.
+     "<IPv4: 22>"},
+    {"255.255.259.255",  // Not an IP address.
+     "255.255.259.255"},
+    {"255.300.255.255",  // Not an IP address.
+     "255.300.255.255"},
+    {"3-1.2.3.4",  // USB path, not an IP address.
+     "3-1.2.3.4"},
+    {"aaaa123.123.45.4aaa",  // IP address.
+     "aaaa<IPv4: 23>aaa"},
+    {"11:11;11::11",  // IP address.
+     "11:11;<IPv6: 5>"},
+    {"11::11",  // IP address.
+     "<IPv6: 5>"},
+    {"11:11:abcdef:0:0:0:0:0",  // No PII.
+     "11:11:abcdef:0:0:0:0:0"},
+    {"::",  // Unspecified.
+     "::"},
+    {"::1",  // Local host.
+     "::1"},
+    {"Instance::Set",  // Ignore match, no PII.
+     "Instance::Set"},
+    {"Instant::ff",  // Ignore match, no PII.
+     "Instant::ff"},
+    {"net::ERR_CONN_TIMEOUT",  // Ignore match, no PII.
+     "net::ERR_CONN_TIMEOUT"},
+    {"ff01::1",  // All nodes address (interface local).
+     "ff01::1"},
+    {"ff01::2",  // All routers (interface local).
+     "ff01::2"},
+    {"ff01::3",  // Multicast (interface local).
+     "<ff01::/16: 6>"},
+    {"ff02::1",  // All nodes address (link local).
+     "ff02::1"},
+    {"ff02::2",  // All routers (link local).
+     "ff02::2"},
+    {"ff02::3",  // Multicast (link local).
+     "<ff02::/16: 7>"},
+    {"ff02::fb",  // mDNSv6 (link local).
+     "<ff02::/16: 8>"},
+    {"ff08::fb",  // mDNSv6.
+     "<IPv6: 9>"},
+    {"ff0f::101",  // All NTP servers.
+     "<IPv6: 10>"},
+    {"::ffff:cb0c:10ea",  // IPv4-mapped IPV6 (IP address).
+     "<IPv6: 11>"},
+    {"::ffff:a0a:a0a",  // IPv4-mapped IPV6 (private class A).
+     "<M 10.0.0.0/8: 12>"},
+    {"::ffff:a0a:a0a",  // Intentional duplicate.
+     "<M 10.0.0.0/8: 12>"},
+    {"::ffff:ac1e:1e1e",  // IPv4-mapped IPV6 (private class B).
+     "<M 172.16.0.0/12: 13>"},
+    {"::ffff:c0a8:640a",  // IPv4-mapped IPV6 (private class C).
+     "<M 192.168.0.0/16: 14>"},
+    {"::ffff:6473:5c01",  // IPv4-mapped IPV6 (Chrome).
+     "<M 100.115.92.1: 15>"},
+    {"64:ff9b::a0a:a0a",  // IPv4-translated 6to4 IPV6 (private class A).
+     "<T 10.0.0.0/8: 16>"},
+    {"64:ff9b::6473:5c01",  // IPv4-translated 6to4 IPV6 (Chrome).
+     "<T 100.115.92.1: 17>"},
+    {"::0101:ffff:c0a8:640a",  // IP address.
+     "<IPv6: 18>"},
+    {"aa:aa:aa:aa:aa:aa",  // MAC address (BSSID).
+     "[MAC OUI=aa:aa:aa IFACE=1]"},
+    {"chrome://resources/foo",  // Secure chrome resource, exempt.
+     "chrome://resources/foo"},
+    {"chrome://settings/crisper.js",  // Exempt settings URLs.
+     "chrome://settings/crisper.js"},
+    // Exempt first party extension.
+    {"chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js",
+     "chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js"},
+    {"chrome://resources/f?user=bar",  // Potentially PII in parameter.
+     "<URL: 2>"},
+    {"chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js?bar=x",
+     "<URL: 3>"},  // Potentially PII in parameter.
+    {"/root/27540283740a0897ab7c8de0f809add2bacde78f/foo",
+     "/root/<HASH:2754 1>/foo"},  // Hash string.
+#if BUILDFLAG(IS_CHROMEOS_ASH)    // We only redact Android paths on Chrome OS.
+    // Allowed android storage path.
+    {"112K\t/home/root/deadbeef1234/android-data/data/system_de",
+     "112K\t/home/root/deadbeef1234/android-data/data/system_de"},
+    // Redacted app-specific storage path.
+    {"8.0K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/de",
+     "8.0K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/d_"},
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+};
+
 class RedactionToolTest : public testing::Test {
  protected:
   std::string RedactMACAddresses(const std::string& input) {
-    return redactor_.RedactMACAddresses(input);
+    return redactor_.RedactMACAddresses(input, nullptr);
   }
 
   std::string RedactHashes(const std::string& input) {
-    return redactor_.RedactHashes(input);
+    return redactor_.RedactHashes(input, nullptr);
   }
 
   std::string RedactAndroidAppStoragePaths(const std::string& input) {
-    return redactor_.RedactAndroidAppStoragePaths(input);
+    return redactor_.RedactAndroidAppStoragePaths(input, nullptr);
   }
 
   std::string RedactCustomPatterns(const std::string& input) {
@@ -35,13 +198,13 @@
   std::string RedactCustomPatternWithContext(
       const std::string& input,
       const CustomPatternWithAlias& pattern) {
-    return redactor_.RedactCustomPatternWithContext(input, pattern);
+    return redactor_.RedactCustomPatternWithContext(input, pattern, nullptr);
   }
 
   std::string RedactCustomPatternWithoutContext(
       const std::string& input,
       const CustomPatternWithAlias& pattern) {
-    return redactor_.RedactCustomPatternWithoutContext(input, pattern);
+    return redactor_.RedactCustomPatternWithoutContext(input, pattern, nullptr);
   }
 
   RedactionTool redactor_{kFakeFirstPartyExtensionIDs};
@@ -273,9 +436,14 @@
 }
 
 TEST_F(RedactionToolTest, RedactCustomPatternWithContext) {
-  const CustomPatternWithAlias kPattern1 = {"ID", "(\\b(?i)id:? ')(\\d+)(')"};
-  const CustomPatternWithAlias kPattern2 = {"ID", "(\\b(?i)id=')(\\d+)(')"};
-  const CustomPatternWithAlias kPattern3 = {"IDG", "(\\b(?i)idg=')(\\d+)(')"};
+  // The PIIType for the CustomPatternWithAlias is not relevant, only for
+  // testing.
+  const CustomPatternWithAlias kPattern1 = {"ID", "(\\b(?i)id:? ')(\\d+)(')",
+                                            PIIType::kUUID};
+  const CustomPatternWithAlias kPattern2 = {"ID", "(\\b(?i)id=')(\\d+)(')",
+                                            PIIType::kUUID};
+  const CustomPatternWithAlias kPattern3 = {"IDG", "(\\b(?i)idg=')(\\d+)(')",
+                                            PIIType::kCellID};
   EXPECT_EQ("", RedactCustomPatternWithContext("", kPattern1));
   EXPECT_EQ("foo\nbar\n",
             RedactCustomPatternWithContext("foo\nbar\n", kPattern1));
@@ -301,182 +469,91 @@
 }
 
 TEST_F(RedactionToolTest, RedactCustomPatternWithoutContext) {
-  CustomPatternWithAlias kPattern = {"pattern", "(o+)"};
+  // The PIIType for the CustomPatternWithAlias here is not relevant, only for
+  // testing.
+  CustomPatternWithAlias kPattern = {"pattern", "(o+)", PIIType::kEmail};
   EXPECT_EQ("", RedactCustomPatternWithoutContext("", kPattern));
   EXPECT_EQ("f<pattern: 1>\nf<pattern: 2>z\nf<pattern: 1>l\n",
             RedactCustomPatternWithoutContext("fo\nfooz\nfol\n", kPattern));
 }
 
 TEST_F(RedactionToolTest, RedactChunk) {
-  // For better readability, put all the pre/post redaction strings in an array
-  // of pairs, and then convert that to two strings which become the input and
-  // output of the redactor.
-  std::pair<std::string, std::string> data[] = {
-    {"aaaaaaaa [SSID=123aaaaaa]aaaaa",  // SSID.
-     "aaaaaaaa [SSID=<SSID: 1>]aaaaa"},
-    {"aaaaaaaahttp://tets.comaaaaaaa",  // URL.
-     "aaaaaaaa<URL: 1>"},
-    {"aaaaaemail@example.comaaa",  // Email address.
-     "<email: 1>"},
-    {"example@@1234",  // No PII, it is not invalid email address.
-     "example@@1234"},
-    {"255.255.155.2",  // IP address.
-     "<IPv4: 1>"},
-    {"255.255.155.255",  // IP address.
-     "<IPv4: 2>"},
-    {"127.0.0.1",  // IPv4 loopback.
-     "<127.0.0.0/8: 3>"},
-    {"127.255.0.1",  // IPv4 loopback.
-     "<127.0.0.0/8: 4>"},
-    {"0.0.0.0",  // Any IPv4.
-     "<0.0.0.0/8: 5>"},
-    {"0.255.255.255",  // Any IPv4.
-     "<0.0.0.0/8: 6>"},
-    {"10.10.10.100",  // IPv4 private class A.
-     "<10.0.0.0/8: 7>"},
-    {"10.10.10.100",  // Intentional duplicate.
-     "<10.0.0.0/8: 7>"},
-    {"10.10.10.101",  // IPv4 private class A.
-     "<10.0.0.0/8: 8>"},
-    {"10.255.255.255",  // IPv4 private class A.
-     "<10.0.0.0/8: 9>"},
-    {"172.16.0.0",  // IPv4 private class B.
-     "<172.16.0.0/12: 10>"},
-    {"172.31.255.255",  // IPv4 private class B.
-     "<172.16.0.0/12: 11>"},
-    {"172.11.5.5",  // IP address.
-     "<IPv4: 12>"},
-    {"172.111.5.5",  // IP address.
-     "<IPv4: 13>"},
-    {"192.168.0.0",  // IPv4 private class C.
-     "<192.168.0.0/16: 14>"},
-    {"192.168.255.255",  // IPv4 private class C.
-     "<192.168.0.0/16: 15>"},
-    {"192.169.2.120",  // IP address.
-     "<IPv4: 16>"},
-    {"169.254.0.1",  // Link local.
-     "<169.254.0.0/16: 17>"},
-    {"169.200.0.1",  // IP address.
-     "<IPv4: 18>"},
-    {"fe80::",  // Link local.
-     "<fe80::/10: 1>"},
-    {"fe80::ffff",  // Link local.
-     "<fe80::/10: 2>"},
-    {"febf:ffff::ffff",  // Link local.
-     "<fe80::/10: 3>"},
-    {"fecc::1111",  // IP address.
-     "<IPv6: 4>"},
-    {"224.0.0.24",  // Multicast.
-     "<224.0.0.0/4: 19>"},
-    {"240.0.0.0",  // IP address.
-     "<IPv4: 20>"},
-    {"255.255.255.255",  // Broadcast.
-     "255.255.255.255"},
-    {"100.115.92.92",  // ChromeOS.
-     "100.115.92.92"},
-    {"100.115.91.92",  // IP address.
-     "<IPv4: 21>"},
-    {"1.1.1.1",  // DNS
-     "1.1.1.1"},
-    {"8.8.8.8",  // DNS
-     "8.8.8.8"},
-    {"8.8.4.4",  // DNS
-     "8.8.4.4"},
-    {"8.8.8.4",  // IP address.
-     "<IPv4: 22>"},
-    {"255.255.259.255",  // Not an IP address.
-     "255.255.259.255"},
-    {"255.300.255.255",  // Not an IP address.
-     "255.300.255.255"},
-    {"3-1.2.3.4",  // USB path, not an IP address.
-     "3-1.2.3.4"},
-    {"aaaa123.123.45.4aaa",  // IP address.
-     "aaaa<IPv4: 23>aaa"},
-    {"11:11;11::11",  // IP address.
-     "11:11;<IPv6: 5>"},
-    {"11::11",  // IP address.
-     "<IPv6: 5>"},
-    {"11:11:abcdef:0:0:0:0:0",  // No PII.
-     "11:11:abcdef:0:0:0:0:0"},
-    {"::",  // Unspecified.
-     "::"},
-    {"::1",  // Local host.
-     "::1"},
-    {"Instance::Set",  // Ignore match, no PII.
-     "Instance::Set"},
-    {"Instant::ff",  // Ignore match, no PII.
-     "Instant::ff"},
-    {"net::ERR_CONN_TIMEOUT",  // Ignore match, no PII.
-     "net::ERR_CONN_TIMEOUT"},
-    {"ff01::1",  // All nodes address (interface local).
-     "ff01::1"},
-    {"ff01::2",  // All routers (interface local).
-     "ff01::2"},
-    {"ff01::3",  // Multicast (interface local).
-     "<ff01::/16: 6>"},
-    {"ff02::1",  // All nodes address (link local).
-     "ff02::1"},
-    {"ff02::2",  // All routers (link local).
-     "ff02::2"},
-    {"ff02::3",  // Multicast (link local).
-     "<ff02::/16: 7>"},
-    {"ff02::fb",  // mDNSv6 (link local).
-     "<ff02::/16: 8>"},
-    {"ff08::fb",  // mDNSv6.
-     "<IPv6: 9>"},
-    {"ff0f::101",  // All NTP servers.
-     "<IPv6: 10>"},
-    {"::ffff:cb0c:10ea",  // IPv4-mapped IPV6 (IP address).
-     "<IPv6: 11>"},
-    {"::ffff:a0a:a0a",  // IPv4-mapped IPV6 (private class A).
-     "<M 10.0.0.0/8: 12>"},
-    {"::ffff:a0a:a0a",  // Intentional duplicate.
-     "<M 10.0.0.0/8: 12>"},
-    {"::ffff:ac1e:1e1e",  // IPv4-mapped IPV6 (private class B).
-     "<M 172.16.0.0/12: 13>"},
-    {"::ffff:c0a8:640a",  // IPv4-mapped IPV6 (private class C).
-     "<M 192.168.0.0/16: 14>"},
-    {"::ffff:6473:5c01",  // IPv4-mapped IPV6 (Chrome).
-     "<M 100.115.92.1: 15>"},
-    {"64:ff9b::a0a:a0a",  // IPv4-translated 6to4 IPV6 (private class A).
-     "<T 10.0.0.0/8: 16>"},
-    {"64:ff9b::6473:5c01",  // IPv4-translated 6to4 IPV6 (Chrome).
-     "<T 100.115.92.1: 17>"},
-    {"::0101:ffff:c0a8:640a",  // IP address.
-     "<IPv6: 18>"},
-    {"aa:aa:aa:aa:aa:aa",  // MAC address (BSSID).
-     "[MAC OUI=aa:aa:aa IFACE=1]"},
-    {"chrome://resources/foo",  // Secure chrome resource, exempt.
-     "chrome://resources/foo"},
-    {"chrome://settings/crisper.js",  // Exempt settings URLs.
-     "chrome://settings/crisper.js"},
-    // Exempt first party extension.
-    {"chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js",
-     "chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js"},
-    {"chrome://resources/f?user=bar",  // Potentially PII in parameter.
-     "<URL: 2>"},
-    {"chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js?bar=x",
-     "<URL: 3>"},  // Potentially PII in parameter.
-    {"/root/27540283740a0897ab7c8de0f809add2bacde78f/foo",
-     "/root/<HASH:2754 1>/foo"},  // Hash string.
-#if BUILDFLAG(IS_CHROMEOS_ASH)    // We only redact Android paths on Chrome OS.
-    // Allowed android storage path.
-    {"112K\t/home/root/deadbeef1234/android-data/data/system_de",
-     "112K\t/home/root/deadbeef1234/android-data/data/system_de"},
-    // Redacted app-specific storage path.
-    {"8.0K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/de",
-     "8.0K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/d_"},
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-  };
   std::string redaction_input;
   std::string redaction_output;
-  for (const auto& s : data) {
+  for (const auto& s : kStringsWithRedactions) {
     redaction_input.append(s.first).append("\n");
     redaction_output.append(s.second).append("\n");
   }
   EXPECT_EQ(redaction_output, redactor_.Redact(redaction_input));
 }
 
+TEST_F(RedactionToolTest, DetectPII) {
+  std::string redaction_input;
+  for (const auto& s : kStringsWithRedactions) {
+    redaction_input.append(s.first).append("\n");
+  }
+  std::map<PIIType, std::set<std::string>> pii_in_data {
+#if BUILDFLAG(IS_CHROMEOS_ASH)  // We only detect Android paths on Chrome OS.
+    {PIIType::kAndroidAppStoragePath, {"/de"}},
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+        {PIIType::kSSID, {"123aaaaaa"}},
+        {PIIType::kURL,
+         {"http://tets.comaaaaaaa", "chrome://resources/f?user=bar",
+          "chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/"
+          "foobar.js?bar=x"}},
+        {PIIType::kEmail, {"aaaaaemail@example.comaaa"}},
+        {PIIType::kIPAddress,
+         {
+             "255.255.155.2",
+             "255.255.155.255",
+             "127.0.0.1",
+             "127.255.0.1",
+             "0.0.0.0",
+             "0.255.255.255",
+             "10.10.10.100",
+             "10.10.10.101",
+             "10.255.255.255",
+             "172.16.0.0",
+             "172.31.255.255",
+             "172.11.5.5",
+             "172.111.5.5",
+             "192.168.0.0",
+             "192.168.255.255",
+             "192.169.2.120",
+             "169.254.0.1",
+             "169.200.0.1",
+             "224.0.0.24",
+             "240.0.0.0",
+             "100.115.91.92",
+             "8.8.8.4",
+             "123.123.45.4",
+             "fe80::",
+             "fe80::ffff",
+             "febf:ffff::ffff",
+             "fecc::1111",
+             "11::11",
+             "ff01::3",
+             "ff02::3",
+             "ff02::fb",
+             "ff08::fb",
+             "ff0f::101",
+             "::ffff:cb0c:10ea",
+             "::ffff:a0a:a0a",
+             "::ffff:ac1e:1e1e",
+             "::ffff:c0a8:640a",
+             "::ffff:6473:5c01",
+             "64:ff9b::a0a:a0a",
+             "64:ff9b::6473:5c01",
+             "::0101:ffff:c0a8:640a",
+         }},
+        {PIIType::kMACAddress, {"aa:aa:aa:aa:aa:aa"}}, {
+      PIIType::kHash, { "27540283740a0897ab7c8de0f809add2bacde78f" }
+    }
+  };
+
+  EXPECT_EQ(pii_in_data, redactor_.Detect(redaction_input));
+}
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)  // We only redact Android paths on Chrome OS.
 TEST_F(RedactionToolTest, RedactAndroidAppStoragePaths) {
   EXPECT_EQ("", RedactAndroidAppStoragePaths(""));
diff --git a/components/language/content/browser/geo_language_provider.cc b/components/language/content/browser/geo_language_provider.cc
index 8fbf0ba..15c0874 100644
--- a/components/language/content/browser/geo_language_provider.cc
+++ b/components/language/content/browser/geo_language_provider.cc
@@ -73,7 +73,7 @@
 
   prefs_ = prefs;
 
-  const base::ListValue* const cached_languages_list =
+  const base::Value* const cached_languages_list =
       prefs_->GetList(kCachedGeoLanguagesPref);
   for (const auto& language_value : cached_languages_list->GetList()) {
     languages_.push_back(language_value.GetString());
diff --git a/components/language/content/browser/geo_language_provider_unittest.cc b/components/language/content/browser/geo_language_provider_unittest.cc
index a1e988d..c76de1e 100644
--- a/components/language/content/browser/geo_language_provider_unittest.cc
+++ b/components/language/content/browser/geo_language_provider_unittest.cc
@@ -73,7 +73,7 @@
 
   const std::vector<std::string> GetCachedLanguages() {
     std::vector<std::string> languages;
-    const base::ListValue* const cached_languages_list =
+    const base::Value* const cached_languages_list =
         local_state_.GetList(GeoLanguageProvider::kCachedGeoLanguagesPref);
     for (const auto& language_value : cached_languages_list->GetList()) {
       languages.push_back(language_value.GetString());
diff --git a/components/language/core/browser/url_language_histogram.cc b/components/language/core/browser/url_language_histogram.cc
index b06ffbf..4271303 100644
--- a/components/language/core/browser/url_language_histogram.cc
+++ b/components/language/core/browser/url_language_histogram.cc
@@ -92,8 +92,8 @@
 std::vector<UrlLanguageHistogram::LanguageInfo>
 UrlLanguageHistogram::GetTopLanguages() const {
   std::vector<UrlLanguageHistogram::LanguageInfo> top_languages =
-      GetAllLanguages(
-          *pref_service_->GetDictionary(kUrlLanguageHistogramCounters));
+      GetAllLanguages(base::Value::AsDictionaryValue(
+          *pref_service_->GetDictionary(kUrlLanguageHistogramCounters)));
 
   std::sort(top_languages.begin(), top_languages.end(),
             [](UrlLanguageHistogram::LanguageInfo a,
@@ -106,8 +106,8 @@
 
 float UrlLanguageHistogram::GetLanguageFrequency(
     const std::string& language_code) const {
-  const base::DictionaryValue* dict =
-      pref_service_->GetDictionary(kUrlLanguageHistogramCounters);
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *pref_service_->GetDictionary(kUrlLanguageHistogramCounters));
   int counters_sum = GetCountersSum(*dict);
   // If the sample is not large enough yet, pretend there are no top languages.
   if (counters_sum < kMinCountersSum)
diff --git a/components/media_router/browser/presentation/presentation_service_delegate_impl.cc b/components/media_router/browser/presentation/presentation_service_delegate_impl.cc
index 06d7613..cb92ed33f8 100644
--- a/components/media_router/browser/presentation/presentation_service_delegate_impl.cc
+++ b/components/media_router/browser/presentation/presentation_service_delegate_impl.cc
@@ -711,7 +711,7 @@
 #if !defined(OS_ANDROID)
 bool PresentationServiceDelegateImpl::ShouldCancelAutoJoinForOrigin(
     const url::Origin& origin) {
-  const base::ListValue* origins =
+  const base::Value* origins =
       user_prefs::UserPrefs::Get(GetWebContents().GetBrowserContext())
           ->GetList(prefs::kMediaRouterTabMirroringSources);
   return origins &&
diff --git a/components/metrics/data_use_tracker.cc b/components/metrics/data_use_tracker.cc
index dcedc8f..6953a748 100644
--- a/components/metrics/data_use_tracker.cc
+++ b/components/metrics/data_use_tracker.cc
@@ -113,8 +113,7 @@
   DictionaryPrefUpdate pref_updater(local_state_, pref_name);
   std::string todays_key = GetCurrentMeasurementDateAsString();
 
-  const base::DictionaryValue* user_pref_dict =
-      local_state_->GetDictionary(pref_name);
+  const base::Value* user_pref_dict = local_state_->GetDictionary(pref_name);
   int todays_traffic = user_pref_dict->FindIntKey(todays_key).value_or(0);
   pref_updater->SetInteger(todays_key, todays_traffic + message_size);
 }
@@ -129,7 +128,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   const base::DictionaryValue* user_pref_dict =
-      local_state_->GetDictionary(pref_name);
+      &base::Value::AsDictionaryValue(*local_state_->GetDictionary(pref_name));
   const base::Time current_date = GetCurrentMeasurementDate();
   const base::Time week_ago = current_date - base::Days(7);
 
@@ -153,7 +152,7 @@
 
   int total_data_use = 0;
   const base::DictionaryValue* pref_dict =
-      local_state_->GetDictionary(pref_name);
+      &base::Value::AsDictionaryValue(*local_state_->GetDictionary(pref_name));
   for (base::DictionaryValue::Iterator it(*pref_dict); !it.IsAtEnd();
        it.Advance()) {
     total_data_use += it.value().GetIfInt().value_or(0);
diff --git a/components/metrics/demographics/user_demographics.cc b/components/metrics/demographics/user_demographics.cc
index 2d6490f..581c0752 100644
--- a/components/metrics/demographics/user_demographics.cc
+++ b/components/metrics/demographics/user_demographics.cc
@@ -101,8 +101,7 @@
 // Gets the synced user's birth year from synced prefs, see doc of
 // DemographicMetricsProvider in demographic_metrics_provider.h for more
 // details.
-absl::optional<int> GetUserBirthYear(
-    const base::DictionaryValue* demographics) {
+absl::optional<int> GetUserBirthYear(const base::Value* demographics) {
   const base::Value* value =
       demographics->FindPath(kSyncDemographicsBirthYearPath);
   int birth_year = (value != nullptr && value->is_int())
@@ -120,7 +119,7 @@
 // DemographicMetricsProvider in demographic_metrics_provider.h for more
 // details.
 absl::optional<UserDemographicsProto_Gender> GetUserGender(
-    const base::DictionaryValue* demographics) {
+    const base::Value* demographics) {
   const base::Value* value =
       demographics->FindPath(kSyncDemographicsGenderPath);
   int gender_int = (value != nullptr && value->is_int())
@@ -216,7 +215,7 @@
   // user_demographics.h for more details.
 
   // Get the pref that contains the user's birth year and gender.
-  const base::DictionaryValue* demographics =
+  const base::Value* demographics =
       pref_service->GetDictionary(kSyncDemographicsPrefName);
   DCHECK(demographics != nullptr);
 
diff --git a/components/metrics/unsent_log_store.cc b/components/metrics/unsent_log_store.cc
index c773583..e0c251b 100644
--- a/components/metrics/unsent_log_store.cc
+++ b/components/metrics/unsent_log_store.cc
@@ -184,7 +184,8 @@
 }
 
 void UnsentLogStore::LoadPersistedUnsentLogs() {
-  ReadLogsFromPrefList(*local_state_->GetList(log_data_pref_name_));
+  ReadLogsFromPrefList(
+      base::Value::AsListValue(*local_state_->GetList(log_data_pref_name_)));
   RecordMetaDataMetrics();
 }
 
@@ -388,8 +389,7 @@
   if (metadata_pref_name_ == nullptr)
     return;
 
-  const base::DictionaryValue* value =
-      local_state_->GetDictionary(metadata_pref_name_);
+  const base::Value* value = local_state_->GetDictionary(metadata_pref_name_);
   if (!value)
     return;
 
diff --git a/components/metrics/unsent_log_store_unittest.cc b/components/metrics/unsent_log_store_unittest.cc
index 7d923be0..83d7aaa 100644
--- a/components/metrics/unsent_log_store_unittest.cc
+++ b/components/metrics/unsent_log_store_unittest.cc
@@ -138,7 +138,7 @@
   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
 
   unsent_log_store.TrimAndPersistUnsentLogs();
-  const base::ListValue* list_value = prefs_.GetList(kTestPrefName);
+  const base::Value* list_value = prefs_.GetList(kTestPrefName);
   EXPECT_EQ(0U, list_value->GetList().size());
 
   TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
diff --git a/components/mirroring/browser/single_client_video_capture_host.cc b/components/mirroring/browser/single_client_video_capture_host.cc
index 5687b24..1101952 100644
--- a/components/mirroring/browser/single_client_video_capture_host.cc
+++ b/components/mirroring/browser/single_client_video_capture_host.cc
@@ -119,7 +119,8 @@
     OnFinishedConsumingBuffer(buffer_id, media::VideoCaptureFeedback());
   }
   DCHECK(buffer_context_map_.empty());
-  observer_->OnStateChanged(media::mojom::VideoCaptureState::ENDED);
+  observer_->OnStateChanged(media::mojom::VideoCaptureResult::NewState(
+      media::mojom::VideoCaptureState::ENDED));
   observer_.reset();
   weak_factory_.InvalidateWeakPtrs();
   launched_device_ = nullptr;
@@ -245,11 +246,12 @@
   }
 }
 
-void SingleClientVideoCaptureHost::OnError(media::VideoCaptureError) {
+void SingleClientVideoCaptureHost::OnError(media::VideoCaptureError error) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   VLOG(1) << __func__;
   if (observer_)
-    observer_->OnStateChanged(media::mojom::VideoCaptureState::FAILED);
+    observer_->OnStateChanged(
+        media::mojom::VideoCaptureResult::NewErrorCode(error));
 }
 
 void SingleClientVideoCaptureHost::OnFrameDropped(
@@ -266,7 +268,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DVLOG(2) << __func__;
   DCHECK(observer_);
-  observer_->OnStateChanged(media::mojom::VideoCaptureState::STARTED);
+  observer_->OnStateChanged(media::mojom::VideoCaptureResult::NewState(
+      media::mojom::VideoCaptureState::STARTED));
 }
 
 void SingleClientVideoCaptureHost::OnStartedUsingGpuDecode() {
diff --git a/components/mirroring/browser/single_client_video_capture_host_unittest.cc b/components/mirroring/browser/single_client_video_capture_host_unittest.cc
index a359a22d..416db33 100644
--- a/components/mirroring/browser/single_client_video_capture_host_unittest.cc
+++ b/components/mirroring/browser/single_client_video_capture_host_unittest.cc
@@ -149,7 +149,14 @@
     OnBufferDestroyedCall(buffer_id);
   }
 
-  MOCK_METHOD1(OnStateChanged, void(media::mojom::VideoCaptureState state));
+  MOCK_METHOD1(OnStateChangedCall, void(media::mojom::VideoCaptureState state));
+  MOCK_METHOD1(OnVideoCaptureErrorCall, void(media::VideoCaptureError error));
+  void OnStateChanged(media::mojom::VideoCaptureResultPtr result) override {
+    if (result->which() == media::mojom::VideoCaptureResult::Tag::STATE)
+      OnStateChangedCall(result->get_state());
+    else
+      OnVideoCaptureErrorCall(result->get_error_code());
+  }
 
   void Start() {
     host_->Start(device_id_, session_id_, VideoCaptureParams(),
@@ -211,7 +218,7 @@
   ~SingleClientVideoCaptureHostTest() override {
     base::RunLoop run_loop;
     EXPECT_CALL(*consumer_,
-                OnStateChanged(media::mojom::VideoCaptureState::ENDED))
+                OnStateChangedCall(media::mojom::VideoCaptureState::ENDED))
         .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
     consumer_->Stop();
     run_loop.Run();
diff --git a/components/mirroring/service/fake_video_capture_host.cc b/components/mirroring/service/fake_video_capture_host.cc
index bd9b760b7..f0846f3 100644
--- a/components/mirroring/service/fake_video_capture_host.cc
+++ b/components/mirroring/service/fake_video_capture_host.cc
@@ -30,14 +30,16 @@
     mojo::PendingRemote<media::mojom::VideoCaptureObserver> observer) {
   ASSERT_TRUE(observer);
   observer_.Bind(std::move(observer));
-  observer_->OnStateChanged(media::mojom::VideoCaptureState::STARTED);
+  observer_->OnStateChanged(media::mojom::VideoCaptureResult::NewState(
+      media::mojom::VideoCaptureState::STARTED));
 }
 
 void FakeVideoCaptureHost::Stop(const base::UnguessableToken& device_id) {
   if (!observer_)
     return;
 
-  observer_->OnStateChanged(media::mojom::VideoCaptureState::ENDED);
+  observer_->OnStateChanged(media::mojom::VideoCaptureResult::NewState(
+      media::mojom::VideoCaptureState::ENDED));
   observer_.reset();
   OnStopped();
 }
diff --git a/components/mirroring/service/video_capture_client.cc b/components/mirroring/service/video_capture_client.cc
index 437dfe2..75295682 100644
--- a/components/mirroring/service/video_capture_client.cc
+++ b/components/mirroring/service/video_capture_client.cc
@@ -91,30 +91,33 @@
   video_capture_host_->RequestRefreshFrame(DeviceId());
 }
 
-void VideoCaptureClient::OnStateChanged(media::mojom::VideoCaptureState state) {
+void VideoCaptureClient::OnStateChanged(
+    media::mojom::VideoCaptureResultPtr result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DVLOG(2) << __func__ << " state: " << state;
-
-  switch (state) {
-    case media::mojom::VideoCaptureState::STARTED:
-      RequestRefreshFrame();
-      break;
-    case media::mojom::VideoCaptureState::FAILED:
-      if (!error_callback_.is_null())
-        std::move(error_callback_).Run();
-      break;
-    case media::mojom::VideoCaptureState::PAUSED:
-    case media::mojom::VideoCaptureState::RESUMED:
-      break;
-    case media::mojom::VideoCaptureState::STOPPED:
-    case media::mojom::VideoCaptureState::ENDED:
-      client_buffers_.clear();
-      mapped_buffers_.clear();
-      weak_factory_.InvalidateWeakPtrs();
-      error_callback_.Reset();
-      frame_deliver_callback_.Reset();
-      receiver_.reset();
-      break;
+  if (result->which() == media::mojom::VideoCaptureResult::Tag::STATE) {
+    media::mojom::VideoCaptureState state = result->get_state();
+    DVLOG(2) << __func__ << " state: " << state;
+    switch (state) {
+      case media::mojom::VideoCaptureState::STARTED:
+        RequestRefreshFrame();
+        break;
+      case media::mojom::VideoCaptureState::PAUSED:
+      case media::mojom::VideoCaptureState::RESUMED:
+        break;
+      case media::mojom::VideoCaptureState::STOPPED:
+      case media::mojom::VideoCaptureState::ENDED:
+        client_buffers_.clear();
+        mapped_buffers_.clear();
+        weak_factory_.InvalidateWeakPtrs();
+        error_callback_.Reset();
+        frame_deliver_callback_.Reset();
+        receiver_.reset();
+        break;
+    }
+  } else {
+    DVLOG(2) << __func__ << " Failed with an error.";
+    if (!error_callback_.is_null())
+      std::move(error_callback_).Run();
   }
 }
 
@@ -260,7 +263,9 @@
     LOG(DFATAL) << "Unable to wrap shared memory mapping.";
     video_capture_host_->ReleaseBuffer(DeviceId(), buffer->buffer_id,
                                        media::VideoCaptureFeedback());
-    OnStateChanged(media::mojom::VideoCaptureState::FAILED);
+
+    OnStateChanged(media::mojom::VideoCaptureResult::NewErrorCode(
+        media::VideoCaptureError::kDeviceClientTooManyFramesDroppedY16));
     return;
   }
   frame->AddDestructionObserver(
@@ -281,7 +286,8 @@
         media::ConvertAndScaleFrame(*frame, *new_frame, nv12_to_i420_tmp_buf_);
     if (!status.is_ok()) {
       LOG(DFATAL) << "Unable to convert frame to I420.";
-      OnStateChanged(media::mojom::VideoCaptureState::FAILED);
+      OnStateChanged(media::mojom::VideoCaptureResult::NewErrorCode(
+          media::VideoCaptureError::kDeviceClientTooManyFramesDroppedY16));
       return;
     }
     frame = new_frame;
diff --git a/components/mirroring/service/video_capture_client.h b/components/mirroring/service/video_capture_client.h
index 9cdf437..1e18cd0 100644
--- a/components/mirroring/service/video_capture_client.h
+++ b/components/mirroring/service/video_capture_client.h
@@ -61,7 +61,7 @@
   void RequestRefreshFrame();
 
   // media::mojom::VideoCaptureObserver implementations.
-  void OnStateChanged(media::mojom::VideoCaptureState state) override;
+  void OnStateChanged(media::mojom::VideoCaptureResultPtr result) override;
   void OnNewBuffer(int32_t buffer_id,
                    media::mojom::VideoBufferHandlePtr buffer_handle) override;
   void OnBufferReady(
diff --git a/components/network_time/network_time_tracker.cc b/components/network_time/network_time_tracker.cc
index 16b7630..1df3dcd 100644
--- a/components/network_time/network_time_tracker.cc
+++ b/components/network_time/network_time_tracker.cc
@@ -180,7 +180,7 @@
       tick_clock_(std::move(tick_clock)),
       pref_service_(pref_service),
       time_query_completed_(false) {
-  const base::DictionaryValue* time_mapping =
+  const base::Value* time_mapping =
       pref_service_->GetDictionary(prefs::kNetworkTimeMapping);
   absl::optional<double> time_js = time_mapping->FindDoubleKey(kPrefTime);
   absl::optional<double> ticks_js = time_mapping->FindDoubleKey(kPrefTicks);
diff --git a/components/network_time/network_time_tracker_unittest.cc b/components/network_time/network_time_tracker_unittest.cc
index 3a40a5fe..4e638f2a 100644
--- a/components/network_time/network_time_tracker_unittest.cc
+++ b/components/network_time/network_time_tracker_unittest.cc
@@ -395,7 +395,7 @@
   EXPECT_EQ(NetworkTimeTracker::NETWORK_TIME_AVAILABLE,
             tracker_->GetNetworkTime(&out_network_time, nullptr));
   absl::optional<double> local, network;
-  const base::DictionaryValue* saved_prefs =
+  const base::Value* saved_prefs =
       pref_service_.GetDictionary(prefs::kNetworkTimeMapping);
   local = saved_prefs->FindDoubleKey("local");
   network = saved_prefs->FindDoubleKey("network");
diff --git a/components/ntp_snippets/category_rankers/click_based_category_ranker.cc b/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
index 4fec421..08d7df6 100644
--- a/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
+++ b/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
@@ -347,7 +347,7 @@
 bool ClickBasedCategoryRanker::ReadOrderFromPrefs(
     std::vector<RankedCategory>* result_categories) const {
   result_categories->clear();
-  const base::ListValue* list =
+  const base::Value* list =
       pref_service_->GetList(prefs::kClickBasedCategoryRankerOrderWithClicks);
   if (!list || list->GetList().size() == 0) {
     return false;
diff --git a/components/ntp_snippets/content_suggestions_service.cc b/components/ntp_snippets/content_suggestions_service.cc
index dc827a1..0fd34cb 100644
--- a/components/ntp_snippets/content_suggestions_service.cc
+++ b/components/ntp_snippets/content_suggestions_service.cc
@@ -673,8 +673,7 @@
   DCHECK(dismissed_providers_by_category_.empty());
   DCHECK(providers_by_category_.empty());
 
-  const base::ListValue* list =
-      pref_service_->GetList(prefs::kDismissedCategories);
+  const base::Value* list = pref_service_->GetList(prefs::kDismissedCategories);
   for (const base::Value& entry : list->GetList()) {
     if (!entry.is_int()) {
       DLOG(WARNING) << "Invalid category pref value: " << entry;
diff --git a/components/ntp_snippets/pref_util.cc b/components/ntp_snippets/pref_util.cc
index f004a75..9f269a9 100644
--- a/components/ntp_snippets/pref_util.cc
+++ b/components/ntp_snippets/pref_util.cc
@@ -15,7 +15,7 @@
 std::set<std::string> ReadDismissedIDsFromPrefs(const PrefService& pref_service,
                                                 const std::string& pref_name) {
   std::set<std::string> dismissed_ids;
-  const base::ListValue* list = pref_service.GetList(pref_name);
+  const base::Value* list = pref_service.GetList(pref_name);
   for (const base::Value& value : list->GetList()) {
     DCHECK(value.is_string())
         << "Failed to parse dismissed id from prefs param " << pref_name
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
index a57e9ed..604ed46 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
@@ -1555,7 +1555,7 @@
   // This must only be called at startup, before there are any categories.
   DCHECK(category_contents_.empty());
 
-  const base::ListValue* list =
+  const base::Value* list =
       pref_service_->GetList(prefs::kRemoteSuggestionCategories);
   for (const base::Value& entry : list->GetList()) {
     const base::DictionaryValue* dict = nullptr;
diff --git a/components/ntp_tiles/custom_links_store.cc b/components/ntp_tiles/custom_links_store.cc
index f6cbb1d..3d5515b 100644
--- a/components/ntp_tiles/custom_links_store.cc
+++ b/components/ntp_tiles/custom_links_store.cc
@@ -34,8 +34,7 @@
 std::vector<CustomLinksManager::Link> CustomLinksStore::RetrieveLinks() {
   std::vector<CustomLinksManager::Link> links;
 
-  const base::ListValue* stored_links =
-      prefs_->GetList(prefs::kCustomLinksList);
+  const base::Value* stored_links = prefs_->GetList(prefs::kCustomLinksList);
 
   for (const base::Value& link : stored_links->GetList()) {
     const base::Value* url_value = link.FindKey(kDictionaryKeyUrl);
diff --git a/components/ntp_tiles/most_visited_sites.cc b/components/ntp_tiles/most_visited_sites.cc
index ab8d89f2..29f2f780 100644
--- a/components/ntp_tiles/most_visited_sites.cc
+++ b/components/ntp_tiles/most_visited_sites.cc
@@ -755,7 +755,7 @@
 
 // static
 bool MostVisitedSites::WasNtpAppMigratedToWebApp(PrefService* prefs, GURL url) {
-  const base::ListValue* migrated_apps =
+  const base::Value* migrated_apps =
       prefs->GetList(webapps::kWebAppsMigratedPreinstalledApps);
   if (!migrated_apps)
     return false;
diff --git a/components/ntp_tiles/popular_sites_impl.cc b/components/ntp_tiles/popular_sites_impl.cc
index fbdbfef..746d1c7 100644
--- a/components/ntp_tiles/popular_sites_impl.cc
+++ b/components/ntp_tiles/popular_sites_impl.cc
@@ -271,7 +271,8 @@
       url_loader_factory_(std::move(url_loader_factory)),
       is_fallback_(false),
       sections_(
-          ParseSites(*prefs->GetList(prefs::kPopularSitesJsonPref),
+          ParseSites(base::Value::AsListValue(
+                         *prefs->GetList(prefs::kPopularSitesJsonPref)),
                      prefs_->GetInteger(prefs::kPopularSitesVersionPref))) {}
 
 PopularSitesImpl::~PopularSitesImpl() {}
@@ -396,7 +397,8 @@
 }
 
 const base::ListValue* PopularSitesImpl::GetCachedJson() {
-  return prefs_->GetList(prefs::kPopularSitesJsonPref);
+  return &base::Value::AsListValue(
+      *prefs_->GetList(prefs::kPopularSitesJsonPref));
 }
 
 // static
diff --git a/components/omnibox/browser/autocomplete_controller.h b/components/omnibox/browser/autocomplete_controller.h
index 4ca205e..c3c7cfe 100644
--- a/components/omnibox/browser/autocomplete_controller.h
+++ b/components/omnibox/browser/autocomplete_controller.h
@@ -119,11 +119,13 @@
   // result in changing the result set the observers is notified again. When the
   // controller is done the notification AUTOCOMPLETE_CONTROLLER_RESULT_READY is
   // sent.
-  void Start(const AutocompleteInput& input);
+  // Made virtual for mocking in tests.
+  virtual void Start(const AutocompleteInput& input);
 
   // Simply calls StartPrefetch() on all providers so those providers that
   // override it could perform a prefetch request and populate their caches.
-  void StartPrefetch(const AutocompleteInput& input);
+  // Made virtual for mocking in tests.
+  virtual void StartPrefetch(const AutocompleteInput& input);
 
   // Cancels the current query, ensuring there will be no future notifications
   // fired.  If new matches have come in since the most recent notification was
@@ -204,6 +206,7 @@
  private:
   friend class AutocompleteProviderTest;
   friend class OmniboxSuggestionButtonRowBrowserTest;
+  friend class ZeroSuggestPrefetchTabHelperBrowserTest;
   FRIEND_TEST_ALL_PREFIXES(AutocompleteProviderTest,
                            RedundantKeywordsIgnoredInResult);
   FRIEND_TEST_ALL_PREFIXES(AutocompleteProviderTest, UpdateAssistedQueryStats);
diff --git a/components/omnibox/browser/base_search_provider.cc b/components/omnibox/browser/base_search_provider.cc
index 7bcc4a1b..ab87f3e 100644
--- a/components/omnibox/browser/base_search_provider.cc
+++ b/components/omnibox/browser/base_search_provider.cc
@@ -227,7 +227,8 @@
          (classification == OEP::NTP_REALBOX) ||
          (classification == OEP::START_SURFACE_HOMEPAGE) ||
          (classification == OEP::START_SURFACE_NEW_TAB) ||
-         (classification == OEP::ANDROID_SHORTCUTS_WIDGET);
+         (classification == OEP::ANDROID_SHORTCUTS_WIDGET) ||
+         (classification == OEP::NTP_ZPS_PREFETCH);
 }
 
 // static
diff --git a/components/omnibox/browser/omnibox_controller.h b/components/omnibox/browser/omnibox_controller.h
index f0a637ac..9cab3c0 100644
--- a/components/omnibox/browser/omnibox_controller.h
+++ b/components/omnibox/browser/omnibox_controller.h
@@ -45,6 +45,11 @@
     return autocomplete_controller_.get();
   }
 
+  void set_autocomplete_controller(
+      std::unique_ptr<AutocompleteController> autocomplete_controller) {
+    autocomplete_controller_ = std::move(autocomplete_controller);
+  }
+
   // Set |current_match_| to an invalid value, indicating that we do not yet
   // have a valid match for the current text in the omnibox.
   void InvalidateCurrentMatch();
diff --git a/components/omnibox/browser/omnibox_edit_model.h b/components/omnibox/browser/omnibox_edit_model.h
index ca5259a..035b20e 100644
--- a/components/omnibox/browser/omnibox_edit_model.h
+++ b/components/omnibox/browser/omnibox_edit_model.h
@@ -78,6 +78,12 @@
     return omnibox_controller_->autocomplete_controller();
   }
 
+  void set_autocomplete_controller(
+      std::unique_ptr<AutocompleteController> autocomplete_controller) {
+    omnibox_controller_->set_autocomplete_controller(
+        std::move(autocomplete_controller));
+  }
+
   void set_popup_view(OmniboxPopupView* popup_view);
   OmniboxPopupView* get_popup_view();
 
diff --git a/components/omnibox/browser/omnibox_prefs.cc b/components/omnibox/browser/omnibox_prefs.cc
index 802b8c5e..f2fb007b0 100644
--- a/components/omnibox/browser/omnibox_prefs.cc
+++ b/components/omnibox/browser/omnibox_prefs.cc
@@ -62,7 +62,7 @@
     int suggestion_group_id) {
   DCHECK(prefs);
 
-  const base::DictionaryValue* dictionary =
+  const base::Value* dictionary =
       prefs->GetDictionary(kSuggestionGroupVisibility);
   DCHECK(dictionary);
 
diff --git a/components/omnibox/browser/omnibox_view.cc b/components/omnibox/browser/omnibox_view.cc
index c0bd69e..66b82f30 100644
--- a/components/omnibox/browser/omnibox_view.cc
+++ b/components/omnibox/browser/omnibox_view.cc
@@ -16,6 +16,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "components/bookmarks/browser/bookmark_model.h"
+#include "components/omnibox/browser/autocomplete_controller.h"
+#include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/location_bar_model.h"
 #include "components/omnibox/browser/omnibox_edit_controller.h"
@@ -254,6 +256,11 @@
     model_->StopAutocomplete();
 }
 
+void OmniboxView::StartPrefetch(const AutocompleteInput& input) {
+  if (model_)
+    model_->autocomplete_controller()->StartPrefetch(input);
+}
+
 bool OmniboxView::IsImeShowingPopup() const {
   // Default to claiming that the IME is not showing a popup, since hiding the
   // omnibox dropdown is a bad user experience when we don't know for sure that
diff --git a/components/omnibox/browser/omnibox_view.h b/components/omnibox/browser/omnibox_view.h
index a2b5e7a..b9fe13a 100644
--- a/components/omnibox/browser/omnibox_view.h
+++ b/components/omnibox/browser/omnibox_view.h
@@ -29,6 +29,7 @@
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/range/range.h"
 
+class AutocompleteInput;
 class GURL;
 class OmniboxEditController;
 class OmniboxViewMacTest;
@@ -152,6 +153,10 @@
   // defines a method with that name.
   virtual void CloseOmniboxPopup();
 
+  // Starts an autocomplete prefetch query so those providers that benefit from
+  // it could perform a prefetch request and populate their caches.
+  virtual void StartPrefetch(const AutocompleteInput& input);
+
   // Sets the focus to the omnibox. |is_user_initiated| is true when the user
   // explicitly focused the omnibox, and false when the omnibox was
   // automatically focused (like for browser startup or NTP load).
diff --git a/components/omnibox/browser/shortcuts_backend_unittest.cc b/components/omnibox/browser/shortcuts_backend_unittest.cc
index 3bd3d9b..0f72c78 100644
--- a/components/omnibox/browser/shortcuts_backend_unittest.cc
+++ b/components/omnibox/browser/shortcuts_backend_unittest.cc
@@ -85,7 +85,10 @@
   bool changed_notified_ = false;
 };
 
-ShortcutsBackendTest::ShortcutsBackendTest() = default;
+ShortcutsBackendTest::ShortcutsBackendTest() {
+  scoped_feature_list().InitAndDisableFeature(
+      omnibox::kPreserveLongerShortcutsText);
+}
 
 ShortcutsDatabase::Shortcut::MatchCore
 ShortcutsBackendTest::MatchCoreForTesting(const std::string& url,
@@ -440,6 +443,7 @@
 class ShortcutsBackendLongerShortcutsTest : public ShortcutsBackendTest {
  public:
   ShortcutsBackendLongerShortcutsTest() {
+    scoped_feature_list().Reset();
     scoped_feature_list().InitAndEnableFeature(
         omnibox::kPreserveLongerShortcutsText);
   }
diff --git a/components/omnibox/browser/shortcuts_provider_unittest.cc b/components/omnibox/browser/shortcuts_provider_unittest.cc
index 6a5f30a..10d157b 100644
--- a/components/omnibox/browser/shortcuts_provider_unittest.cc
+++ b/components/omnibox/browser/shortcuts_provider_unittest.cc
@@ -220,7 +220,12 @@
   scoped_refptr<ShortcutsProvider> provider_;
 };
 
-ShortcutsProviderTest::ShortcutsProviderTest() {}
+ShortcutsProviderTest::ShortcutsProviderTest() {
+  // `scoped_feature_list_` needs to be initialized as early as possible, to
+  // avoid data races caused by tasks on other threads accessing it.
+  scoped_feature_list_.InitAndDisableFeature(
+      omnibox::kPreserveLongerShortcutsText);
+}
 
 void ShortcutsProviderTest::SetUp() {
   client_ = std::make_unique<FakeAutocompleteProviderClient>();
@@ -518,6 +523,7 @@
   ShortcutsProviderPreserveLongerShortcutsTest() {
     // `scoped_feature_list_` needs to be initialized as early as possible, to
     // avoid data races caused by tasks on other threads accessing it.
+    scoped_feature_list_.Reset();
     scoped_feature_list_.InitAndEnableFeature(
         omnibox::kPreserveLongerShortcutsText);
   }
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index feb25b8..f029588 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -210,7 +210,7 @@
 // enabled, they preserve up to 3 additional chars. See `GetShortcutText()` in
 // shortcuts_backend.cc for details.
 const base::Feature kPreserveLongerShortcutsText{
-    "OmniboxPreserveLongerShortcutsText", base::FEATURE_DISABLED_BY_DEFAULT};
+    "OmniboxPreserveLongerShortcutsText", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // If enabled, inputs may match bookmark paths. These path matches won't
 // contribute to scoring. E.g. 'planets jupiter' can suggest a bookmark titled
diff --git a/components/omnibox/resources/omnibox_pedal_synonyms.grd b/components/omnibox/resources/omnibox_pedal_synonyms.grd
index 18d8315..7dfc458 100644
--- a/components/omnibox/resources/omnibox_pedal_synonyms.grd
+++ b/components/omnibox/resources/omnibox_pedal_synonyms.grd
@@ -37,7 +37,7 @@
       <output filename="omnibox_pedal_synonyms_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="omnibox_pedal_synonyms_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="omnibox_pedal_synonyms_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="omnibox_pedal_synonyms_am.pak" type="data_package" lang="am" />
diff --git a/components/omnibox/resources/omnibox_pedal_synonyms.grd_unused b/components/omnibox/resources/omnibox_pedal_synonyms.grd_unused
index 79b7e4a..0a6c56c 100644
--- a/components/omnibox/resources/omnibox_pedal_synonyms.grd_unused
+++ b/components/omnibox/resources/omnibox_pedal_synonyms.grd_unused
@@ -41,7 +41,7 @@
       <output filename="omnibox_pedal_synonyms_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="omnibox_pedal_synonyms_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="omnibox_pedal_synonyms_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="omnibox_pedal_synonyms_am.pak" type="data_package" lang="am" />
diff --git a/components/omnibox/resources/omnibox_resources.grd b/components/omnibox/resources/omnibox_resources.grd
index dcbb2f5..dfb8eb33 100644
--- a/components/omnibox/resources/omnibox_resources.grd
+++ b/components/omnibox/resources/omnibox_resources.grd
@@ -38,7 +38,7 @@
       <output filename="omnibox_resources_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="omnibox_resources_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="omnibox_resources_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="omnibox_resources_am.pak" type="data_package" lang="am" />
diff --git a/components/optimization_guide/core/hints_fetcher.cc b/components/optimization_guide/core/hints_fetcher.cc
index 3909ae5..273e1ebf 100644
--- a/components/optimization_guide/core/hints_fetcher.cc
+++ b/components/optimization_guide/core/hints_fetcher.cc
@@ -62,21 +62,6 @@
   return std::string();
 }
 
-// Returns the subset of URLs from |urls| for which the URL is considered
-// valid and can be included in a hints fetch.
-std::vector<GURL> GetValidURLsForFetching(const std::vector<GURL>& urls) {
-  std::vector<GURL> valid_urls;
-  for (const auto& url : urls) {
-    if (valid_urls.size() >=
-        features::MaxUrlsForOptimizationGuideServiceHintsFetch()) {
-      break;
-    }
-    if (IsValidURLForURLKeyedHint(url))
-      valid_urls.push_back(url);
-  }
-  return valid_urls;
-}
-
 void RecordRequestStatusHistogram(proto::RequestContext request_context,
                                   HintsFetcherRequestStatus status) {
   base::UmaHistogramEnumeration(
@@ -190,16 +175,17 @@
     HintsFetchedCallback hints_fetched_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_GT(optimization_types.size(), 0u);
+  request_context_ = request_context;
 
   if (network_connection_tracker_->IsOffline()) {
-    RecordRequestStatusHistogram(request_context,
+    RecordRequestStatusHistogram(request_context_,
                                  HintsFetcherRequestStatus::kNetworkOffline);
     std::move(hints_fetched_callback).Run(absl::nullopt);
     return false;
   }
 
   if (active_url_loader_) {
-    RecordRequestStatusHistogram(request_context,
+    RecordRequestStatusHistogram(request_context_,
                                  HintsFetcherRequestStatus::kFetcherBusy);
     std::move(hints_fetched_callback).Run(absl::nullopt);
     return false;
@@ -207,10 +193,10 @@
 
   std::vector<std::string> filtered_hosts =
       GetSizeLimitedHostsDueForHintsRefresh(hosts);
-  std::vector<GURL> valid_urls = GetValidURLsForFetching(urls);
+  std::vector<GURL> valid_urls = GetSizeLimitedURLsForFetching(urls);
   if (filtered_hosts.empty() && valid_urls.empty()) {
     RecordRequestStatusHistogram(
-        request_context, HintsFetcherRequestStatus::kNoHostsOrURLsToFetch);
+        request_context_, HintsFetcherRequestStatus::kNoHostsOrURLsToFetch);
     std::move(hints_fetched_callback).Run(absl::nullopt);
     return false;
   }
@@ -222,14 +208,13 @@
 
   if (optimization_types.empty()) {
     RecordRequestStatusHistogram(
-        request_context,
+        request_context_,
         HintsFetcherRequestStatus::kNoSupportedOptimizationTypes);
     std::move(hints_fetched_callback).Run(absl::nullopt);
     return false;
   }
 
   hints_fetch_start_time_ = base::TimeTicks::Now();
-  request_context_ = request_context;
 
   proto::GetHintsRequest get_hints_request;
   get_hints_request.add_supported_key_representations(proto::HOST);
@@ -445,6 +430,26 @@
   HandleResponse(response_body ? *response_body : "", net_error, response_code);
 }
 
+// Returns the subset of URLs from |urls| for which the URL is considered
+// valid and can be included in a hints fetch.
+std::vector<GURL> HintsFetcher::GetSizeLimitedURLsForFetching(
+    const std::vector<GURL>& urls) const {
+  std::vector<GURL> valid_urls;
+  for (size_t i = 0; i < urls.size(); i++) {
+    if (valid_urls.size() >=
+        features::MaxUrlsForOptimizationGuideServiceHintsFetch()) {
+      base::UmaHistogramCounts100(
+          "OptimizationGuide.HintsFetcher.GetHintsRequest.DroppedUrls." +
+              GetStringNameForRequestContext(request_context_),
+          urls.size() - i);
+      break;
+    }
+    if (IsValidURLForURLKeyedHint(urls[i]))
+      valid_urls.push_back(urls[i]);
+  }
+  return valid_urls;
+}
+
 std::vector<std::string> HintsFetcher::GetSizeLimitedHostsDueForHintsRefresh(
     const std::vector<std::string>& hosts) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -455,7 +460,17 @@
   std::vector<std::string> target_hosts;
   target_hosts.reserve(hosts.size());
 
-  for (const auto& host : hosts) {
+  for (size_t i = 0; i < hosts.size(); i++) {
+    if (target_hosts.size() >=
+        features::MaxHostsForOptimizationGuideServiceHintsFetch()) {
+      base::UmaHistogramCounts100(
+          "OptimizationGuide.HintsFetcher.GetHintsRequest.DroppedHosts." +
+              GetStringNameForRequestContext(request_context_),
+          hosts.size() - i);
+      break;
+    }
+
+    std::string host = hosts[i];
     // Skip over localhosts, IP addresses, and invalid hosts.
     if (net::HostStringIsLocalhost(host))
       continue;
@@ -479,11 +494,6 @@
     }
     if (host_hints_due_for_refresh)
       target_hosts.push_back(host);
-
-    if (target_hosts.size() >=
-        features::MaxHostsForOptimizationGuideServiceHintsFetch()) {
-      break;
-    }
   }
   DCHECK_GE(features::MaxHostsForOptimizationGuideServiceHintsFetch(),
             target_hosts.size());
diff --git a/components/optimization_guide/core/hints_fetcher.h b/components/optimization_guide/core/hints_fetcher.h
index 95c2000..7fc3f0e4 100644
--- a/components/optimization_guide/core/hints_fetcher.h
+++ b/components/optimization_guide/core/hints_fetcher.h
@@ -142,6 +142,11 @@
   // in the pref.
   void UpdateHostsSuccessfullyFetched(base::TimeDelta valid_duration);
 
+  // Returns the subset of URLs from |urls| for which the URL is considered
+  // valid and can be included in a hints fetch.
+  std::vector<GURL> GetSizeLimitedURLsForFetching(
+      const std::vector<GURL>& urls) const;
+
   // Returns the subset of hosts from |hosts| for which the hints should be
   // refreshed. The count of returned hosts is limited to
   // features::MaxHostsForOptimizationGuideServiceHintsFetch().
diff --git a/components/optimization_guide/core/hints_fetcher_unittest.cc b/components/optimization_guide/core/hints_fetcher_unittest.cc
index 58e9c14..7605284 100644
--- a/components/optimization_guide/core/hints_fetcher_unittest.cc
+++ b/components/optimization_guide/core/hints_fetcher_unittest.cc
@@ -606,6 +606,8 @@
 }
 
 TEST_P(HintsFetcherTest, MaxHostsForOptimizationGuideServiceHintsFetch) {
+  base::HistogramTester histogram_tester;
+
   std::string response_content;
   std::vector<std::string> all_hosts;
 
@@ -640,6 +642,12 @@
     EXPECT_TRUE(
         WasHostCoveredByFetch("host" + base::NumberToString(i) + ".com"));
   }
+
+  // extra1.com and extra2.com should have been considered "dropped".
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.DroppedHosts."
+      "BatchUpdateActiveTabs",
+      2, 1);
 }
 
 TEST_P(HintsFetcherTest, MaxUrlsForOptimizationGuideServiceHintsFetch) {
@@ -677,6 +685,12 @@
     EXPECT_EQ(last_request.urls(i).url(),
               "https://url" + base::NumberToString(i) + ".com/");
   }
+
+  // notfetched.com and notfetched-2.com should have been considered "dropped".
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.DroppedUrls."
+      "BatchUpdateActiveTabs",
+      2, 1);
 }
 
 TEST_P(HintsFetcherTest, OnlyURLsToFetch) {
@@ -697,6 +711,11 @@
   histogram_tester.ExpectUniqueSample(
       "OptimizationGuide.HintsFetcher.RequestStatus.BatchUpdateActiveTabs",
       static_cast<int>(HintsFetcherRequestStatus::kSuccess), 1);
+  // Nothing was dropped so this shouldn't be recorded.
+  histogram_tester.ExpectTotalCount(
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.DroppedHosts", 0);
+  histogram_tester.ExpectTotalCount(
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.DroppedUrls", 0);
 }
 
 TEST_P(HintsFetcherTest, NoHostsOrURLsToFetch) {
diff --git a/components/password_manager/core/browser/hash_password_manager.cc b/components/password_manager/core/browser/hash_password_manager.cc
index 4f3b2683..19aa516 100644
--- a/components/password_manager/core/browser/hash_password_manager.cc
+++ b/components/password_manager/core/browser/hash_password_manager.cc
@@ -222,8 +222,7 @@
   if (!prefs_ || !prefs_->HasPrefPath(prefs::kPasswordHashDataList))
     return result;
 
-  const base::ListValue* hash_list =
-      prefs_->GetList(prefs::kPasswordHashDataList);
+  const base::Value* hash_list = prefs_->GetList(prefs::kPasswordHashDataList);
 
   for (const base::Value& entry : hash_list->GetList()) {
     absl::optional<PasswordHashData> password_hash_data =
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index fb2fb49..00feeda 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -1148,6 +1148,12 @@
 
   for (auto& manager : form_managers_)
     manager->ProcessServerPredictions(predictions_);
+
+  PasswordGenerationFrameHelper* password_generation_manager =
+      driver ? driver->GetPasswordGenerationHelper() : nullptr;
+  if (password_generation_manager) {
+    password_generation_manager->ProcessPasswordRequirements(forms);
+  }
 }
 
 PasswordFormManager* PasswordManager::GetSubmittedManager() const {
diff --git a/components/password_manager/core/browser/password_manager_features_util.cc b/components/password_manager/core/browser/password_manager_features_util.cc
index 3bc8b89..e651733 100644
--- a/components/password_manager/core/browser/password_manager_features_util.cc
+++ b/components/password_manager/core/browser/password_manager_features_util.cc
@@ -85,7 +85,7 @@
 // Returns the total number of accounts for which an opt-in to the account
 // storage exists. Used for metrics.
 int GetNumberOfOptedInAccounts(const PrefService* pref_service) {
-  const base::DictionaryValue* global_pref =
+  const base::Value* global_pref =
       pref_service->GetDictionary(prefs::kAccountStoragePerAccountSettings);
   int count = 0;
   for (auto entry : global_pref->DictItems()) {
@@ -100,7 +100,7 @@
  public:
   AccountStorageSettingsReader(const PrefService* prefs,
                                const GaiaIdHash& gaia_id_hash) {
-    const base::DictionaryValue* global_pref =
+    const base::Value* global_pref =
         prefs->GetDictionary(prefs::kAccountStoragePerAccountSettings);
     if (global_pref)
       account_settings_ = global_pref->FindDictKey(gaia_id_hash.ToBase64());
diff --git a/components/permissions/permission_actions_history.cc b/components/permissions/permission_actions_history.cc
index d468cdf..b78cb48e 100644
--- a/components/permissions/permission_actions_history.cc
+++ b/components/permissions/permission_actions_history.cc
@@ -51,7 +51,7 @@
 std::vector<PermissionActionsHistory::Entry>
 PermissionActionsHistory::GetHistory(const base::Time& begin,
                                      EntryFilter entry_filter) {
-  const base::DictionaryValue* dictionary =
+  const base::Value* dictionary =
       pref_service_->GetDictionary(prefs::kPermissionActions);
   if (!dictionary)
     return {};
diff --git a/components/permissions_strings.grdp b/components/permissions_strings.grdp
index a0ccba06..0bc7ebd 100644
--- a/components/permissions_strings.grdp
+++ b/components/permissions_strings.grdp
@@ -83,7 +83,7 @@
       Allow 2 permissions?
     </message>
 </if>
-  <if expr="is_android or chromeos or lacros or is_win">
+  <if expr="is_android or chromeos_ash or chromeos_lacros or is_win">
     <message name="IDS_PROTECTED_MEDIA_IDENTIFIER_PERMISSION_FRAGMENT" desc="Permission fragment shown in the permissions bubble when a web page requests access to the computer's protected media identifier.">
       Know your unique device identifier
     </message>
diff --git a/components/policy/core/browser/configuration_policy_handler.cc b/components/policy/core/browser/configuration_policy_handler.cc
index e8b9727..5bc81c96 100644
--- a/components/policy/core/browser/configuration_policy_handler.cc
+++ b/components/policy/core/browser/configuration_policy_handler.cc
@@ -242,14 +242,13 @@
   if (!input)
     return true;
 
-  const base::ListValue* list_value = nullptr;
-  if (!input->GetAsList(&list_value)) {
+  if (!input->is_list()) {
     NOTREACHED();
     return false;
   }
 
   int index = -1;
-  for (const auto& entry : list_value->GetList()) {
+  for (const auto& entry : input->GetList()) {
     ++index;
     if (!entry.is_string()) {
       if (errors) {
diff --git a/components/policy/core/browser/url_blocklist_manager.cc b/components/policy/core/browser/url_blocklist_manager.cc
index 37d297a..244825593d 100644
--- a/components/policy/core/browser/url_blocklist_manager.cc
+++ b/components/policy/core/browser/url_blocklist_manager.cc
@@ -78,11 +78,11 @@
 #endif
 
 // Returns a blocklist based on the given |block| and |allow| pattern lists.
-std::unique_ptr<URLBlocklist> BuildBlocklist(const base::ListValue* block,
-                                             const base::ListValue* allow) {
+std::unique_ptr<URLBlocklist> BuildBlocklist(const base::Value* block,
+                                             const base::Value* allow) {
   auto blocklist = std::make_unique<URLBlocklist>();
-  blocklist->Block(block);
-  blocklist->Allow(allow);
+  blocklist->Block(&base::Value::AsListValue(*block));
+  blocklist->Allow(&base::Value::AsListValue(*allow));
   return blocklist;
 }
 
@@ -241,12 +241,10 @@
       background_task_runner_.get(), FROM_HERE,
       base::BindOnce(
           &BuildBlocklist,
-          base::Owned(pref_service_->GetList(policy_prefs::kUrlBlocklist)
-                          ->CreateDeepCopy()
-                          .release()),
-          base::Owned(pref_service_->GetList(policy_prefs::kUrlAllowlist)
-                          ->CreateDeepCopy()
-                          .release())),
+          base::Owned(base::Value::ToUniquePtrValue(
+              pref_service_->GetList(policy_prefs::kUrlBlocklist)->Clone())),
+          base::Owned(base::Value::ToUniquePtrValue(
+              pref_service_->GetList(policy_prefs::kUrlAllowlist)->Clone()))),
       base::BindOnce(&URLBlocklistManager::SetBlocklist,
                      ui_weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/components/policy/core/browser/url_blocklist_policy_handler.cc b/components/policy/core/browser/url_blocklist_policy_handler.cc
index 3b3d5f1..992268d9 100644
--- a/components/policy/core/browser/url_blocklist_policy_handler.cc
+++ b/components/policy/core/browser/url_blocklist_policy_handler.cc
@@ -20,6 +20,7 @@
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_value_map.h"
 #include "components/strings/grit/components_strings.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace policy {
 
@@ -93,40 +94,36 @@
 void URLBlocklistPolicyHandler::ApplyPolicySettings(const PolicyMap& policies,
                                                     PrefValueMap* prefs) {
   const base::Value* url_blocklist_policy = policies.GetValue(policy_name());
-  const base::ListValue* url_blocklist = nullptr;
-  if (url_blocklist_policy) {
-    url_blocklist_policy->GetAsList(&url_blocklist);
-  }
-
   const base::Value* disabled_schemes_policy =
       policies.GetValue(key::kDisabledSchemes);
-  const base::ListValue* disabled_schemes = nullptr;
-  if (disabled_schemes_policy)
-    disabled_schemes_policy->GetAsList(&disabled_schemes);
 
-  std::vector<base::Value> merged_url_blocklist;
+  absl::optional<std::vector<base::Value>> merged_url_blocklist;
 
   // We start with the DisabledSchemes because we have size limit when
   // handling URLBlocklists.
-  if (disabled_schemes) {
-    for (const auto& entry : disabled_schemes->GetList()) {
+  if (disabled_schemes_policy && disabled_schemes_policy->is_list()) {
+    merged_url_blocklist = std::vector<base::Value>();
+    for (const auto& entry : disabled_schemes_policy->GetList()) {
       if (entry.is_string()) {
-        merged_url_blocklist.emplace_back(
+        merged_url_blocklist->emplace_back(
             base::StrCat({entry.GetString(), "://*"}));
       }
     }
   }
 
-  if (url_blocklist) {
-    for (const auto& entry : url_blocklist->GetList()) {
+  if (url_blocklist_policy && url_blocklist_policy->is_list()) {
+    if (!merged_url_blocklist)
+      merged_url_blocklist = std::vector<base::Value>();
+
+    for (const auto& entry : url_blocklist_policy->GetList()) {
       if (entry.is_string())
-        merged_url_blocklist.push_back(entry.Clone());
+        merged_url_blocklist->push_back(entry.Clone());
     }
   }
 
-  if (disabled_schemes || url_blocklist) {
+  if (merged_url_blocklist) {
     prefs->SetValue(policy_prefs::kUrlBlocklist,
-                    base::Value(std::move(merged_url_blocklist)));
+                    base::Value(std::move(merged_url_blocklist.value())));
   }
 }
 
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index 45415046..51b741a 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -356,7 +356,6 @@
     "mock_policy_service.h",
     "policy_test_utils.cc",
     "policy_test_utils.h",
-    "remote_commands/test_support/blocking_queue.h",
     "remote_commands/test_support/echo_remote_command_job.cc",
     "remote_commands/test_support/echo_remote_command_job.h",
     "remote_commands/test_support/testing_remote_commands_server.cc",
diff --git a/components/policy/core/common/remote_commands/remote_commands_service_unittest.cc b/components/policy/core/common/remote_commands/remote_commands_service_unittest.cc
index e4f20c3..c6c283e 100644
--- a/components/policy/core/common/remote_commands/remote_commands_service_unittest.cc
+++ b/components/policy/core/common/remote_commands/remote_commands_service_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
+#include "base/test/repeating_test_future.h"
 #include "base/test/test_future.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -28,7 +29,6 @@
 #include "components/policy/core/common/remote_commands/remote_command_job.h"
 #include "components/policy/core/common/remote_commands/remote_commands_factory.h"
 #include "components/policy/core/common/remote_commands/remote_commands_queue.h"
-#include "components/policy/core/common/remote_commands/test_support/blocking_queue.h"
 #include "components/policy/core/common/remote_commands/test_support/echo_remote_command_job.h"
 #include "components/policy/core/common/remote_commands/test_support/testing_remote_commands_server.h"
 #include "components/policy/proto/device_management_backend.pb.h"
@@ -282,7 +282,7 @@
 
   // Wait until a new job is constructed.
   FakeJob& WaitForJob() {
-    FakeJob* result = created_jobs_.Pop();
+    FakeJob* result = created_jobs_.Take();
     DCHECK_NE(result, nullptr);
     return *result;
   }
@@ -294,13 +294,13 @@
       RemoteCommandsService* service) override {
     auto result = std::make_unique<FakeJob>(type);
 
-    created_jobs_.Push(result.get());
+    created_jobs_.AddValue(result.get());
     return result;
   }
 
-  // Asynchronous queue, populated in BuildJobForType() and consumed in
+  // List of all jobs created in BuildJobForType() and consumed in
   // WaitForJob().
-  test::BlockingQueue<FakeJob*> created_jobs_;
+  base::test::RepeatingTestFuture<FakeJob*> created_jobs_;
 };
 
 // Mocked RemoteCommand factory.
diff --git a/components/policy/core/common/remote_commands/test_support/blocking_queue.h b/components/policy/core/common/remote_commands/test_support/blocking_queue.h
deleted file mode 100644
index a90a5eb..0000000
--- a/components/policy/core/common/remote_commands/test_support/blocking_queue.h
+++ /dev/null
@@ -1,112 +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 COMPONENTS_POLICY_CORE_COMMON_REMOTE_COMMANDS_TEST_SUPPORT_BLOCKING_QUEUE_H_
-#define COMPONENTS_POLICY_CORE_COMMON_REMOTE_COMMANDS_TEST_SUPPORT_BLOCKING_QUEUE_H_
-
-#include <memory>
-#include <queue>
-#include <utility>
-
-#include "base/check.h"
-#include "base/run_loop.h"
-#include "base/sequence_checker.h"
-#include "base/thread_annotations.h"
-
-namespace policy {
-namespace test {
-
-// FIFO Queue that supports pushing and popping of elements from different tasks
-// (on the same sequence).
-// Only to be used during unittests!
-// If no elements are available (yet), the call to Pop() will block until
-// an element arrives (through a call to Push()).
-//
-// All access should happen from the same sequence.
-template <typename Type>
-class BlockingQueue {
- public:
-  BlockingQueue() = default;
-  BlockingQueue(const BlockingQueue&) = delete;
-  BlockingQueue& operator=(const BlockingQueue&) = delete;
-  ~BlockingQueue() = default;
-
-  void Push(Type element) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    elements_.push(std::move(element));
-    SignalElementIsAvailable();
-  }
-
-  // Waits until an element is available.
-  // Returns immediately if elements are already available.
-  //
-  // Returns true if an element arrived, or false if a timeout happens.
-  // A timeout can only happen if |base::test::ScopedRunLoopTimeout| is used in
-  // the calling context. In case of a timeout, the test will be failed
-  // automatically by |base::test::ScopedRunLoopTimeout|, however if you want to
-  // provide a better error message you can always add an explicit check:
-  //
-  //   ASSERT_TRUE(queue.Wait()) << "Detailed error message";
-  //
-  bool Wait() {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    if (elements_.empty())
-      WaitForElement();
-
-    return !IsEmpty();
-  }
-
-  // Returns the first element from this queue (FIFO).
-  // If no elements are available, this will wait until one arrives.
-  //
-  // Will DCHECK if a timeout happens.
-  Type Pop() {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    // Ensure an element is available.
-    Wait();
-
-    DCHECK(!IsEmpty());
-    Type result = elements_.front();
-    elements_.pop();
-    return result;
-  }
-
-  bool IsEmpty() const {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    return elements_.empty();
-  }
-
- private:
-  // Wait until a new element is available.
-  void WaitForElement() VALID_CONTEXT_REQUIRED(sequence_checker_) {
-    DCHECK_EQ(run_loop_, nullptr);
-
-    run_loop_ = std::make_unique<base::RunLoop>();
-    // This blocks until 'run_loop_->Quit()' is called.
-    run_loop_->Run();
-    run_loop_ = nullptr;
-  }
-
-  void SignalElementIsAvailable() VALID_CONTEXT_REQUIRED(sequence_checker_) {
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-
-  std::queue<Type> elements_ GUARDED_BY_CONTEXT(sequence_checker_);
-
-  // Used by Wait() to know when Push() is called.
-  std::unique_ptr<base::RunLoop> run_loop_
-      GUARDED_BY_CONTEXT(sequence_checker_);
-
-  SEQUENCE_CHECKER(sequence_checker_);
-};
-
-}  // namespace test
-}  // namespace policy
-
-#endif  // COMPONENTS_POLICY_CORE_COMMON_REMOTE_COMMANDS_TEST_SUPPORT_BLOCKING_QUEUE_H_
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index c56266d..1ad38b5 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -13032,7 +13032,7 @@
           'type': 'string',
         },
       },
-      'supported_on': ['chrome_os:98-'],
+      'future_on': ['chrome_os'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
diff --git a/components/policy/resources/policy_templates_es.xtb b/components/policy/resources/policy_templates_es.xtb
index a75150ed..7d024d5 100644
--- a/components/policy/resources/policy_templates_es.xtb
+++ b/components/policy/resources/policy_templates_es.xtb
@@ -4883,7 +4883,7 @@
       Si se le asigna el valor "true", <ph name="PLUGIN_VM_NAME" /> estará habilitado para el usuario (si los demás ajustes lo permiten). A <ph name="PLUGIN_VM_ALLOWED_POLICY_NAME" /> y <ph name="USER_PLUGIN_VM_ALLOWED_POLICY_NAME" /> se les debe haber asignado el valor "true", y <ph name="PLUGIN_VM_LICENSE_KEY_POLICY_NAME" /> o <ph name="PLUGIN_VM_USER_ID_POLICY_NAME" /> deben haberse configurado para que <ph name="PLUGIN_VM_NAME" /> pueda ejecutarse.</translation>
 <translation id="6752711782954612641">Si <ph name="DEFAULT_SEARCH_PROVIDER_ENABLED_POLICY_NAME" /> está activada y se asigna un valor a <ph name="DEFAULT_SEARCH_PROVIDER_SEARCH_URL_POST_PARAMS_POLICY_NAME" />, se especificarán los parámetros utilizados al buscar una URL con POST. Se compone de pares de nombre-valor separados por comas. Si un valor es un parámetro de plantilla, como <ph name="SEARCH_TERM_MARKER" />, se sustituirá por datos de términos de búsqueda reales.
 
-      Si no se asigna ningún valor a <ph name="DEFAULT_SEARCH_PROVIDER_SEARCH_URL_POST_PARAMS_POLICY_NAME" />, las solicitudes de búsqueda se enviarán mediante el método "get".</translation>
+      Si no se asigna ningún valor a <ph name="DEFAULT_SEARCH_PROVIDER_SEARCH_URL_POST_PARAMS_POLICY_NAME" />, las solicitudes de búsqueda se enviarán mediante el método GET.</translation>
 <translation id="6757613329154374267">Copia de seguridad y restauración habilitada</translation>
 <translation id="6758659208493449452">Esta política controla si los usuarios registrados en el Programa de Protección Avanzada tienen acceso a funciones de protección adicionales. Puede que algunas de estas funciones impliquen compartir datos con Google (por ejemplo, los usuarios de Protección Avanzada podrán enviar sus descargas a Google para que realice un análisis de software malicioso). Si se asigna el valor "True" a esta política o no se le asigna ningún valor, los usuarios registrados tendrán acceso a funciones de protección adicionales. Si se le asigna el valor "False", los usuarios de Protección Avanzada solo tendrán acceso a las funciones estándar.</translation>
 <translation id="6766216162565713893">Permitir que los sitios le pidan permiso al usuario para acceder a dispositivos Bluetooth cercanos</translation>
@@ -6287,7 +6287,7 @@
           Si no se establece esta política, se utilizará BlockKeygen y el usuario podrá modificarla.</translation>
 <translation id="8313927126392971570">Si <ph name="DEFAULT_SEARCH_PROVIDER_ENABLED_POLICY_NAME" /> está activada y se asigna un valor a <ph name="DEFAULT_SEARCH_PROVIDER_IMAGE_URL_POST_PARMS_POLICY_NAME" />, se especificarán los parámetros utilizados al buscar imágenes con POST. Se compone de pares de nombre-valor separados por comas. Si un valor es un parámetro de plantilla, como {imageThumbnail}, se sustituirá por datos de miniaturas de imágenes reales.
 
-      Si no se asigna ningún valor a <ph name="DEFAULT_SEARCH_PROVIDER_IMAGE_URL_POST_PARMS_POLICY_NAME" />, la solicitud de búsqueda de imágenes se enviará mediante el método "get".</translation>
+      Si no se asigna ningún valor a <ph name="DEFAULT_SEARCH_PROVIDER_IMAGE_URL_POST_PARMS_POLICY_NAME" />, la solicitud de búsqueda de imágenes se enviará mediante el método GET.</translation>
 <translation id="8314214821702356835">Permite a los usuarios reproducir contenido multimedia cuando el dispositivo está bloqueado</translation>
 <translation id="8319678975002906774">Establece valores de la configuración administrada aplicados a sitios web para orígenes determinados</translation>
 <translation id="8320149248919453401">Modo de carga de la batería</translation>
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py
index c1275d9..05c78bb 100755
--- a/components/policy/tools/generate_policy_source.py
+++ b/components/policy/tools/generate_policy_source.py
@@ -12,6 +12,7 @@
 
 
 from argparse import ArgumentParser
+from collections import defaultdict
 from collections import namedtuple
 from collections import OrderedDict
 from functools import partial
@@ -1340,24 +1341,40 @@
       policies, protobuf_type)
   f.write('const std::array<%sPolicyAccess, %d> k%sPolicyAccess {{\n' %
           (protobuf_type, len(supported_user_policies), protobuf_type))
-  extra_args = ''
   for policy in supported_user_policies:
     name = policy.name
+    lowercase_name = name.lower()
+
     if protobuf_type == 'String':
       extra_args = ',\n   ' + _GetStringPolicyType(policy.policy_type)
+    else:
+      extra_args = ''
+
+    chunk_number = _ChunkNumber(policy.id)
+    if chunk_number == 0:
+      has_proto = 'policy.has_%s()' % lowercase_name
+      get_proto = 'policy.%s()' % lowercase_name
+    else:
+      has_subproto = 'policy.has_subproto%d() &&\n' % chunk_number
+      has_policy = '              policy.subproto%d().has_%s()' % (
+          chunk_number, lowercase_name)
+      has_proto = has_subproto + has_policy
+      get_proto = 'policy.subproto%d().%s()' % (chunk_number, lowercase_name)
+
     f.write('  {key::k%s,\n'
             '   %s,\n'
             '   [](const em::CloudPolicySettings& policy) {\n'
-            '     return policy.has_%s();\n'
+            '     return %s;\n'
             '   },\n'
             '   [](const em::CloudPolicySettings& policy)\n'
             '       -> const em::%sPolicyProto& {\n'
-            '     return policy.%s();\n'
+            '     return %s;\n'
             '   }%s\n'
-            '  },\n' % (name, str(policy.per_profile).lower(), name.lower(),
-                        protobuf_type, name.lower(), extra_args))
+            '  },\n' % (name, str(policy.per_profile).lower(), has_proto,
+                        protobuf_type, get_proto, extra_args))
   f.write('}};\n\n')
 
+
 #------------------ policy risk tag header -------------------------#
 
 
@@ -1471,11 +1488,23 @@
 option go_package="chromium/policy/enterprise_management_proto";
 
 import "policy_common_definitions{full_runtime_suffix}.proto";
+
 '''
 
 # Field IDs [1..RESERVED_IDS] will not be used in the wrapping protobuf.
 RESERVED_IDS = 2
 
+# All user policies with ID <= |_LAST_TOP_LEVEL_POLICY_ID| are added to the top
+# level of ChromeSettingsProto and CloudPolicySettings, whereas all policies
+# with ID > |_LAST_TOP_LEVEL_POLICY_ID| are nested into sub-protos. See
+# https://crbug.com/1237044 for more details.
+_LAST_TOP_LEVEL_POLICY_ID = 979
+
+# The approximate number of policies in one nested chunk for user policies with
+# ID > |_LAST_TOP_LEVEL_POLICY_ID|. See https://crbug.com/1237044 for more
+# details.
+_CHUNK_SIZE = 800
+
 
 def _WritePolicyProto(f, policy):
   _OutputComment(f, policy.caption + '\n\n' + policy.desc)
@@ -1500,6 +1529,27 @@
   f.write('}\n\n')
 
 
+def _ChunkNumber(policy_id):
+  # Compute which chunk the policy should go to. Chunk 0 contains the legacy
+  # policies, whereas subsequent chunks contain nested policies.
+  if policy_id <= _LAST_TOP_LEVEL_POLICY_ID:
+    return 0
+  else:
+    return (policy_id - _LAST_TOP_LEVEL_POLICY_ID - 1) // _CHUNK_SIZE + 1
+
+
+def _FieldNumber(policy_id, chunk_number):
+  if chunk_number == 0:
+    # For the top-level policies the field number in the proto file is the
+    # same as the id assigned to the policy in policy_templates.json,
+    # skipping the RESERVED_IDS.
+    return policy_id + RESERVED_IDS
+  else:
+    # For the nested policies, the field numbers should always be in the
+    # range [1, _CHUNK_SIZE], therefore we calculate them using this formula.
+    return (policy_id - _LAST_TOP_LEVEL_POLICY_ID - 1) % _CHUNK_SIZE + 1
+
+
 def _WriteChromeSettingsProtobuf(policies,
                                  policy_atomic_groups,
                                  target_platform,
@@ -1514,7 +1564,7 @@
       CHROME_SETTINGS_PROTO_HEAD.format(
           full_runtime_comment=full_runtime_comment,
           full_runtime_suffix=full_runtime_suffix))
-  fields = []
+  fields = defaultdict(list)
   f.write('// PBs for individual settings.\n\n')
   for policy in policies:
     # Note: This protobuf also gets the unsupported policies, since it's an
@@ -1522,17 +1572,40 @@
     if not policy.is_device_only:
       # Write the individual policy proto into the file
       _WritePolicyProto(f, policy)
-      # Add to |fields| in order to eventually add to ChromeSettingsProto
-      fields += [
-          '  optional %sProto %s = %s;\n' %
-          (policy.name, policy.name, policy.id + RESERVED_IDS)
-      ]
+
+      chunk_number = _ChunkNumber(policy.id)
+      field_number = _FieldNumber(policy.id, chunk_number)
+
+      # Add to |fields| in order to eventually add to ChromeSettingsProto.
+      fields[chunk_number].append('  optional %sProto %s = %s;\n' %
+                                  (policy.name, policy.name, field_number))
+
+  sorted_chunk_numbers = sorted(fields.keys())
+
+  if len(sorted_chunk_numbers) > 1:
+    f.write('// --------------------------------------------------\n'
+            '// PBs for policies with ID > %d.\n\n' % _LAST_TOP_LEVEL_POLICY_ID)
+
+    for sorted_chunk_number in sorted_chunk_numbers:
+      if sorted_chunk_number == 0:
+        continue
+      f.write('message ChromeSettingsSubProto%d {\n' % sorted_chunk_number)
+      f.write(''.join(fields[sorted_chunk_number]))
+      f.write('}\n\n')
 
   f.write('// --------------------------------------------------\n'
           '// Big wrapper PB containing the above groups.\n\n'
           'message ChromeSettingsProto {\n')
-  f.write(''.join(fields))
-  f.write('}\n\n')
+
+  for sorted_chunk_number in sorted_chunk_numbers:
+    if sorted_chunk_number == 0:
+      f.write(''.join(fields[sorted_chunk_number]))
+    else:
+      f.write('  optional ChromeSettingsSubProto%d subProto%d = %s;\n' %
+              (sorted_chunk_number, sorted_chunk_number,
+               _LAST_TOP_LEVEL_POLICY_ID + RESERVED_IDS + sorted_chunk_number))
+
+  f.write('}\n')
 
 
 def _WriteCloudPolicyProtobuf(policies,
@@ -1548,13 +1621,42 @@
   f.write(
       CLOUD_POLICY_PROTO_HEAD.format(full_runtime_comment=full_runtime_comment,
                                      full_runtime_suffix=full_runtime_suffix))
-  f.write('message CloudPolicySettings {\n')
+
+  fields = defaultdict(list)
+
   for policy in policies:
-    if policy.is_supported and not policy.is_device_only:
-      f.write(
-          '  optional %sPolicyProto %s = %s;\n' %
-          (policy.policy_protobuf_type, policy.name, policy.id + RESERVED_IDS))
-  f.write('}\n\n')
+    if not policy.is_supported or policy.is_device_only:
+      continue
+
+    chunk_number = _ChunkNumber(policy.id)
+    field_number = _FieldNumber(policy.id, chunk_number)
+
+    # Add to |fields| in order to eventually add to CloudPolicyProto.
+    fields[chunk_number].append(
+        '  optional %sPolicyProto %s = %s;\n' %
+        (policy.policy_protobuf_type, policy.name, field_number))
+
+  sorted_chunk_numbers = sorted(fields.keys())
+
+  if len(sorted_chunk_numbers) > 1:
+    for sorted_chunk_number in sorted_chunk_numbers:
+      if sorted_chunk_number == 0:
+        continue
+      f.write('message CloudPolicySubProto%d {\n' % sorted_chunk_number)
+      f.write(''.join(fields[sorted_chunk_number]))
+      f.write('}\n\n')
+
+  f.write('message CloudPolicySettings {\n')
+
+  for sorted_chunk_number in sorted_chunk_numbers:
+    if sorted_chunk_number == 0:
+      f.write(''.join(fields[sorted_chunk_number]))
+    else:
+      f.write('  optional CloudPolicySubProto%d subProto%d = %s;\n' %
+              (sorted_chunk_number, sorted_chunk_number,
+               _LAST_TOP_LEVEL_POLICY_ID + RESERVED_IDS + sorted_chunk_number))
+
+  f.write('}\n')
 
 
 def _WritePolicyCommonDefinitionsFullRuntimeProtobuf(
diff --git a/components/policy/tools/generate_policy_source_test.py b/components/policy/tools/generate_policy_source_test.py
index 9a8fa2d..6acf648 100755
--- a/components/policy/tools/generate_policy_source_test.py
+++ b/components/policy/tools/generate_policy_source_test.py
@@ -6,6 +6,7 @@
 import codecs
 import unittest
 from unittest.mock import patch, mock_open, call
+from typing import NamedTuple
 
 import generate_policy_source
 import generate_policy_source_test_data as test_data
@@ -13,6 +14,12 @@
 from generate_policy_source import PolicyDetails
 
 
+class PolicyData(NamedTuple):
+  policy_id: int
+  chunk_number: int
+  field_number: int
+
+
 class PolicyGenerationTest(unittest.TestCase):
 
   TEMPLATES_JSON = {
@@ -85,6 +92,61 @@
           "tags": [],
           "caption": "CloudOnlyPolicy caption",
           "desc": "CloudOnlyPolicy desc",
+      }, {
+          "name": "ChunkZeroLastFieldBooleanPolicy",
+          "type": "main",
+          "schema": {
+              "type": "boolean"
+          },
+          "supported_on": ["chrome_os:99-"],
+          "id": 979,
+          "tags": [],
+          "caption": "ChunkZeroLastFieldBooleanPolicy caption",
+          "desc": "ChunkZeroLastFieldBooleanPolicy desc.",
+      }, {
+          "name": "ChunkOneFirstFieldBooleanPolicy",
+          "type": "main",
+          "schema": {
+              "type": "boolean"
+          },
+          "supported_on": ["chrome_os:99-"],
+          "id": 980,
+          "tags": [],
+          "caption": "ChunkOneFirstFieldBooleanPolicy caption",
+          "desc": "ChunkOneFirstFieldBooleanPolicy desc.",
+      }, {
+          "name": "ChunkOneLastFieldBooleanPolicy",
+          "type": "main",
+          "schema": {
+              "type": "boolean"
+          },
+          "supported_on": ["chrome_os:99-"],
+          "id": 1779,
+          "tags": [],
+          "caption": "ChunkOneLastFieldBooleanPolicy caption",
+          "desc": "ChunkOneLastFieldBooleanPolicy desc.",
+      }, {
+          "name": "ChunkTwoFirstFieldStringPolicy",
+          "type": "string",
+          "schema": {
+              "type": "string"
+          },
+          "supported_on": ["chrome_os:99-"],
+          "id": 1780,
+          "tags": [],
+          "caption": "ChunkTwoFirstFieldStringPolicy caption",
+          "desc": "ChunkTwoFirstFieldStringPolicy desc"
+      }, {
+          "name": "ChunkTwoLastFieldStringPolicy",
+          "type": "string",
+          "schema": {
+              "type": "string"
+          },
+          "supported_on": ["chrome_os:99-"],
+          "id": 2579,
+          "tags": [],
+          "caption": "ChunkTwoLastFieldStringPolicy caption",
+          "desc": "ChunkTwoLastFieldStringPolicy desc"
       }],
       "policy_atomic_group_definitions": []
   }
@@ -356,5 +418,45 @@
                            test_data.EXPECTED_APP_RESTRICTIONS_XML)
 
 
+  def testChunkNumberAndFieldNumber(self):
+    test_data = [
+        # Last top-level policy
+        PolicyData(policy_id=979, chunk_number=0, field_number=981),
+        # First policy in chunk 1
+        PolicyData(policy_id=980, chunk_number=1, field_number=1),
+        # Last policy in chunk 1
+        PolicyData(policy_id=1779, chunk_number=1, field_number=800),
+        # First policy in chunk 2
+        PolicyData(policy_id=1780, chunk_number=2, field_number=1),
+        # Last policy in chunk 2
+        PolicyData(policy_id=2579, chunk_number=2, field_number=800),
+        # First policy in chunk 3
+        PolicyData(policy_id=2580, chunk_number=3, field_number=1),
+        # Last policy in chunk 3
+        PolicyData(policy_id=3379, chunk_number=3, field_number=800),
+        # First policy in chunk 501
+        PolicyData(policy_id=400980, chunk_number=501, field_number=1),
+        # Last policy in chunk 501
+        PolicyData(policy_id=401779, chunk_number=501, field_number=800),
+        # First policy in chunk 502
+        PolicyData(policy_id=401780, chunk_number=502, field_number=1),
+        # Last policy in chunk 502
+        PolicyData(policy_id=402579, chunk_number=502, field_number=800),
+        # First policy in chunk 503
+        PolicyData(policy_id=402580, chunk_number=503, field_number=1),
+        # Last policy in chunk 503
+        PolicyData(policy_id=403379, chunk_number=503, field_number=800),
+    ]
+
+    for policy_data in test_data:
+      self.assertEqual(
+          generate_policy_source._ChunkNumber(policy_data.policy_id),
+          policy_data.chunk_number)
+      self.assertEqual(
+          generate_policy_source._FieldNumber(policy_data.policy_id,
+                                              policy_data.chunk_number),
+          policy_data.field_number)
+
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/components/policy/tools/generate_policy_source_test_data.py b/components/policy/tools/generate_policy_source_test_data.py
index add2c798..f5ed79e6 100644
--- a/components/policy/tools/generate_policy_source_test_data.py
+++ b/components/policy/tools/generate_policy_source_test_data.py
@@ -17,6 +17,7 @@
 option go_package="chromium/policy/enterprise_management_proto";
 
 import "policy_common_definitions%(full_runtime_suffix)s.proto";
+
 message CloudPolicySettings {
   optional StringPolicyProto ExampleStringPolicy = 3;
   optional BooleanPolicyProto ExampleBoolPolicy = 4;
@@ -90,6 +91,69 @@
   optional bool CloudOnlyPolicy = 2;
 }
 
+// ChunkZeroLastFieldBooleanPolicy caption
+//
+// ChunkZeroLastFieldBooleanPolicy desc.
+//
+// Supported on:
+message ChunkZeroLastFieldBooleanPolicyProto {
+  optional PolicyOptions policy_options = 1;
+  optional bool ChunkZeroLastFieldBooleanPolicy = 2;
+}
+
+// ChunkOneFirstFieldBooleanPolicy caption
+//
+// ChunkOneFirstFieldBooleanPolicy desc.
+//
+// Supported on:
+message ChunkOneFirstFieldBooleanPolicyProto {
+  optional PolicyOptions policy_options = 1;
+  optional bool ChunkOneFirstFieldBooleanPolicy = 2;
+}
+
+// ChunkOneLastFieldBooleanPolicy caption
+//
+// ChunkOneLastFieldBooleanPolicy desc.
+//
+// Supported on:
+message ChunkOneLastFieldBooleanPolicyProto {
+  optional PolicyOptions policy_options = 1;
+  optional bool ChunkOneLastFieldBooleanPolicy = 2;
+}
+
+// ChunkTwoFirstFieldStringPolicy caption
+//
+// ChunkTwoFirstFieldStringPolicy desc
+//
+// Supported on:
+message ChunkTwoFirstFieldStringPolicyProto {
+  optional PolicyOptions policy_options = 1;
+  optional string ChunkTwoFirstFieldStringPolicy = 2;
+}
+
+// ChunkTwoLastFieldStringPolicy caption
+//
+// ChunkTwoLastFieldStringPolicy desc
+//
+// Supported on:
+message ChunkTwoLastFieldStringPolicyProto {
+  optional PolicyOptions policy_options = 1;
+  optional string ChunkTwoLastFieldStringPolicy = 2;
+}
+
+// --------------------------------------------------
+// PBs for policies with ID > 979.
+
+message ChromeSettingsSubProto1 {
+  optional ChunkOneFirstFieldBooleanPolicyProto ChunkOneFirstFieldBooleanPolicy = 1;
+  optional ChunkOneLastFieldBooleanPolicyProto ChunkOneLastFieldBooleanPolicy = 800;
+}
+
+message ChromeSettingsSubProto2 {
+  optional ChunkTwoFirstFieldStringPolicyProto ChunkTwoFirstFieldStringPolicy = 1;
+  optional ChunkTwoLastFieldStringPolicyProto ChunkTwoLastFieldStringPolicy = 800;
+}
+
 // --------------------------------------------------
 // Big wrapper PB containing the above groups.
 
@@ -99,6 +163,9 @@
   optional ExampleBoolMergeMetapolicyProto ExampleBoolMergeMetapolicy = 5;
   optional ExampleBoolPrecedenceMetapolicyProto ExampleBoolPrecedenceMetapolicy = 6;
   optional CloudOnlyPolicyProto CloudOnlyPolicy = 7;
+  optional ChunkZeroLastFieldBooleanPolicyProto ChunkZeroLastFieldBooleanPolicy = 981;
+  optional ChromeSettingsSubProto1 subProto1 = 982;
+  optional ChromeSettingsSubProto2 subProto2 = 983;
 }
 """
 
@@ -158,6 +225,11 @@
 extern const char kExampleBoolMergeMetapolicy[];
 extern const char kExampleBoolPrecedenceMetapolicy[];
 extern const char kCloudOnlyPolicy[];
+extern const char kChunkZeroLastFieldBooleanPolicy[];
+extern const char kChunkOneFirstFieldBooleanPolicy[];
+extern const char kChunkOneLastFieldBooleanPolicy[];
+extern const char kChunkTwoFirstFieldStringPolicy[];
+extern const char kChunkTwoLastFieldStringPolicy[];
 
 }  // namespace key
 
@@ -369,6 +441,11 @@
 const char kExampleBoolMergeMetapolicy[] = "ExampleBoolMergeMetapolicy";
 const char kExampleBoolPrecedenceMetapolicy[] = "ExampleBoolPrecedenceMetapolicy";
 const char kCloudOnlyPolicy[] = "CloudOnlyPolicy";
+const char kChunkZeroLastFieldBooleanPolicy[] = "ChunkZeroLastFieldBooleanPolicy";
+const char kChunkOneFirstFieldBooleanPolicy[] = "ChunkOneFirstFieldBooleanPolicy";
+const char kChunkOneLastFieldBooleanPolicy[] = "ChunkOneLastFieldBooleanPolicy";
+const char kChunkTwoFirstFieldStringPolicy[] = "ChunkTwoFirstFieldStringPolicy";
+const char kChunkTwoLastFieldStringPolicy[] = "ChunkTwoLastFieldStringPolicy";
 
 }  // namespace key
 
diff --git a/components/policy_strings.grdp b/components/policy_strings.grdp
index 6dae5625..cf7b6370 100644
--- a/components/policy_strings.grdp
+++ b/components/policy_strings.grdp
@@ -271,7 +271,7 @@
 <ph name="DEBUG_INFO">$1<ex>ERROR: Field name 'SomeRandomField' is unknown. (at toplevel)</ex></ph>
     </message>
   </if>
-  <if expr="chromeos or lacros">
+  <if expr="chromeos_ash or chromeos_lacros">
     <message name="IDS_POLICY_SCOPE_ERROR" desc="Text displayed in the status column when a policy is set in an unsupported scope.">
       Policy scope is not supported.
     </message>
diff --git a/components/prefs/pref_service.cc b/components/prefs/pref_service.cc
index 3644899d..b392e98 100644
--- a/components/prefs/pref_service.cc
+++ b/components/prefs/pref_service.cc
@@ -298,8 +298,7 @@
   return GetPreferenceValueChecked(path);
 }
 
-const base::DictionaryValue* PrefService::GetDictionary(
-    const std::string& path) const {
+const base::Value* PrefService::GetDictionary(const std::string& path) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   const base::Value* value = GetPreferenceValueChecked(path);
@@ -309,7 +308,7 @@
     NOTREACHED();
     return nullptr;
   }
-  return static_cast<const base::DictionaryValue*>(value);
+  return value;
 }
 
 const base::Value* PrefService::GetUserPrefValue(
@@ -352,7 +351,7 @@
   return value;
 }
 
-const base::ListValue* PrefService::GetList(const std::string& path) const {
+const base::Value* PrefService::GetList(const std::string& path) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   const base::Value* value = GetPreferenceValueChecked(path);
@@ -362,7 +361,7 @@
     NOTREACHED();
     return nullptr;
   }
-  return static_cast<const base::ListValue*>(value);
+  return value;
 }
 
 void PrefService::AddPrefObserver(const std::string& path, PrefObserver* obs) {
diff --git a/components/prefs/pref_service.h b/components/prefs/pref_service.h
index 24b5820..318271e 100644
--- a/components/prefs/pref_service.h
+++ b/components/prefs/pref_service.h
@@ -233,8 +233,8 @@
   // Note that |path| must point to a registered preference. In that case, these
   // functions will never return NULL.
   const base::Value* Get(const std::string& path) const;
-  const base::DictionaryValue* GetDictionary(const std::string& path) const;
-  const base::ListValue* GetList(const std::string& path) const;
+  const base::Value* GetDictionary(const std::string& path) const;
+  const base::Value* GetList(const std::string& path) const;
 
   // Removes a user pref and restores the pref to its default value.
   void ClearPref(const std::string& path);
diff --git a/components/prefs/scoped_user_pref_update_unittest.cc b/components/prefs/scoped_user_pref_update_unittest.cc
index 3c1bee2..050606d 100644
--- a/components/prefs/scoped_user_pref_update_unittest.cc
+++ b/components/prefs/scoped_user_pref_update_unittest.cc
@@ -55,7 +55,7 @@
     Mock::VerifyAndClearExpectations(&observer_);
 
     // Modifications happen online and are instantly visible, though.
-    const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref);
+    const base::Value* current_value = prefs_.GetDictionary(kPref);
     ASSERT_TRUE(current_value);
     EXPECT_TRUE(expected_dictionary.Equals(current_value));
 
@@ -64,18 +64,18 @@
   }
   Mock::VerifyAndClearExpectations(&observer_);
 
-  const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref);
+  const base::Value* current_value = prefs_.GetDictionary(kPref);
   ASSERT_TRUE(current_value);
   EXPECT_TRUE(expected_dictionary.Equals(current_value));
 }
 
 TEST_F(ScopedUserPrefUpdateTest, NeverTouchAnything) {
-  const base::DictionaryValue* old_value = prefs_.GetDictionary(kPref);
+  const base::Value* old_value = prefs_.GetDictionary(kPref);
   EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
   {
     DictionaryPrefUpdate update(&prefs_, kPref);
   }
-  const base::DictionaryValue* new_value = prefs_.GetDictionary(kPref);
+  const base::Value* new_value = prefs_.GetDictionary(kPref);
   EXPECT_EQ(old_value, new_value);
   Mock::VerifyAndClearExpectations(&observer_);
 }
diff --git a/components/printing_component_strings.grdp b/components/printing_component_strings.grdp
index 0e36b12..f1ad867b 100644
--- a/components/printing_component_strings.grdp
+++ b/components/printing_component_strings.grdp
@@ -12,7 +12,7 @@
     Print Compositor Service
   </message>
 
-  <if expr="chromeos or lacros">
+  <if expr="chromeos_ash or chromeos_lacros">
     <message name="IDS_PRINT_CHAMBER_HUMIDITY" desc="PWG5100.21 (8.1.1) chamber-humidity">
       Chamber humidity
     </message>
diff --git a/components/proxy_config/pref_proxy_config_tracker_impl.cc b/components/proxy_config/pref_proxy_config_tracker_impl.cc
index 17e984e..a3fa01b 100644
--- a/components/proxy_config/pref_proxy_config_tracker_impl.cc
+++ b/components/proxy_config/pref_proxy_config_tracker_impl.cc
@@ -262,7 +262,7 @@
       pref_service->FindPreference(proxy_config::prefs::kProxy);
   DCHECK(pref);
 
-  const base::DictionaryValue* dict =
+  const base::Value* dict =
       pref_service->GetDictionary(proxy_config::prefs::kProxy);
   DCHECK(dict);
   ProxyConfigDictionary proxy_dict(dict->Clone());
diff --git a/components/query_tiles/internal/tile_service_scheduler_impl.cc b/components/query_tiles/internal/tile_service_scheduler_impl.cc
index badee9aa..2a93704 100644
--- a/components/query_tiles/internal/tile_service_scheduler_impl.cc
+++ b/components/query_tiles/internal/tile_service_scheduler_impl.cc
@@ -145,7 +145,7 @@
 
 std::unique_ptr<net::BackoffEntry> TileServiceSchedulerImpl::GetBackoff() {
   std::unique_ptr<net::BackoffEntry> result;
-  const base::ListValue* value = prefs_->GetList(kBackoffEntryKey);
+  const base::Value* value = prefs_->GetList(kBackoffEntryKey);
   if (value) {
     result = net::BackoffEntrySerializer::DeserializeFromValue(
         *value, backoff_policy_.get(), tick_clock_, clock_->Now());
diff --git a/components/query_tiles/internal/tile_service_scheduler_unittest.cc b/components/query_tiles/internal/tile_service_scheduler_unittest.cc
index 2cbb0b9..8d207ef3 100644
--- a/components/query_tiles/internal/tile_service_scheduler_unittest.cc
+++ b/components/query_tiles/internal/tile_service_scheduler_unittest.cc
@@ -88,7 +88,7 @@
 
   std::unique_ptr<net::BackoffEntry> GetBackoffPolicy() {
     std::unique_ptr<net::BackoffEntry> result;
-    const base::ListValue* value = prefs()->GetList(kBackoffEntryKey);
+    const base::Value* value = prefs()->GetList(kBackoffEntryKey);
     if (value) {
       result = net::BackoffEntrySerializer::DeserializeFromValue(
           *value, &kTestPolicy, tick_clock(), clock()->Now());
diff --git a/components/reporting/DIR_METADATA b/components/reporting/DIR_METADATA
new file mode 100644
index 0000000..f332abae1
--- /dev/null
+++ b/components/reporting/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//ash/components/policy/COMMON_METADATA"
diff --git a/components/reporting/metrics/fake_sampler.cc b/components/reporting/metrics/fake_sampler.cc
index 125a49c..d92ada7 100644
--- a/components/reporting/metrics/fake_sampler.cc
+++ b/components/reporting/metrics/fake_sampler.cc
@@ -16,7 +16,7 @@
 
 void FakeSampler::Collect(MetricCallback cb) {
   num_calls_++;
-  std::move(cb).Run(std::move(metric_data_));
+  std::move(cb).Run(metric_data_);
 }
 
 void FakeSampler::SetMetricData(MetricData metric_data) {
diff --git a/components/reporting/proto/synced/metric_data.proto b/components/reporting/proto/synced/metric_data.proto
index 4a1cf7e..8568b80 100644
--- a/components/reporting/proto/synced/metric_data.proto
+++ b/components/reporting/proto/synced/metric_data.proto
@@ -108,15 +108,15 @@
   // Access point address.
   optional string access_point_address = 2;
   // Transmission bit rate measured in Mbps.
-  optional uint32 tx_bit_rate_mbps = 3;
+  optional int64 tx_bit_rate_mbps = 3;
   // Receiving bit rate measured in Mbps.
-  optional uint32 rx_bit_rate_mbps = 4;
+  optional int64 rx_bit_rate_mbps = 4;
   // Transmission power measured in dBm.
   optional int32 tx_power_dbm = 5;
   // Is wifi encryption key on or not.
   optional bool encryption_on = 6;
   // Wifi link quality.
-  optional uint32 link_quality = 7;
+  optional int64 link_quality = 7;
   // Wifi signal level in dBm.
   optional int32 signal_level_dbm = 8;
 }
@@ -148,7 +148,7 @@
   THUNDERBOLT_SECURITY_USB_ONLY_LEVEL = 5;
   // PCIE tunneling is disabled.
   THUNDERBOLT_SECURITY_NO_PCIE_LEVEL = 6;
-};
+}
 
 message ThunderboltInfo {
   optional ThunderboltSecurityLevel security_level = 1;
@@ -188,9 +188,9 @@
   // The state of memory encryption on the device.
   optional MemoryEncryptionState encryption_state = 1;
   // The maximum number of keys that can be used for encryption.
-  optional uint32 max_keys = 2;
+  optional int64 max_keys = 2;
   // The length of the encryption keys.
-  optional uint32 key_length = 3;
+  optional int64 key_length = 3;
   // The encryption algorithm being used on the device.
   optional MemoryEncryptionAlgorithm encryption_algorithm = 4;
 }
@@ -200,6 +200,50 @@
   optional TotalMemoryEncryptionInfo tme_info = 1;
 }
 
+enum NetworkDeviceType {
+  NETWORK_DEVICE_TYPE_UNSPECIFIED = 0;
+  CELLULAR_DEVICE = 1;
+  ETHERNET_DEVICE = 2;
+  WIFI_DEVICE = 5;
+}
+
+// Info details about network interface.
+message NetworkInterface {
+  // Network device type.
+  optional NetworkDeviceType type = 1;
+
+  // MAC address (if applicable) of the corresponding network device. This is
+  // formatted as an ASCII string with 12 hex digits. Example: A0B1C2D3E4F5.
+  optional string mac_address = 2;
+
+  // MEID (if applicable) of the corresponding network device. Formatted as
+  // ASCII string composed of 14 hex digits. Example: A10000009296F2.
+  optional string meid = 3;
+
+  // IMEI (if applicable) of the corresponding network device. 15-16 decimal
+  // digits encoded as ASCII string. Example: 355402040158759.
+  optional string imei = 4;
+
+  // The device path associated with this network interface.
+  optional string device_path = 5;
+
+  // The integrated circuit card ID associated with the device's sim card.
+  optional string iccid = 6;
+
+  // The mobile directory number associated with the device's sim card.
+  optional string mdn = 7;
+
+  // List of EID (EUICC Identifier) of all cellular EUICCs
+  // (Embedded Universal Integrated Circuit Cards) on the device.
+  // 32 decimal digits encoded as ASCII string.
+  repeated string eids = 8;
+}
+
+// Networks info data.
+message NetworksInfo {
+  repeated NetworkInterface network_interfaces = 1;
+}
+
 // Information about keylocker. This is supported on Intel CPUs.
 message KeylockerInfo {
   // If keylocker is supported on the devices CPUs.
@@ -222,6 +266,8 @@
   optional BusDeviceInfo bus_device_info = 2;
   // Memory info for the device.
   optional MemoryInfo memory_info = 3;
+  // Network interfaces info.
+  optional NetworksInfo networks_info = 4;
 }
 
 // Audio telemetry data recorded intermittently
@@ -257,12 +303,29 @@
   NETWORK_CONNECTION_STATE_CHANGE = 2;
   NETWORK_SIGNAL_STRENGTH_CHANGE = 3;
   AUDIO_SEVERE_UNDERRUN = 4;
+  USB_ADDED = 5;
+  USB_REMOVED = 6;
+}
+
+message UsbEventData {
+  // Vendor name
+  optional string vendor = 1;
+  // Device name, model name, or product name
+  optional string name = 2;
+  // Vendor ID
+  optional int64 vid = 3;
+  // Product ID
+  optional int64 pid = 4;
+  // List of names of USB categories the device falls under
+  // https://www.usb.org/defined-class-codes
+  repeated string categories = 5;
 }
 
 // Indicates one of the following conditions occurred on the device, data
 // associated with the event will be reported as TelemetryData.
 message EventData {
   optional MetricEventType type = 1;
+  optional UsbEventData usb_event_data = 2;
 }
 
 // Main message to be reported, can contain `InfoData`, `TelemetryData`, or
diff --git a/components/resources/autofill_and_password_manager_internals_resources.grdp b/components/resources/autofill_and_password_manager_internals_resources.grdp
index 04be13c9..a194178 100644
--- a/components/resources/autofill_and_password_manager_internals_resources.grdp
+++ b/components/resources/autofill_and_password_manager_internals_resources.grdp
@@ -1,11 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <grit-part>
-  <if expr="not is_ios">
-    <include name="IDR_AUTOFILL_AND_PASSWORD_MANAGER_INTERNALS_HTML" file="../autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html" type="BINDATA" />
-  </if>
-  <if expr="is_ios">
-    <include name="IDR_AUTOFILL_AND_PASSWORD_MANAGER_INTERNALS_HTML" file="../autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals_ios.html" type="BINDATA" />
-  </if>
+  <include name="IDR_AUTOFILL_AND_PASSWORD_MANAGER_INTERNALS_HTML" file="../autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html" type="BINDATA" />
   <include name="IDR_AUTOFILL_AND_PASSWORD_MANAGER_INTERNALS_JS" file="../autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js" preprocess="true" type="BINDATA" />
-  <include name="IDR_AUTOFILL_AND_PASSWORD_MANAGER_INTERNALS_CSS" file="../autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.css" type="BINDATA" />
 </grit-part>
diff --git a/components/safe_browsing/content/browser/triggers/trigger_throttler.cc b/components/safe_browsing/content/browser/triggers/trigger_throttler.cc
index 2e97419..a55541c0 100644
--- a/components/safe_browsing/content/browser/triggers/trigger_throttler.cc
+++ b/components/safe_browsing/content/browser/triggers/trigger_throttler.cc
@@ -198,7 +198,7 @@
   if (!local_state_prefs_)
     return;
 
-  const base::DictionaryValue* event_dict = local_state_prefs_->GetDictionary(
+  const base::Value* event_dict = local_state_prefs_->GetDictionary(
       prefs::kSafeBrowsingTriggerEventTimestamps);
   for (auto trigger_pair : event_dict->DictItems()) {
     // Check that the first item in the pair is convertible to a trigger type
diff --git a/components/safe_browsing/content/browser/triggers/trigger_throttler_unittest.cc b/components/safe_browsing/content/browser/triggers/trigger_throttler_unittest.cc
index b4c7ba61..8aa1eb6 100644
--- a/components/safe_browsing/content/browser/triggers/trigger_throttler_unittest.cc
+++ b/components/safe_browsing/content/browser/triggers/trigger_throttler_unittest.cc
@@ -180,7 +180,7 @@
 
   // Check the pref directly, it should reflect the events for each trigger.
   PrefService* prefs = get_pref_service();
-  const base::DictionaryValue* event_dict =
+  const base::Value* event_dict =
       prefs->GetDictionary(prefs::kSafeBrowsingTriggerEventTimestamps);
 
   const std::string kAdSampleKey = "2";
diff --git a/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc b/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc
index f4bd833a..b99ae6f 100644
--- a/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc
+++ b/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc
@@ -263,7 +263,7 @@
 
 const base::Value* SafeBrowsingMetricsCollector::GetSafeBrowsingEventDictionary(
     UserState user_state) {
-  const base::DictionaryValue* state_dict =
+  const base::Value* state_dict =
       pref_service_->GetDictionary(prefs::kSafeBrowsingEventTimestamps);
 
   return state_dict->FindDictKey(UserStateToPrefKey(user_state));
diff --git a/components/safe_browsing/core/browser/safe_browsing_metrics_collector_unittest.cc b/components/safe_browsing/core/browser/safe_browsing_metrics_collector_unittest.cc
index a79fd99..757bc23 100644
--- a/components/safe_browsing/core/browser/safe_browsing_metrics_collector_unittest.cc
+++ b/components/safe_browsing/core/browser/safe_browsing_metrics_collector_unittest.cc
@@ -44,7 +44,7 @@
 
   const base::Value* GetTsFromUserStateAndEventType(UserState state,
                                                     EventType event_type) {
-    const base::DictionaryValue* state_dict =
+    const base::Value* state_dict =
         pref_service_.GetDictionary(prefs::kSafeBrowsingEventTimestamps);
     const base::Value* event_dict =
         state_dict->FindDictKey(base::NumberToString(static_cast<int>(state)));
diff --git a/components/safe_browsing/core/browser/tailored_security_service/tailored_security_service.cc b/components/safe_browsing/core/browser/tailored_security_service/tailored_security_service.cc
index 7ce8b7e..e57ad07 100644
--- a/components/safe_browsing/core/browser/tailored_security_service/tailored_security_service.cc
+++ b/components/safe_browsing/core/browser/tailored_security_service/tailored_security_service.cc
@@ -356,12 +356,12 @@
     bool is_enabled,
     base::Time previous_update) {
   if (is_tailored_security_enabled_ != is_enabled) {
-    is_tailored_security_enabled_ = is_enabled;
-    last_updated_ = base::Time::Now();
     for (auto& observer : observer_list_) {
       observer.OnTailoredSecurityBitChanged(is_enabled, previous_update);
     }
   }
+  is_tailored_security_enabled_ = is_enabled;
+  last_updated_ = base::Time::Now();
 }
 
 void TailoredSecurityService::QueryTailoredSecurityBitCompletionCallback(
diff --git a/components/safe_browsing/core/common/safe_browsing_prefs.cc b/components/safe_browsing/core/common/safe_browsing_prefs.cc
index bacfcd9..05e74a72 100644
--- a/components/safe_browsing/core/common/safe_browsing_prefs.cc
+++ b/components/safe_browsing/core/common/safe_browsing_prefs.cc
@@ -288,8 +288,8 @@
 
 base::ListValue GetSafeBrowsingPoliciesList(PrefService* prefs) {
   base::ListValue preferences_list;
-  const base::ListValue* allowlist_domains =
-      prefs->GetList(prefs::kSafeBrowsingAllowlistDomains);
+  const base::ListValue* allowlist_domains = &base::Value::AsListValue(
+      *prefs->GetList(prefs::kSafeBrowsingAllowlistDomains));
   std::vector<std::string> domain_list;
   CanonicalizeDomainList(*allowlist_domains, &domain_list);
   std::string domains;
@@ -321,8 +321,8 @@
 void GetSafeBrowsingAllowlistDomainsPref(
     const PrefService& prefs,
     std::vector<std::string>* out_canonicalized_domain_list) {
-  const base::ListValue* pref_value =
-      prefs.GetList(prefs::kSafeBrowsingAllowlistDomains);
+  const base::ListValue* pref_value = &base::Value::AsListValue(
+      *prefs.GetList(prefs::kSafeBrowsingAllowlistDomains));
   CanonicalizeDomainList(*pref_value, out_canonicalized_domain_list);
 }
 
@@ -344,7 +344,7 @@
 bool IsURLAllowlistedByPolicy(const GURL& url, const PrefService& pref) {
   if (!pref.HasPrefPath(prefs::kSafeBrowsingAllowlistDomains))
     return false;
-  const base::ListValue* allowlist =
+  const base::Value* allowlist =
       pref.GetList(prefs::kSafeBrowsingAllowlistDomains);
   for (const base::Value& value : allowlist->GetList()) {
     if (url.DomainIs(value.GetString()))
@@ -355,7 +355,7 @@
 
 std::vector<std::string> GetURLAllowlistByPolicy(PrefService* pref_service) {
   std::vector<std::string> allowlist_domains;
-  const base::ListValue* allowlist =
+  const base::Value* allowlist =
       pref_service->GetList(prefs::kSafeBrowsingAllowlistDomains);
   for (const base::Value& value : allowlist->GetList()) {
     allowlist_domains.push_back(value.GetString());
@@ -374,7 +374,7 @@
 
 void GetPasswordProtectionLoginURLsPref(const PrefService& prefs,
                                         std::vector<GURL>* out_login_url_list) {
-  const base::ListValue* pref_value =
+  const base::Value* pref_value =
       prefs.GetList(prefs::kPasswordProtectionLoginURLs);
   out_login_url_list->clear();
   for (const base::Value& value : pref_value->GetList()) {
diff --git a/components/search_engines/default_search_manager.cc b/components/search_engines/default_search_manager.cc
index 9a7f8b60..51aaa6dc 100644
--- a/components/search_engines/default_search_manager.cc
+++ b/components/search_engines/default_search_manager.cc
@@ -290,8 +290,8 @@
   DCHECK(pref);
   default_search_controlled_by_policy_ = pref->IsManaged();
 
-  const base::DictionaryValue* url_dict =
-      pref_service_->GetDictionary(kDefaultSearchProviderDataPrefName);
+  const base::DictionaryValue* url_dict = &base::Value::AsDictionaryValue(
+      *pref_service_->GetDictionary(kDefaultSearchProviderDataPrefName));
   if (url_dict->DictEmpty())
     return;
 
diff --git a/components/search_engines/template_url_prepopulate_data.cc b/components/search_engines/template_url_prepopulate_data.cc
index c9c39340..d9f8297 100644
--- a/components/search_engines/template_url_prepopulate_data.cc
+++ b/components/search_engines/template_url_prepopulate_data.cc
@@ -1340,7 +1340,7 @@
   if (!prefs)
     return t_urls;
 
-  const base::ListValue* list = prefs->GetList(prefs::kSearchProviderOverrides);
+  const base::Value* list = prefs->GetList(prefs::kSearchProviderOverrides);
   if (!list)
     return t_urls;
 
diff --git a/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc b/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc
index 82ae6331..877a9e3 100644
--- a/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc
+++ b/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc
@@ -41,7 +41,7 @@
 absl::optional<SelectedSegment>
 SegmentationResultPrefs::ReadSegmentationResultFromPref(
     const std::string& result_key) {
-  const base::DictionaryValue* dictionary =
+  const base::Value* dictionary =
       prefs_->GetDictionary(kSegmentationResultPref);
   DCHECK(dictionary);
 
diff --git a/components/signin/internal/identity_manager/account_tracker_service.cc b/components/signin/internal/identity_manager/account_tracker_service.cc
index 56c45d3..8192c06 100644
--- a/components/signin/internal/identity_manager/account_tracker_service.cc
+++ b/components/signin/internal/identity_manager/account_tracker_service.cc
@@ -551,7 +551,7 @@
 }
 
 void AccountTrackerService::LoadFromPrefs() {
-  const base::ListValue* list = pref_service_->GetList(prefs::kAccountInfo);
+  const base::Value* list = pref_service_->GetList(prefs::kAccountInfo);
   std::set<CoreAccountId> to_remove;
   for (size_t i = 0; i < list->GetList().size(); ++i) {
     const base::Value& dict_value = list->GetList()[i];
diff --git a/components/strings/components_strings_en-GB.xtb b/components/strings/components_strings_en-GB.xtb
index 899dbd6..4496e1e 100644
--- a/components/strings/components_strings_en-GB.xtb
+++ b/components/strings/components_strings_en-GB.xtb
@@ -112,6 +112,7 @@
 <translation id="1308113895091915999">Offer available</translation>
 <translation id="1312803275555673949">What evidence supports it?</translation>
 <translation id="131405271941274527"><ph name="URL" /> wants to send and receive info when you tap your phone on an NFC device</translation>
+<translation id="1314311879718644478">View augmented reality content</translation>
 <translation id="1314509827145471431">Bind right</translation>
 <translation id="1318023360584041678">Saved in tab group</translation>
 <translation id="1319245136674974084">Don’t ask again for this app</translation>
@@ -215,6 +216,7 @@
 <translation id="1583429793053364125">Something went wrong while displaying this web page.</translation>
 <translation id="1586541204584340881">Which extensions you have installed</translation>
 <translation id="1588438908519853928">Normal</translation>
+<translation id="1589050138437146318">Install ARCore?</translation>
 <translation id="1592005682883173041">Local Data Access</translation>
 <translation id="1594030484168838125">Choose</translation>
 <translation id="160851722280695521">Play the Dino Run game in Chrome</translation>
@@ -1391,6 +1393,7 @@
 <translation id="5333022057423422993">Chrome found the password that you just used in a data breach. To secure your accounts, we recommend checking your saved passwords.</translation>
 <translation id="5334013548165032829">Detailed system logs</translation>
 <translation id="5334145288572353250">Save address?</translation>
+<translation id="5335920952954443287">Beating heart</translation>
 <translation id="5340250774223869109">Application is blocked</translation>
 <translation id="534295439873310000">NFC devices</translation>
 <translation id="5344579389779391559">This page may try to charge you money</translation>
@@ -2063,6 +2066,7 @@
 <translation id="7654909834015434372">When you edit annotations, this document will return to its original rotation</translation>
 <translation id="765676359832457558">Hide advanced settings ...</translation>
 <translation id="7658239707568436148">Cancel</translation>
+<translation id="7659878911471462949">Tears of joy</translation>
 <translation id="7662298039739062396">Setting controlled by an extension</translation>
 <translation id="7663736086183791259">Certificate <ph name="CERTIFICATE_VALIDITY" /></translation>
 <translation id="7666397036351755929">Not allowed in Incognito</translation>
@@ -2266,6 +2270,7 @@
 <translation id="830498451218851433">Fold half</translation>
 <translation id="8307358339886459768">Small-Photo</translation>
 <translation id="8307888238279532626">Apps installed and how often they are used</translation>
+<translation id="8317207217658302555">Update ARCore?</translation>
 <translation id="831997045666694187">Evening</translation>
 <translation id="8321476692217554900">notifications</translation>
 <translation id="8328484624016508118">After closing all Incognito tabs, Chrome clears:
diff --git a/components/strings/components_strings_hy.xtb b/components/strings/components_strings_hy.xtb
index be4d40f..b8222be 100644
--- a/components/strings/components_strings_hy.xtb
+++ b/components/strings/components_strings_hy.xtb
@@ -1260,7 +1260,8 @@
 <translation id="4955242332710481440">A5-Extra</translation>
 <translation id="4958444002117714549">Ընդարձակել ցանկը</translation>
 <translation id="4968522289500246572">Այս հավելվածը նախատեսված է բջջային սարքերի համար։ Չափը փոխելուց հետո հավելվածը կարող է սխալներով աշխատել։</translation>
-<translation id="4969341057194253438">Ջնջել տեսաագրությունը</translation>
+<translation id="4969341057194253438">Ջնջել տեսագրությունը
+</translation>
 <translation id="4973922308112707173">Երկու անցք վերևում</translation>
 <translation id="4976702386844183910">Վերջին այցելությունը՝ <ph name="DATE" /></translation>
 <translation id="4984088539114770594">Օգտագործե՞լ խոսափողը</translation>
diff --git a/components/strings/components_strings_lo.xtb b/components/strings/components_strings_lo.xtb
index 2897d61e..1638f5b 100644
--- a/components/strings/components_strings_lo.xtb
+++ b/components/strings/components_strings_lo.xtb
@@ -112,6 +112,7 @@
 <translation id="1308113895091915999">ມີຂໍ້ສະເໜີ</translation>
 <translation id="1312803275555673949">ມີຫຼັກຖານໃດສະໜັບສະໜຸນມັນ?</translation>
 <translation id="131405271941274527"><ph name="URL" /> ຕ້ອງການສົ່ງ ແລະ ຮັບຂໍ້ມູນເມື່ອທ່ານແຕະໂທລະສັບຂອງທ່ານໃນອຸປະກອນ NFC</translation>
+<translation id="1314311879718644478">ເບິ່ງເນື້ອຫາອາກິວເມັນ ຣີອາລິຕີ</translation>
 <translation id="1314509827145471431">ຫຍິບເຫຼັ້ມເບື້ອງຂວາ</translation>
 <translation id="1318023360584041678">ບັນທຶກໃນກຸ່ມແຖບແລ້ວ</translation>
 <translation id="1319245136674974084">ບໍ່ຕ້ອງຖາມອີກສຳລັບແອັບນີ້</translation>
@@ -215,6 +216,7 @@
 <translation id="1583429793053364125">ມີບາງອັນຜິດພາດ ໃນຂະນະທີ່ກໍາລັງສະແດງໜ້າ​ເວັບນີ້.</translation>
 <translation id="1586541204584340881">ສ່ວນຂະຫຍາຍທີ່ທ່ານຕິດຕັ້ງ</translation>
 <translation id="1588438908519853928">ປົກ​ກະ​ຕິ</translation>
+<translation id="1589050138437146318">ຕິດຕັ້ງ ARCore ບໍ?</translation>
 <translation id="1592005682883173041">ການເຂົ້າເຖິງຂໍ້ມູນພາຍໃນເຄື່ອງ</translation>
 <translation id="1594030484168838125">ເລືອກ</translation>
 <translation id="160851722280695521">ຫຼິ້ນເກມ Dino Run ໃນ Chrome</translation>
@@ -1392,6 +1394,7 @@
 <translation id="5333022057423422993">Chrome ພົບລະຫັດຜ່ານທີ່ທ່ານຫາກໍໃຊ້ໄປນັ້ນໃນການຮົ່ວໄຫຼຂໍ້ມູນ. ເພື່ອຮັກສາຄວາມປອດໄພບັນຊີຂອງທ່ານ, ພວກເຮົາແນະນຳໃຫ້ທ່ານກວດສອບລະຫັດຜ່ານທີ່ທ່ານບັນທຶກໄວ້.</translation>
 <translation id="5334013548165032829">ບັນທຶກລະບົບລະອຽດ</translation>
 <translation id="5334145288572353250">ບັນທຶກທີ່ຢູ່ບໍ?</translation>
+<translation id="5335920952954443287">ຫົວໃຈເຕັ້ນ</translation>
 <translation id="5340250774223869109">ແອັບພລິເຄຊັນຖືກບລັອກໄວ້</translation>
 <translation id="534295439873310000">ອຸປະກອນ NFC</translation>
 <translation id="5344579389779391559">ໜ້ານີ້ອາດຈະພະຍາຍາມຮຽກເກັບເງິນນຳທ່ານ</translation>
@@ -2064,6 +2067,7 @@
 <translation id="7654909834015434372">ເມື່ອທ່ານແກ້ໄຂຄຳອະທິບາຍຄວາມເຫັນ, ເອກະສານນີ້ຈະກັບໄປໃຊ້ການໝຸນແບບເດີມຂອງມັນ</translation>
 <translation id="765676359832457558">ເຊື່ອງ​ການ​ຕັ້ງຄ່າຂັ້ນສູງ ...</translation>
 <translation id="7658239707568436148">ຍົກ​ເລີກ​</translation>
+<translation id="7659878911471462949">ນ້ຳຕາແຫ່ງຄວາມສຸກ</translation>
 <translation id="7662298039739062396">ການຕັ້ງຄ່າຖືກຄວບຄຸມໂດຍສ່ວນຂະຫຍາຍ</translation>
 <translation id="7663736086183791259">ໃບຮັບຮອງ <ph name="CERTIFICATE_VALIDITY" /></translation>
 <translation id="7666397036351755929">ບໍ່ອະນຸຍາດໃຫ້ໃຊ້ໃນໂໝດບໍ່ເປີດເຜີຍຕົວຕົນ</translation>
@@ -2267,6 +2271,7 @@
 <translation id="830498451218851433">ພັບເຄິ່ງ</translation>
 <translation id="8307358339886459768">Small-Photo</translation>
 <translation id="8307888238279532626">ແອັບທີ່ຕິດຕັ້ງ ແລະ ມີການນຳໃຊ້ພວກມັນເລື້ອຍໆສໍ່າໃດ</translation>
+<translation id="8317207217658302555">ອັບເດດ ARCore ບໍ?</translation>
 <translation id="831997045666694187">ຕອນແລງ</translation>
 <translation id="8321476692217554900">ການແຈ້ງເຕືອນ</translation>
 <translation id="8328484624016508118">ຫຼັງຈາກການປິດແຖບ​ບໍ່ເປີດເຜີຍຕົວຕົນທັງໝົດແລ້ວ, Chrome ຈະລຶບລ້າງ:
diff --git a/components/strings/components_strings_nl.xtb b/components/strings/components_strings_nl.xtb
index 23c134f..d6dbe47 100644
--- a/components/strings/components_strings_nl.xtb
+++ b/components/strings/components_strings_nl.xtb
@@ -1097,7 +1097,7 @@
 <translation id="4460315069258617173">Toegestaan totdat je tabbladen voor deze site sluit</translation>
 <translation id="4464826014807964867">Websites met gegevens van je organisatie</translation>
 <translation id="447665707681730621"><ph name="BUBBLE_MESSAGE" />. <ph name="LEARN_MORE_TEXT" />.</translation>
-<translation id="4476953670630786061">Dit formulier is niet beveiligd. Automatisch invullen staat uit.</translation>
+<translation id="4476953670630786061">Dit formulier is niet beveiligd. Automatisch invullen is uitgezet.</translation>
 <translation id="4477350412780666475">Volgend nummer</translation>
 <translation id="4481251927743463293">Wat incognito doet</translation>
 <translation id="4482953324121162758">Deze site wordt niet vertaald.</translation>
diff --git a/components/strings/components_strings_uz.xtb b/components/strings/components_strings_uz.xtb
index 9f593de..d78c829 100644
--- a/components/strings/components_strings_uz.xtb
+++ b/components/strings/components_strings_uz.xtb
@@ -112,6 +112,7 @@
 <translation id="1308113895091915999">Mavjud takliflar</translation>
 <translation id="1312803275555673949">Buning qaysi isboti bor ekan?</translation>
 <translation id="131405271941274527"><ph name="URL" /> sayti NFC orqali axborot almashinishga ruxsat soʻramoqda</translation>
+<translation id="1314311879718644478">Boyitilgan reallik kontentini ochish</translation>
 <translation id="1314509827145471431">Oʻng chekkasini belgilash</translation>
 <translation id="1318023360584041678">Varaqlar guruhiga saqlangan</translation>
 <translation id="1319245136674974084">Bu ilova uchun boshqa soʻralmasin</translation>
@@ -214,6 +215,7 @@
 <translation id="1583429793053364125">Veb-sahifani ochish vaqtida kutilmagan xatolik yuz berdi.</translation>
 <translation id="1586541204584340881">Oʻrnatilgan kengaytmalar</translation>
 <translation id="1588438908519853928">Normal</translation>
+<translation id="1589050138437146318">ARCore oʻrnatilsinmi?</translation>
 <translation id="1592005682883173041">Qurilmadagi mahalliy ma’lumotlarga ruxsat</translation>
 <translation id="1594030484168838125">Tanlang</translation>
 <translation id="160851722280695521">Chromeda Dino oʻyinini boshlash</translation>
@@ -1387,6 +1389,7 @@
 <translation id="5333022057423422993">Chrome hozirgina oshkor qilingan parol ishlatganini aniqladi. Hisoblaringiz himoyasi uchun saqlangan parollaringizni tekshiring.</translation>
 <translation id="5334013548165032829">Batafsil tizim jurnali qaydlari</translation>
 <translation id="5334145288572353250">Manzil saqlansinmi?</translation>
+<translation id="5335920952954443287">Urayotgan yurak</translation>
 <translation id="5340250774223869109">Ilova bloklandi</translation>
 <translation id="534295439873310000">NFC qurilmalar</translation>
 <translation id="5344579389779391559">Bu sahifa sizdan pul talab qilishi mumkin</translation>
@@ -2058,6 +2061,7 @@
 <translation id="7654909834015434372">Izohlarni tahrirlash vaqtida bu hujjat asl holatiga qaytadi</translation>
 <translation id="765676359832457558">Qo‘shimcha sozlamalarni yashirish</translation>
 <translation id="7658239707568436148">Bekor qilish</translation>
+<translation id="7659878911471462949">Quvonch koʻz yoshlari</translation>
 <translation id="7662298039739062396">Sozlama kengaytma tomonidan boshqariladi</translation>
 <translation id="7663736086183791259"><ph name="CERTIFICATE_VALIDITY" /> sertifikati</translation>
 <translation id="7666397036351755929">Inkognito rejimida ruxsat berilmagan</translation>
@@ -2261,6 +2265,7 @@
 <translation id="830498451218851433">Yarim taxlash</translation>
 <translation id="8307358339886459768">Small-Photo</translation>
 <translation id="8307888238279532626">Oʻrnatilgan ilovalar va ularning qanday ishlatilishi</translation>
+<translation id="8317207217658302555">ARCore yangilansinmi?</translation>
 <translation id="831997045666694187">Kechqurun</translation>
 <translation id="8321476692217554900">bildirishnomalar</translation>
 <translation id="8328484624016508118">Barcha Inkognito varaqlar yopilganidan keyin Chrome quyidagilarni tozalaydi:
diff --git a/components/sync/driver/glue/sync_transport_data_prefs.cc b/components/sync/driver/glue/sync_transport_data_prefs.cc
index f2d105b..5e63433c 100644
--- a/components/sync/driver/glue/sync_transport_data_prefs.cc
+++ b/components/sync/driver/glue/sync_transport_data_prefs.cc
@@ -176,7 +176,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::map<ModelType, int64_t> invalidation_versions;
   const base::DictionaryValue* invalidation_dictionary =
-      pref_service_->GetDictionary(kSyncInvalidationVersions);
+      &base::Value::AsDictionaryValue(
+          *pref_service_->GetDictionary(kSyncInvalidationVersions));
   for (ModelType type : ProtocolTypes()) {
     std::string key = ModelTypeToString(type);
     std::string version_str;
diff --git a/components/sync/engine/BUILD.gn b/components/sync/engine/BUILD.gn
index c0c198b..3c468be 100644
--- a/components/sync/engine/BUILD.gn
+++ b/components/sync/engine/BUILD.gn
@@ -107,8 +107,8 @@
     "model_type_worker.h",
     "net/http_bridge.cc",
     "net/http_bridge.h",
+    "net/http_post_provider.h",
     "net/http_post_provider_factory.h",
-    "net/http_post_provider_interface.h",
     "net/server_connection_manager.cc",
     "net/server_connection_manager.h",
     "net/sync_server_connection_manager.cc",
diff --git a/components/sync/engine/net/http_bridge.cc b/components/sync/engine/net/http_bridge.cc
index 8cbe5ec..12ed79f 100644
--- a/components/sync/engine/net/http_bridge.cc
+++ b/components/sync/engine/net/http_bridge.cc
@@ -63,10 +63,10 @@
 
 HttpBridgeFactory::~HttpBridgeFactory() = default;
 
-scoped_refptr<HttpPostProviderInterface> HttpBridgeFactory::Create() {
+scoped_refptr<HttpPostProvider> HttpBridgeFactory::Create() {
   DCHECK(url_loader_factory_);
 
-  scoped_refptr<HttpPostProviderInterface> http =
+  scoped_refptr<HttpPostProvider> http =
       new HttpBridge(user_agent_, url_loader_factory_->Clone());
   return http;
 }
diff --git a/components/sync/engine/net/http_bridge.h b/components/sync/engine/net/http_bridge.h
index 1714b01f..16f20858 100644
--- a/components/sync/engine/net/http_bridge.h
+++ b/components/sync/engine/net/http_bridge.h
@@ -17,8 +17,8 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread_checker.h"
 #include "base/timer/timer.h"
+#include "components/sync/engine/net/http_post_provider.h"
 #include "components/sync/engine/net/http_post_provider_factory.h"
-#include "components/sync/engine/net/http_post_provider_interface.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "url/gurl.h"
 
@@ -38,7 +38,7 @@
 // Provides a way for the sync backend to use Chromium directly for HTTP
 // requests rather than depending on a third party provider (e.g libcurl).
 // This is a one-time use bridge. Create one for each request you want to make.
-class HttpBridge : public HttpPostProviderInterface {
+class HttpBridge : public HttpPostProvider {
  public:
   HttpBridge(const std::string& user_agent,
              std::unique_ptr<network::PendingSharedURLLoaderFactory>
@@ -47,7 +47,7 @@
   HttpBridge(const HttpBridge&) = delete;
   HttpBridge& operator=(const HttpBridge&) = delete;
 
-  // HttpPostProviderInterface implementation.
+  // HttpPostProvider implementation.
   void SetExtraRequestHeaders(const char* headers) override;
   void SetURL(const GURL& url) override;
   void SetPostPayload(const char* content_type,
@@ -196,7 +196,7 @@
   ~HttpBridgeFactory() override;
 
   // HttpPostProviderFactory:
-  scoped_refptr<HttpPostProviderInterface> Create() override;
+  scoped_refptr<HttpPostProvider> Create() override;
 
  private:
   // The user agent to use in all requests.
diff --git a/components/sync/engine/net/http_post_provider_interface.h b/components/sync/engine/net/http_post_provider.h
similarity index 85%
rename from components/sync/engine/net/http_post_provider_interface.h
rename to components/sync/engine/net/http_post_provider.h
index 4da9cb5..55314de 100644
--- a/components/sync/engine/net/http_post_provider_interface.h
+++ b/components/sync/engine/net/http_post_provider.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_SYNC_ENGINE_NET_HTTP_POST_PROVIDER_INTERFACE_H_
-#define COMPONENTS_SYNC_ENGINE_NET_HTTP_POST_PROVIDER_INTERFACE_H_
+#ifndef COMPONENTS_SYNC_ENGINE_NET_HTTP_POST_PROVIDER_H_
+#define COMPONENTS_SYNC_ENGINE_NET_HTTP_POST_PROVIDER_H_
 
 #include <string>
 
@@ -19,8 +19,7 @@
 // It is RefCountedThreadSafe because its implementations may PostTask to
 // background task runners, and thus need to stick around across context
 // switches, etc.
-class HttpPostProviderInterface
-    : public base::RefCountedThreadSafe<HttpPostProviderInterface> {
+class HttpPostProvider : public base::RefCountedThreadSafe<HttpPostProvider> {
  public:
   // Add additional headers to the request.
   virtual void SetExtraRequestHeaders(const char* headers) = 0;
@@ -62,10 +61,10 @@
   virtual void Abort() = 0;
 
  protected:
-  friend class base::RefCountedThreadSafe<HttpPostProviderInterface>;
-  virtual ~HttpPostProviderInterface() = default;
+  friend class base::RefCountedThreadSafe<HttpPostProvider>;
+  virtual ~HttpPostProvider() = default;
 };
 
 }  // namespace syncer
 
-#endif  // COMPONENTS_SYNC_ENGINE_NET_HTTP_POST_PROVIDER_INTERFACE_H_
+#endif  // COMPONENTS_SYNC_ENGINE_NET_HTTP_POST_PROVIDER_H_
diff --git a/components/sync/engine/net/http_post_provider_factory.h b/components/sync/engine/net/http_post_provider_factory.h
index c66599a..cb49dd7 100644
--- a/components/sync/engine/net/http_post_provider_factory.h
+++ b/components/sync/engine/net/http_post_provider_factory.h
@@ -17,7 +17,7 @@
 
 namespace syncer {
 
-class HttpPostProviderInterface;
+class HttpPostProvider;
 
 // A factory to create HttpPostProviders to hide details about the
 // implementations and dependencies.
@@ -27,8 +27,8 @@
  public:
   virtual ~HttpPostProviderFactory() = default;
 
-  // Obtain a new HttpPostProviderInterface instance, owned by caller.
-  virtual scoped_refptr<HttpPostProviderInterface> Create() = 0;
+  // Obtain a new HttpPostProvider instance, owned by caller.
+  virtual scoped_refptr<HttpPostProvider> Create() = 0;
 };
 
 using CreateHttpPostProviderFactory =
diff --git a/components/sync/engine/net/sync_server_connection_manager.cc b/components/sync/engine/net/sync_server_connection_manager.cc
index eb74f04..bb3350b3 100644
--- a/components/sync/engine/net/sync_server_connection_manager.cc
+++ b/components/sync/engine/net/sync_server_connection_manager.cc
@@ -12,8 +12,8 @@
 #include "base/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
 #include "components/sync/engine/cancelation_signal.h"
+#include "components/sync/engine/net/http_post_provider.h"
 #include "components/sync/engine/net/http_post_provider_factory.h"
-#include "components/sync/engine/net/http_post_provider_interface.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_status_code.h"
 
@@ -53,7 +53,7 @@
   // operation should be aborted.
   const raw_ptr<CancelationSignal> cancelation_signal_;
 
-  scoped_refptr<HttpPostProviderInterface> const post_provider_;
+  scoped_refptr<HttpPostProvider> const post_provider_;
 
   std::string buffer_;
 };
diff --git a/components/sync/engine/net/sync_server_connection_manager_unittest.cc b/components/sync/engine/net/sync_server_connection_manager_unittest.cc
index 9ab6188..12948f1 100644
--- a/components/sync/engine/net/sync_server_connection_manager_unittest.cc
+++ b/components/sync/engine/net/sync_server_connection_manager_unittest.cc
@@ -12,16 +12,15 @@
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "components/sync/engine/cancelation_signal.h"
+#include "components/sync/engine/net/http_post_provider.h"
 #include "components/sync/engine/net/http_post_provider_factory.h"
-#include "components/sync/engine/net/http_post_provider_interface.h"
 #include "net/base/net_errors.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace syncer {
 namespace {
 
-
-class BlockingHttpPost : public HttpPostProviderInterface {
+class BlockingHttpPost : public HttpPostProvider {
  public:
   BlockingHttpPost()
       : wait_for_abort_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
@@ -56,7 +55,7 @@
  public:
   ~BlockingHttpPostFactory() override = default;
 
-  scoped_refptr<HttpPostProviderInterface> Create() override {
+  scoped_refptr<HttpPostProvider> Create() override {
     return new BlockingHttpPost();
   }
 };
@@ -114,7 +113,7 @@
 
 namespace {
 
-class FailingHttpPost : public HttpPostProviderInterface {
+class FailingHttpPost : public HttpPostProvider {
  public:
   explicit FailingHttpPost(int net_error_code)
       : net_error_code_(net_error_code) {}
@@ -149,7 +148,7 @@
       : net_error_code_(net_error_code) {}
   ~FailingHttpPostFactory() override = default;
 
-  scoped_refptr<HttpPostProviderInterface> Create() override {
+  scoped_refptr<HttpPostProvider> Create() override {
     return new FailingHttpPost(net_error_code_);
   }
 
diff --git a/components/sync/engine/sync_manager_impl_unittest.cc b/components/sync/engine/sync_manager_impl_unittest.cc
index d7d2513..2f732ba 100644
--- a/components/sync/engine/sync_manager_impl_unittest.cc
+++ b/components/sync/engine/sync_manager_impl_unittest.cc
@@ -29,8 +29,8 @@
 #include "components/sync/engine/cancelation_signal.h"
 #include "components/sync/engine/cycle/sync_cycle.h"
 #include "components/sync/engine/events/protocol_event.h"
+#include "components/sync/engine/net/http_post_provider.h"
 #include "components/sync/engine/net/http_post_provider_factory.h"
-#include "components/sync/engine/net/http_post_provider_interface.h"
 #include "components/sync/engine/nigori/key_derivation_params.h"
 #include "components/sync/engine/polling_constants.h"
 #include "components/sync/engine/sync_scheduler.h"
@@ -54,7 +54,7 @@
 
 namespace {
 
-class TestHttpPostProviderInterface : public HttpPostProviderInterface {
+class TestHttpPostProvider : public HttpPostProvider {
  public:
   void SetExtraRequestHeaders(const char* headers) override {}
   void SetURL(const GURL& url) override {}
@@ -74,14 +74,14 @@
   void Abort() override {}
 
  private:
-  ~TestHttpPostProviderInterface() override = default;
+  ~TestHttpPostProvider() override = default;
 };
 
 class TestHttpPostProviderFactory : public HttpPostProviderFactory {
  public:
   ~TestHttpPostProviderFactory() override = default;
-  scoped_refptr<HttpPostProviderInterface> Create() override {
-    return new TestHttpPostProviderInterface();
+  scoped_refptr<HttpPostProvider> Create() override {
+    return new TestHttpPostProvider();
   }
 };
 
diff --git a/components/sync/test/fake_server/fake_server_http_post_provider.cc b/components/sync/test/fake_server/fake_server_http_post_provider.cc
index 3cd4b07..a121f3c 100644
--- a/components/sync/test/fake_server/fake_server_http_post_provider.cc
+++ b/components/sync/test/fake_server/fake_server_http_post_provider.cc
@@ -27,7 +27,7 @@
 FakeServerHttpPostProviderFactory::~FakeServerHttpPostProviderFactory() =
     default;
 
-scoped_refptr<syncer::HttpPostProviderInterface>
+scoped_refptr<syncer::HttpPostProvider>
 FakeServerHttpPostProviderFactory::Create() {
   return new FakeServerHttpPostProvider(fake_server_, fake_server_task_runner_);
 }
diff --git a/components/sync/test/fake_server/fake_server_http_post_provider.h b/components/sync/test/fake_server/fake_server_http_post_provider.h
index 734c3475..feff275 100644
--- a/components/sync/test/fake_server/fake_server_http_post_provider.h
+++ b/components/sync/test/fake_server/fake_server_http_post_provider.h
@@ -14,14 +14,14 @@
 #include "base/sequence_checker.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/sequenced_task_runner.h"
+#include "components/sync/engine/net/http_post_provider.h"
 #include "components/sync/engine/net/http_post_provider_factory.h"
-#include "components/sync/engine/net/http_post_provider_interface.h"
 
 namespace fake_server {
 
 class FakeServer;
 
-class FakeServerHttpPostProvider : public syncer::HttpPostProviderInterface {
+class FakeServerHttpPostProvider : public syncer::HttpPostProvider {
  public:
   FakeServerHttpPostProvider(
       const base::WeakPtr<FakeServer>& fake_server,
@@ -31,7 +31,7 @@
   FakeServerHttpPostProvider& operator=(const FakeServerHttpPostProvider&) =
       delete;
 
-  // HttpPostProviderInterface implementation.
+  // HttpPostProvider implementation.
   void SetExtraRequestHeaders(const char* headers) override;
   void SetURL(const GURL& url) override;
   void SetPostPayload(const char* content_type,
@@ -94,7 +94,7 @@
   ~FakeServerHttpPostProviderFactory() override;
 
   // HttpPostProviderFactory:
-  scoped_refptr<syncer::HttpPostProviderInterface> Create() override;
+  scoped_refptr<syncer::HttpPostProvider> Create() override;
 
  private:
   // |fake_server_| should only be dereferenced on the same thread as
diff --git a/components/sync_bookmarks/bookmark_model_merger.cc b/components/sync_bookmarks/bookmark_model_merger.cc
index afa4de7c..1f505865 100644
--- a/components/sync_bookmarks/bookmark_model_merger.cc
+++ b/components/sync_bookmarks/bookmark_model_merger.cc
@@ -60,8 +60,10 @@
 
 // The value must be a list since there is a container using pointers to its
 // elements.
-using UpdatesPerParentId =
-    std::unordered_map<std::string, std::list<syncer::UpdateResponseData>>;
+using UpdatesPerParentGUID =
+    std::unordered_map<base::GUID,
+                       std::list<syncer::UpdateResponseData>,
+                       base::GUIDHash>;
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused. When adding values, be certain to also
@@ -105,9 +107,9 @@
   kUnsupportedPermanentFolder = 13,
   // A bookmark that is not contained in any permanent folder and is instead
   // hanging (directly or indirectly) from the root node.
-  kDescendantOfRootNodeWithoutPermanentFolder = 14,
+  // kDeprecatedDescendantOfRootNodeWithoutPermanentFolder = 14,
 
-  kMaxValue = kDescendantOfRootNodeWithoutPermanentFolder,
+  kMaxValue = kUnsupportedPermanentFolder,
 };
 
 void LogProblematicBookmark(RemoteBookmarkUpdateError problem) {
@@ -121,12 +123,16 @@
 }
 
 // Gets the bookmark node corresponding to a permanent folder identified by
-// |server_defined_unique_tag|. |bookmark_model| must not be null.
-const bookmarks::BookmarkNode* GetPermanentFolder(
+// |server_defined_unique_tag| or null of the tag is unknown. |bookmark_model|
+// must not be null and |server_defined_unique_tag| must not be empty.
+const bookmarks::BookmarkNode* GetPermanentFolderForServerDefinedUniqueTag(
     const bookmarks::BookmarkModel* bookmark_model,
     const std::string& server_defined_unique_tag) {
   DCHECK(bookmark_model);
+  DCHECK(!server_defined_unique_tag.empty());
 
+  // WARNING: Keep this logic consistent with the analogous in
+  // GetPermanentFolderGUIDForServerDefinedUniqueTag().
   if (server_defined_unique_tag == kBookmarkBarTag) {
     return bookmark_model->bookmark_bar_node();
   }
@@ -140,6 +146,31 @@
   return nullptr;
 }
 
+// Gets the bookmark GUID corresponding to a permanent folder identified by
+// |served_defined_unique_tag| or an invalid GUID if the tag is unknown.
+// |server_defined_unique_tag| must not be empty.
+base::GUID GetPermanentFolderGUIDForServerDefinedUniqueTag(
+    const std::string& server_defined_unique_tag) {
+  DCHECK(!server_defined_unique_tag.empty());
+
+  // WARNING: Keep this logic consistent with the analogous in
+  // GetPermanentFolderForServerDefinedUniqueTag().
+  if (server_defined_unique_tag == kBookmarkBarTag) {
+    return base::GUID::ParseLowercase(
+        bookmarks::BookmarkNode::kBookmarkBarNodeGuid);
+  }
+  if (server_defined_unique_tag == kOtherBookmarksTag) {
+    return base::GUID::ParseLowercase(
+        bookmarks::BookmarkNode::kOtherBookmarksNodeGuid);
+  }
+  if (server_defined_unique_tag == kMobileBookmarksTag) {
+    return base::GUID::ParseLowercase(
+        bookmarks::BookmarkNode::kMobileBookmarksNodeGuid);
+  }
+
+  return base::GUID();
+}
+
 std::string LegacyCanonicalizedTitleFromSpecifics(
     const sync_pb::BookmarkSpecifics& specifics) {
   if (specifics.has_full_title()) {
@@ -215,31 +246,6 @@
   return BookmarksGUIDDuplicates();
 }
 
-void ReparentAllChildren(const std::string& from_parent_id,
-                         const std::string& to_parent_id,
-                         UpdatesPerParentId* updates_per_parent_id) {
-  // Any of parents may be empty.
-  auto from_parent_updates_iter = updates_per_parent_id->find(from_parent_id);
-  if (from_parent_updates_iter == updates_per_parent_id->end()) {
-    // There is nothing to merge.
-    return;
-  }
-
-  // Update parent ids for all entities before moving.
-  for (auto& update : from_parent_updates_iter->second) {
-    DCHECK_EQ(update.entity.parent_id, from_parent_id);
-    update.entity.parent_id = to_parent_id;
-  }
-
-  // Move all elements to a new parent (create one if it didn't exist).
-  (*updates_per_parent_id)[to_parent_id].splice(
-      (*updates_per_parent_id)[to_parent_id].end(),
-      from_parent_updates_iter->second);
-  updates_per_parent_id->erase(from_parent_id);
-
-  // No need to update iterators since splice doesn't invalidate them.
-}
-
 // Returns true the |next_update| is selected to keep and the |previous_update|
 // should be removed. False is returned otherwise. |next_update| and
 // |previous_update| must have the same GUID.
@@ -261,20 +267,19 @@
          previous_update.entity.creation_time;
 }
 
-void DeduplicateValidUpdatesByGUID(UpdatesPerParentId* updates_per_parent_id) {
-  DCHECK(updates_per_parent_id);
+void DeduplicateValidUpdatesByGUID(
+    UpdatesPerParentGUID* updates_per_parent_guid) {
+  DCHECK(updates_per_parent_guid);
 
   std::unordered_map<base::GUID, std::list<UpdateResponseData>::iterator,
                      base::GUIDHash>
       guid_to_update;
 
-  // Removing data in a separate loop helps easier merge parents since one of
-  // them may have already been processed.
-  std::list<std::list<UpdateResponseData>::iterator> updates_to_remove;
-  for (auto& parent_id_and_updates : *updates_per_parent_id) {
-    std::list<UpdateResponseData>* updates = &parent_id_and_updates.second;
-    for (auto updates_iter = updates->begin(); updates_iter != updates->end();
-         ++updates_iter) {
+  for (auto& parent_guid_and_updates : *updates_per_parent_guid) {
+    std::list<UpdateResponseData>* updates = &parent_guid_and_updates.second;
+
+    auto updates_iter = updates->begin();
+    while (updates_iter != updates->end()) {
       const UpdateResponseData& update = *updates_iter;
       DCHECK(!update.entity.is_deleted());
       DCHECK(update.entity.server_defined_unique_tag.empty());
@@ -286,8 +291,10 @@
       auto it_and_success =
           guid_to_update.emplace(guid_in_specifics, updates_iter);
       if (it_and_success.second) {
+        ++updates_iter;
         continue;
       }
+
       const UpdateResponseData& duplicate_update =
           *it_and_success.first->second;
       DCHECK_EQ(guid_in_specifics.AsLowercaseString(),
@@ -301,39 +308,14 @@
 
       if (CompareDuplicateUpdates(/*next_update=*/update,
                                   /*previous_update=*/duplicate_update)) {
-        updates_to_remove.push_back(it_and_success.first->second);
-        // Update |guid_to_update| to find a duplicate folder and merge them.
+        updates->erase(it_and_success.first->second);
         guid_to_update[guid_in_specifics] = updates_iter;
+        ++updates_iter;
       } else {
-        updates_to_remove.push_back(updates_iter);
+        updates_iter = updates->erase(updates_iter);
       }
     }
   }
-
-  for (std::list<UpdateResponseData>::iterator updates_iter :
-       updates_to_remove) {
-    if (updates_iter->entity.specifics.bookmark().type() ==
-        sync_pb::BookmarkSpecifics::FOLDER) {
-      const base::GUID guid = base::GUID::ParseLowercase(
-          updates_iter->entity.specifics.bookmark().guid());
-      DCHECK(base::Contains(guid_to_update, guid));
-      DCHECK(guid_to_update[guid] != updates_iter);
-
-      // Never remove a folder if its duplicate is a URL.
-      DCHECK_EQ(guid_to_update[guid]->entity.specifics.bookmark().type(),
-                sync_pb::BookmarkSpecifics::FOLDER);
-
-      // Merge doesn't affect iterators.
-      ReparentAllChildren(
-          /*from_parent_id=*/updates_iter->entity.id,
-          /*to_parent_id=*/guid_to_update[guid]->entity.id,
-          updates_per_parent_id);
-    }
-
-    const std::string& parent_id = updates_iter->entity.parent_id;
-    DCHECK(base::Contains(*updates_per_parent_id, parent_id));
-    (*updates_per_parent_id)[parent_id].erase(updates_iter);
-  }
 }
 
 // Checks that the |update| is valid and returns false otherwise. It is used to
@@ -363,16 +345,28 @@
   return true;
 }
 
+// Returns the GUID determined by a remote update, which may be an update for a
+// permanent folder or a regular bookmark node.
+base::GUID GetGUIDForUpdate(const UpdateResponseData& update) {
+  if (!update.entity.server_defined_unique_tag.empty()) {
+    return GetPermanentFolderGUIDForServerDefinedUniqueTag(
+        update.entity.server_defined_unique_tag);
+  }
+
+  DCHECK(IsValidUpdate(update));
+  return base::GUID::ParseLowercase(update.entity.specifics.bookmark().guid());
+}
+
 struct GroupedUpdates {
-  // |updates_per_parent_id| contains all valid updates grouped by their
-  // |parent_id|. Permanent nodes and deletions are filtered out. Permanent
+  // |updates_per_parent_guid| contains all valid updates grouped by their
+  // |parent_guid|. Permanent nodes and deletions are filtered out. Permanent
   // nodes are stored in a dedicated list |permanent_node_updates|.
-  UpdatesPerParentId updates_per_parent_id;
+  UpdatesPerParentGUID updates_per_parent_guid;
   UpdateResponseDataList permanent_node_updates;
 };
 
-// Groups all valid updates by the server ID of their parent. Permanent nodes
-// are grouped in a dedicated |permanent_node_updates| list in a returned value.
+// Groups all valid updates by the GUID of their parent. Permanent nodes are
+// grouped in a dedicated |permanent_node_updates| list in a returned value.
 GroupedUpdates GroupValidUpdates(UpdateResponseDataList updates) {
   GroupedUpdates grouped_updates;
   int num_valid_updates = 0;
@@ -381,16 +375,30 @@
     if (update_entity.is_deleted()) {
       continue;
     }
+    // Special-case the root folder to avoid recording
+    // |RemoteBookmarkUpdateError::kUnsupportedPermanentFolder|.
+    if (update_entity.server_defined_unique_tag ==
+        syncer::ModelTypeToRootTag(syncer::BOOKMARKS)) {
+      ++num_valid_updates;
+      continue;
+    }
+    // Non-root permanent folders don't need further validation.
     if (!update_entity.server_defined_unique_tag.empty()) {
       ++num_valid_updates;
       grouped_updates.permanent_node_updates.push_back(std::move(update));
       continue;
     }
+    // Regular (non-permanent) node updates must pass IsValidUpdate().
     if (!IsValidUpdate(update)) {
       continue;
     }
     ++num_valid_updates;
-    grouped_updates.updates_per_parent_id[update_entity.parent_id].push_back(
+
+    const base::GUID parent_guid = base::GUID::ParseLowercase(
+        update_entity.specifics.bookmark().parent_guid());
+    DCHECK(parent_guid.is_valid());
+
+    grouped_updates.updates_per_parent_guid[parent_guid].push_back(
         std::move(update));
   }
 
@@ -457,11 +465,14 @@
 BookmarkModelMerger::RemoteTreeNode::BuildTree(
     UpdateResponseData update,
     size_t max_depth,
-    UpdatesPerParentId* updates_per_parent_id) {
-  DCHECK(updates_per_parent_id);
+    UpdatesPerParentGUID* updates_per_parent_guid) {
+  DCHECK(updates_per_parent_guid);
   DCHECK(!update.entity.server_defined_unique_tag.empty() ||
          IsValidUpdate(update));
 
+  // |guid| may be invalid for unsupported permanent nodes.
+  const base::GUID guid = GetGUIDForUpdate(update);
+
   RemoteTreeNode node;
   node.update_ = std::move(update);
   node.unique_position_ = syncer::UniquePosition::FromProto(
@@ -473,14 +484,15 @@
     return node;
   }
 
-  // Check to prevent creating empty lists in |updates_per_parent_id| and
+  // Check to prevent creating empty lists in |updates_per_parent_guid| and
   // unnecessary rehashing.
-  auto updates_per_parent_id_iter =
-      updates_per_parent_id->find(node.entity().id);
-  if (updates_per_parent_id_iter == updates_per_parent_id->end()) {
+  auto updates_per_parent_guid_iter = updates_per_parent_guid->find(guid);
+  if (updates_per_parent_guid_iter == updates_per_parent_guid->end()) {
     return node;
   }
-  DCHECK(!updates_per_parent_id_iter->second.empty());
+
+  DCHECK(!updates_per_parent_guid_iter->second.empty());
+  DCHECK(guid.is_valid());
 
   // Only folders may have descendants (ignore them otherwise). Treat
   // permanent nodes as folders explicitly.
@@ -489,7 +501,7 @@
       node.update_.entity.server_defined_unique_tag.empty()) {
     // Children of a non-folder are ignored.
     for (UpdateResponseData& child_update :
-         updates_per_parent_id_iter->second) {
+         updates_per_parent_guid_iter->second) {
       LogProblematicBookmark(RemoteBookmarkUpdateError::kParentNotFolder);
       // To avoid double-counting later for bucket |kMissingParentEntity|,
       // clear the update from the list as if it would have been moved.
@@ -499,13 +511,15 @@
   }
 
   // Populate descendants recursively.
-  node.children_.reserve(updates_per_parent_id_iter->second.size());
-  for (UpdateResponseData& child_update : updates_per_parent_id_iter->second) {
-    DCHECK_EQ(child_update.entity.parent_id, node.entity().id);
+  node.children_.reserve(updates_per_parent_guid_iter->second.size());
+  for (UpdateResponseData& child_update :
+       updates_per_parent_guid_iter->second) {
+    DCHECK_EQ(child_update.entity.specifics.bookmark().parent_guid(),
+              guid.AsLowercaseString());
     DCHECK(IsValidBookmarkSpecifics(child_update.entity.specifics.bookmark()));
 
     node.children_.push_back(BuildTree(std::move(child_update), max_depth - 1,
-                                       updates_per_parent_id));
+                                       updates_per_parent_guid));
   }
 
   // Sort the children according to their unique position.
@@ -563,29 +577,27 @@
   // selects the first one.
   // Associate permanent folders.
   for (const auto& tree_tag_and_root : remote_forest_) {
-    // Special-case the root folder to avoid recording
-    // |RemoteBookmarkUpdateError::kUnsupportedPermanentFolder|.
-    if (tree_tag_and_root.first ==
-        syncer::ModelTypeToRootTag(syncer::BOOKMARKS)) {
-      // The root folder is not expected to have children, because all children
-      // should themselves be permanent folders too and hence directly populate
-      // |tree_tag_and_root| without nesting.
-      const int num_unexpected_descendants_of_root_folder =
-          CountRemoteTreeNodeDescendantsForUma(tree_tag_and_root.second);
-      for (int i = 0; i < num_unexpected_descendants_of_root_folder; ++i) {
-        LogProblematicBookmark(RemoteBookmarkUpdateError::
-                                   kDescendantOfRootNodeWithoutPermanentFolder);
-      }
-      continue;
-    }
+    const std::string& server_defined_unique_tag = tree_tag_and_root.first;
+
+    DCHECK(!server_defined_unique_tag.empty());
 
     const bookmarks::BookmarkNode* permanent_folder =
-        GetPermanentFolder(bookmark_model_, tree_tag_and_root.first);
+        GetPermanentFolderForServerDefinedUniqueTag(bookmark_model_,
+                                                    server_defined_unique_tag);
+
+    // Ignore unsupported permanent folders.
     if (!permanent_folder) {
+      DCHECK(!GetPermanentFolderGUIDForServerDefinedUniqueTag(
+                  server_defined_unique_tag)
+                  .is_valid());
       LogProblematicBookmark(
           RemoteBookmarkUpdateError::kUnsupportedPermanentFolder);
       continue;
     }
+
+    DCHECK_EQ(permanent_folder->guid(),
+              GetPermanentFolderGUIDForServerDefinedUniqueTag(
+                  server_defined_unique_tag));
     MergeSubtree(/*local_subtree_root=*/permanent_folder,
                  /*remote_node=*/tree_tag_and_root.second);
   }
@@ -614,7 +626,7 @@
   // of their parent.
   GroupedUpdates grouped_updates = GroupValidUpdates(std::move(updates));
 
-  DeduplicateValidUpdatesByGUID(&grouped_updates.updates_per_parent_id);
+  DeduplicateValidUpdatesByGUID(&grouped_updates.updates_per_parent_guid);
 
   // Construct one tree per permanent entity.
   RemoteForest update_forest;
@@ -629,14 +641,14 @@
         server_defined_unique_tag,
         RemoteTreeNode::BuildTree(std::move(permanent_node_update),
                                   kMaxBookmarkTreeDepth,
-                                  &grouped_updates.updates_per_parent_id));
+                                  &grouped_updates.updates_per_parent_guid));
   }
 
-  // All remaining entries in |updates_per_parent_id| must be unreachable from
+  // All remaining entries in |updates_per_parent_guid| must be unreachable from
   // permanent entities, since otherwise they would have been moved away.
-  for (const auto& parent_id_and_updates :
-       grouped_updates.updates_per_parent_id) {
-    for (const UpdateResponseData& update : parent_id_and_updates.second) {
+  for (const auto& parent_guid_and_updates :
+       grouped_updates.updates_per_parent_guid) {
+    for (const UpdateResponseData& update : parent_guid_and_updates.second) {
       if (update.entity.specifics.has_bookmark()) {
         LogProblematicBookmark(RemoteBookmarkUpdateError::kMissingParentEntity);
         tracker_for_recording_ignored_updates
@@ -699,10 +711,8 @@
     // Permanent nodes don't match by GUID but by |server_defined_unique_tag|.
     // As extra precaution, specially with remote GUIDs in mind, let's ignore
     // them explicitly here.
-    if (node->is_permanent_node() ||
-        GetPermanentFolder(bookmark_model,
-                           remote_entity.server_defined_unique_tag) !=
-            nullptr) {
+    DCHECK(remote_entity.server_defined_unique_tag.empty());
+    if (node->is_permanent_node()) {
       continue;
     }
 
diff --git a/components/sync_bookmarks/bookmark_model_merger.h b/components/sync_bookmarks/bookmark_model_merger.h
index 61d79e0e..d09a20f 100644
--- a/components/sync_bookmarks/bookmark_model_merger.h
+++ b/components/sync_bookmarks/bookmark_model_merger.h
@@ -59,20 +59,23 @@
   // Internal representation of a remote tree, composed of nodes.
   class RemoteTreeNode final {
    private:
-    using UpdatesPerParentId =
-        std::unordered_map<std::string, std::list<syncer::UpdateResponseData>>;
+    using UpdatesPerParentGUID =
+        std::unordered_map<base::GUID,
+                           std::list<syncer::UpdateResponseData>,
+                           base::GUIDHash>;
 
    public:
     // Constructs a tree given |update| as root and recursively all descendants
-    // by traversing |*updates_per_parent_id|. |update| and
-    // |updates_per_parent_id| must not be null. All updates
-    // |*updates_per_parent_id| must represent valid updates. Updates
+    // by traversing |*updates_per_parent_guid|. |update| and
+    // |updates_per_parent_guid| must not be null. All updates
+    // |*updates_per_parent_guid| must represent valid updates. Updates
     // corresponding from descendant nodes are moved away from
-    // |*updates_per_parent_id|. |max_depth| is the max tree depth to sync
+    // |*updates_per_parent_guid|. |max_depth| is the max tree depth to sync
     // after which content is silently ignored.
-    static RemoteTreeNode BuildTree(syncer::UpdateResponseData update,
-                                    size_t max_depth,
-                                    UpdatesPerParentId* updates_per_parent_id);
+    static RemoteTreeNode BuildTree(
+        syncer::UpdateResponseData update,
+        size_t max_depth,
+        UpdatesPerParentGUID* updates_per_parent_guid);
 
     ~RemoteTreeNode();
 
diff --git a/components/sync_bookmarks/bookmark_model_merger_unittest.cc b/components/sync_bookmarks/bookmark_model_merger_unittest.cc
index 40cae0c82..9140621 100644
--- a/components/sync_bookmarks/bookmark_model_merger_unittest.cc
+++ b/components/sync_bookmarks/bookmark_model_merger_unittest.cc
@@ -107,11 +107,8 @@
                             const std::string& title,
                             const syncer::UniquePosition& unique_position) {
     data_.id = GetFakeServerIdFromGUID(guid);
-    data_.parent_id = GetFakeServerIdFromGUID(parent_guid);
     data_.originator_client_item_id = guid.AsLowercaseString();
 
-    // Note that proto field |parent_guid| is currently only used to consider
-    // specifics valid.
     sync_pb::BookmarkSpecifics* bookmark_specifics =
         data_.specifics.mutable_bookmark();
     bookmark_specifics->set_legacy_canonicalized_title(title);
@@ -1013,7 +1010,6 @@
 }
 
 TEST(BookmarkModelMergerTest, ShouldIgnoreChildrenForNonFolderNodes) {
-  const std::string kParentId = "parent_id";
   const std::string kChildId = "child_id";
   const std::string kParentTitle = "Parent Title";
   const std::string kChildTitle = "Child Title";
@@ -1825,9 +1821,14 @@
       /*is_folder=*/false,
       /*unique_position=*/MakeRandomPosition()));
   updates.back().entity.parent_id = kRootNodeId;
+  // To cover a slightly different case and guard against future bugs, let's
+  // assume one of the updates uses a GUID in specifics that refers to the
+  // root node.
   updates.push_back(CreateUpdateResponseData(
       /*guid=*/base::GUID::GenerateRandomV4(),
-      /*parent_guid=*/base::GUID::GenerateRandomV4(), "Title2",
+      /*parent_guid=*/
+      base::GUID::ParseLowercase(bookmarks::BookmarkNode::kRootNodeGuid),
+      "Title2",
       /*url=*/"http://url2",
       /*is_folder=*/false,
       /*unique_position=*/MakeRandomPosition()));
@@ -1837,9 +1838,7 @@
   Merge(std::move(updates), bookmark_model.get());
   histogram_tester.ExpectUniqueSample(
       "Sync.ProblematicServerSideBookmarksDuringMerge",
-      /*sample=*/
-      ExpectedRemoteBookmarkUpdateError::
-          kDescendantOfRootNodeWithoutPermanentFolder,
+      /*sample=*/ExpectedRemoteBookmarkUpdateError::kMissingParentEntity,
       /*count=*/2);
 }
 
@@ -2027,7 +2026,6 @@
       /*guid=*/base::GUID::GenerateRandomV4(), /*parent_guid=*/kGuid,
       "Some title",
       /*url=*/"", /*is_folder=*/true, MakeRandomPosition()));
-  updates.back().entity.parent_id = "Id1";
 
   updates.push_back(CreateUpdateResponseData(
       /*guid=*/kGuid, /*parent_guid=*/BookmarkBarGuid(), kTitle2,
@@ -2038,7 +2036,6 @@
       /*guid=*/base::GUID::GenerateRandomV4(), /*parent_guid=*/kGuid,
       "Some title 2",
       /*url=*/"", /*is_folder=*/true, MakeRandomPosition()));
-  updates.back().entity.parent_id = "Id2";
 
   base::HistogramTester histogram_tester;
   std::unique_ptr<SyncedBookmarkTracker> tracker =
@@ -2189,7 +2186,6 @@
       /*guid=*/base::GUID::GenerateRandomV4(), /*parent_guid=*/kGuid,
       "Some title",
       /*url=*/"", /*is_folder=*/true, MakeRandomPosition()));
-  updates.back().entity.parent_id = "Id1";
 
   updates.push_back(CreateUpdateResponseData(
       /*guid=*/kGuid, /*parent_guid=*/BookmarkBarGuid(), kTitle,
diff --git a/components/sync_preferences/pref_model_associator_unittest.cc b/components/sync_preferences/pref_model_associator_unittest.cc
index 1d381a0..3d6505b 100644
--- a/components/sync_preferences/pref_model_associator_unittest.cc
+++ b/components/sync_preferences/pref_model_associator_unittest.cc
@@ -177,8 +177,7 @@
       pref_service_->FindPreference(kListPrefName);
   base::Value merged_value(pref_sync_service_->MergePreference(
       pref->name(), *pref->GetValue(), *null_value));
-  const base::ListValue* local_list_value =
-      pref_service_->GetList(kListPrefName);
+  const base::Value* local_list_value = pref_service_->GetList(kListPrefName);
   EXPECT_EQ(merged_value, *local_list_value);
 }
 
@@ -194,8 +193,7 @@
       pref_service_->FindPreference(kListPrefName);
   base::Value merged_value(pref_sync_service_->MergePreference(
       pref->name(), *pref->GetValue(), *empty_value));
-  const base::ListValue* local_list_value =
-      pref_service_->GetList(kListPrefName);
+  const base::Value* local_list_value = pref_service_->GetList(kListPrefName);
   EXPECT_EQ(merged_value, *local_list_value);
 }
 
@@ -299,7 +297,7 @@
       pref_service_->FindPreference(kDictionaryPrefName);
   base::Value merged_value(pref_sync_service_->MergePreference(
       pref->name(), *pref->GetValue(), *null_value));
-  const base::DictionaryValue* local_dict_value =
+  const base::Value* local_dict_value =
       pref_service_->GetDictionary(kDictionaryPrefName);
   EXPECT_EQ(merged_value, *local_dict_value);
 }
@@ -316,7 +314,7 @@
       pref_service_->FindPreference(kDictionaryPrefName);
   base::Value merged_value(pref_sync_service_->MergePreference(
       pref->name(), *pref->GetValue(), *empty_value));
-  const base::DictionaryValue* local_dict_value =
+  const base::Value* local_dict_value =
       pref_service_->GetDictionary(kDictionaryPrefName);
   EXPECT_EQ(merged_value, *local_dict_value);
 }
diff --git a/components/test/data/arc/icon_app_28.png b/components/test/data/arc/icon_app_28.png
new file mode 100644
index 0000000..b316a860
--- /dev/null
+++ b/components/test/data/arc/icon_app_28.png
Binary files differ
diff --git a/components/test/data/arc/icon_app_56.png b/components/test/data/arc/icon_app_56.png
new file mode 100644
index 0000000..fd617b1d
--- /dev/null
+++ b/components/test/data/arc/icon_app_56.png
Binary files differ
diff --git a/components/ukm/ukm_service_unittest.cc b/components/ukm/ukm_service_unittest.cc
index 555f6aaa..c3c0e55 100644
--- a/components/ukm/ukm_service_unittest.cc
+++ b/components/ukm/ukm_service_unittest.cc
@@ -160,8 +160,7 @@
   }
 
   int GetPersistedLogCount() {
-    const base::ListValue* list_value =
-        prefs_.GetList(prefs::kUkmUnsentLogStore);
+    const base::Value* list_value = prefs_.GetList(prefs::kUkmUnsentLogStore);
     return list_value->GetList().size();
   }
 
diff --git a/components/update_client/update_client_unittest.cc b/components/update_client/update_client_unittest.cc
index cbdaef81..26f5a02 100644
--- a/components/update_client/update_client_unittest.cc
+++ b/components/update_client/update_client_unittest.cc
@@ -2833,8 +2833,8 @@
   EXPECT_EQ(ComponentState::kUpdated, items[5].state);
   EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[5].id.c_str());
 
-  const base::DictionaryValue* dict =
-      config()->GetPrefService()->GetDictionary("updateclientdata");
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *config()->GetPrefService()->GetDictionary("updateclientdata"));
   std::string pv;
   dict->GetString("apps.jebgalgnebhfojomionfpkfelancnnkf.pv", &pv);
   EXPECT_STREQ("1.0", pv.c_str());
diff --git a/components/user_manager/known_user.cc b/components/user_manager/known_user.cc
index fe75d87..db1c6e6 100644
--- a/components/user_manager/known_user.cc
+++ b/components/user_manager/known_user.cc
@@ -223,7 +223,7 @@
   if (!account_id.is_valid())
     return false;
 
-  const base::ListValue* known_users = local_state_->GetList(kKnownUsers);
+  const base::Value* known_users = local_state_->GetList(kKnownUsers);
   for (const base::Value& element_value : known_users->GetList()) {
     if (element_value.is_dict()) {
       const base::DictionaryValue& element =
@@ -434,7 +434,7 @@
 std::vector<AccountId> KnownUser::GetKnownAccountIds() {
   std::vector<AccountId> result;
 
-  const base::ListValue* known_users = local_state_->GetList(kKnownUsers);
+  const base::Value* known_users = local_state_->GetList(kKnownUsers);
   for (const base::Value& element_value : known_users->GetList()) {
     if (element_value.is_dict()) {
       const base::DictionaryValue& element =
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc
index e9b87e21..a2559fe 100644
--- a/components/user_manager/user_manager_base.cc
+++ b/components/user_manager/user_manager_base.cc
@@ -69,7 +69,7 @@
 
 // This reads integer value from kUserType Local State preference and
 // interprets it as UserType. It is used in initial users load.
-UserType GetStoredUserType(const base::DictionaryValue* prefs_user_types,
+UserType GetStoredUserType(const base::Value* prefs_user_types,
                            const AccountId& account_id) {
   const base::Value* stored_user_type = prefs_user_types->FindKey(
       account_id.HasAccountIdKey() ? account_id.GetAccountIdKey()
@@ -805,17 +805,16 @@
   user_loading_stage_ = STAGE_LOADING;
 
   PrefService* local_state = GetLocalState();
-  const base::ListValue* prefs_regular_users =
+  const base::Value* prefs_regular_users =
       local_state->GetList(kRegularUsersPref);
 
-  const base::DictionaryValue* prefs_display_names =
+  const base::Value* prefs_display_names =
       local_state->GetDictionary(kUserDisplayName);
-  const base::DictionaryValue* prefs_given_names =
+  const base::Value* prefs_given_names =
       local_state->GetDictionary(kUserGivenName);
-  const base::DictionaryValue* prefs_display_emails =
+  const base::Value* prefs_display_emails =
       local_state->GetDictionary(kUserDisplayEmail);
-  const base::DictionaryValue* prefs_user_types =
-      local_state->GetDictionary(kUserType);
+  const base::Value* prefs_user_types = local_state->GetDictionary(kUserType);
 
   // Load public sessions first.
   std::set<AccountId> device_local_accounts_set;
@@ -824,8 +823,8 @@
   // Load regular users and supervised users.
   std::vector<AccountId> regular_users;
   std::set<AccountId> regular_users_set;
-  ParseUserList(*prefs_regular_users, device_local_accounts_set, &regular_users,
-                &regular_users_set);
+  ParseUserList(base::Value::AsListValue(*prefs_regular_users),
+                device_local_accounts_set, &regular_users, &regular_users_set);
   for (std::vector<AccountId>::const_iterator it = regular_users.begin();
        it != regular_users.end(); ++it) {
     if (IsDeprecatedSupervisedAccountId(*it)) {
@@ -884,8 +883,7 @@
 }
 
 bool UserManagerBase::UserExistsInList(const AccountId& account_id) const {
-  const base::ListValue* user_list =
-      GetLocalState()->GetList(kRegularUsersPref);
+  const base::Value* user_list = GetLocalState()->GetList(kRegularUsersPref);
   for (const base::Value& i : user_list->GetList()) {
     const std::string* email = i.GetIfString();
     if (email && (account_id.GetUserEmail() == *email))
@@ -975,7 +973,7 @@
     const AccountId& account_id) const {
   DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
 
-  const base::DictionaryValue* prefs_oauth_status =
+  const base::Value* prefs_oauth_status =
       GetLocalState()->GetDictionary(kUserOAuthTokenStatus);
   if (!prefs_oauth_status)
     return User::OAUTH_TOKEN_STATUS_UNKNOWN;
@@ -991,7 +989,7 @@
 bool UserManagerBase::LoadForceOnlineSignin(const AccountId& account_id) const {
   DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
 
-  const base::DictionaryValue* prefs_force_online =
+  const base::Value* prefs_force_online =
       GetLocalState()->GetDictionary(kUserForceOnlineSignin);
   if (prefs_force_online) {
     return prefs_force_online->FindBoolKey(account_id.GetUserEmail())
diff --git a/components/variations/service/variations_field_trial_creator.cc b/components/variations/service/variations_field_trial_creator.cc
index 86461fd..0972105 100644
--- a/components/variations/service/variations_field_trial_creator.cc
+++ b/components/variations/service/variations_field_trial_creator.cc
@@ -343,7 +343,7 @@
     return permanent_overridden_country;
   }
 
-  const base::ListValue* list_value =
+  const base::Value* list_value =
       local_state()->GetList(prefs::kVariationsPermanentConsistencyCountry);
   const std::string* stored_version_string = nullptr;
   const std::string* stored_country = nullptr;
diff --git a/components/variations/service/variations_service.cc b/components/variations/service/variations_service.cc
index b812395..5ae3f54 100644
--- a/components/variations/service/variations_service.cc
+++ b/components/variations/service/variations_service.cc
@@ -992,7 +992,7 @@
   if (!variations_overridden_country.empty())
     return variations_overridden_country;
 
-  const base::ListValue* list_value =
+  const base::Value* list_value =
       local_state_->GetList(prefs::kVariationsPermanentConsistencyCountry);
   std::string stored_country;
 
diff --git a/components/variations/service/variations_service_unittest.cc b/components/variations/service/variations_service_unittest.cc
index 7190530..6fa4587 100644
--- a/components/variations/service/variations_service_unittest.cc
+++ b/components/variations/service/variations_service_unittest.cc
@@ -818,8 +818,8 @@
                            base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
       expected_list_value.Append(component);
     }
-    const base::ListValue* pref_value =
-        prefs_.GetList(prefs::kVariationsPermanentConsistencyCountry);
+    const base::ListValue* pref_value = &base::Value::AsListValue(
+        *prefs_.GetList(prefs::kVariationsPermanentConsistencyCountry));
     EXPECT_EQ(ListValueToString(expected_list_value),
               ListValueToString(*pref_value))
         << test.permanent_consistency_country_before << ", " << test.version
diff --git a/components/version_ui/resources/about_version.js b/components/version_ui/resources/about_version.js
index f986650..71bf0e2 100644
--- a/components/version_ui/resources/about_version.js
+++ b/components/version_ui/resources/about_version.js
@@ -82,7 +82,7 @@
 }
 // </if>
 
-// <if expr="chromeos or lacros">
+// <if expr="chromeos_ash or chromeos_lacros">
 /**
  * Callback from the backend to inform if Lacros is primary or not.
  * @param {string} isPrimary True if it is primary.
@@ -118,7 +118,7 @@
   addWebUIListener('return-os-firmware-version', returnOsFirmwareVersion);
   addWebUIListener('return-arc-version', returnARCVersion);
   // </if>
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   addWebUIListener('return-lacros-primary', returnLacrosPrimary);
   // </if>
 
diff --git a/components/webxr/README.md b/components/webxr/README.md
new file mode 100644
index 0000000..af0dd75
--- /dev/null
+++ b/components/webxr/README.md
@@ -0,0 +1,21 @@
+# WebXR Component
+
+## WebXR Overview
+The web-exposed interface to WebXR begins in [Blink](https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/xr/README.md).
+This code (with the help of the `VRService` [mojom interface](https://source.chromium.org/chromium/chromium/src/+/main:device/vr/public/mojom/README.md))
+talks with the [browser process](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/xr/README.md)
+to broker a connection directly with the corresponding [device code](https://source.chromium.org/chromium/chromium/src/+/main:device/vr/README.md).
+Note that this device code is often hosted in a separate XR utility process, and
+thus the [isolated_xr_device service](https://source.chromium.org/chromium/chromium/src/+/main:content/services/isolated_xr_device/README.md)
+needs to assist the browser in brokering these connections. The code that talks
+directly with a device or its corresponding SDK/API (e.g. OpenXR) is often
+referred to as a "Runtime" throughout XR code. It is responsible for querying or
+formatting the data into/out of the expected WebXR formats.
+
+## Component Code
+This component code may depend on code in both //device and //content. It is
+intended for code that is necessary for a given runtime to work, but cannot be
+added under //device due to layering violations. Often this is because there may
+need to be customizable extension points added for different embedders. This
+includes code such as rendering utilizing the viz framework, or extension
+methods for embedders to customize the install flow for some runtimes.
diff --git a/content/DEPS b/content/DEPS
index 84b41c4..ec3de28 100644
--- a/content/DEPS
+++ b/content/DEPS
@@ -30,6 +30,7 @@
   "+components/services/filesystem",
   "+components/services/font/public",
   "+components/variations",
+  "+components/value_store",
 
   "+crypto",
   "+grit/blink_resources.h",
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 626b8f5..2d75466 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -340,8 +340,6 @@
     "$target_gen_dir/devtools/protocol/tracing.h",
     "about_url_loader_factory.cc",
     "about_url_loader_factory.h",
-    "accessibility/accessibility_event_recorder.cc",
-    "accessibility/accessibility_event_recorder.h",
     "accessibility/accessibility_tree_formatter_blink.cc",
     "accessibility/accessibility_tree_formatter_blink.h",
     "accessibility/browser_accessibility.cc",
@@ -2321,11 +2319,16 @@
   }
 
   if (is_chromeos) {
+    deps += [ "//components/value_store:value_store" ]
     sources += [
       "handwriting/handwriting_recognition_service_impl_cros.cc",
       "handwriting/handwriting_recognition_service_impl_cros.h",
       "handwriting/handwriting_recognizer_impl_cros.cc",
       "handwriting/handwriting_recognizer_impl_cros.h",
+      "lock_screen/lock_screen_service_impl.cc",
+      "lock_screen/lock_screen_service_impl.h",
+      "lock_screen/lock_screen_storage_impl.cc",
+      "lock_screen/lock_screen_storage_impl.h",
     ]
   }
 
diff --git a/content/browser/accessibility/accessibility_event_recorder.cc b/content/browser/accessibility/accessibility_event_recorder.cc
deleted file mode 100644
index 5c2e7e0..0000000
--- a/content/browser/accessibility/accessibility_event_recorder.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/accessibility/accessibility_event_recorder.h"
-
-#include "content/browser/accessibility/browser_accessibility_manager.h"
-
-namespace content {
-
-AccessibilityEventRecorder::AccessibilityEventRecorder(
-    BrowserAccessibilityManager* manager)
-    : manager_(manager) {}
-
-}  // namespace content
diff --git a/content/browser/accessibility/accessibility_event_recorder.h b/content/browser/accessibility/accessibility_event_recorder.h
deleted file mode 100644
index 163e056..0000000
--- a/content/browser/accessibility/accessibility_event_recorder.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_H_
-#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/memory/raw_ptr.h"
-#include "base/process/process_handle.h"
-#include "content/common/content_export.h"
-#include "ui/accessibility/platform/inspect/ax_event_recorder.h"
-#include "ui/accessibility/platform/inspect/ax_inspect.h"
-
-namespace content {
-
-using ui::AXTreeSelector;
-
-class BrowserAccessibilityManager;
-
-// Listens for native accessibility events fired by a given
-// BrowserAccessibilityManager and saves human-readable log strings for
-// each event fired to a vector. Construct an instance of this class to
-// begin listening, call GetEventLogs() to get all of the logs so far, and
-// destroy it to stop listening.
-//
-// A log string should be of the form "<event> on <node>" where <event> is
-// the name of the event fired (platform-specific) and <node> is information
-// about the accessibility node on which the event was fired, for example its
-// role and name.
-//
-// The implementation is highly platform-specific; a subclass is needed for
-// each platform does most of the work.
-//
-// As currently designed, there should only be one instance of this class.
-class CONTENT_EXPORT AccessibilityEventRecorder : public ui::AXEventRecorder {
- public:
-  // Get a set of factory methods to create event-recorders, one for each test
-  // pass; see |DumpAccessibilityTestBase|.
-  using EventRecorderFactory = std::unique_ptr<AccessibilityEventRecorder> (*)(
-      BrowserAccessibilityManager* manager,
-      base::ProcessId pid,
-      const AXTreeSelector& selector);
-
-  AccessibilityEventRecorder(BrowserAccessibilityManager* manager);
-
-  AccessibilityEventRecorder(const AccessibilityEventRecorder&) = delete;
-  AccessibilityEventRecorder& operator=(const AccessibilityEventRecorder&) =
-      delete;
-
- protected:
-  const raw_ptr<BrowserAccessibilityManager> manager_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_H_
diff --git a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc b/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
index 8df13c6..e514e54 100644
--- a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
@@ -62,8 +62,8 @@
 AccessibilityEventRecorderAuraLinux::AccessibilityEventRecorderAuraLinux(
     BrowserAccessibilityManager* manager,
     base::ProcessId pid,
-    const AXTreeSelector& selector)
-    : AccessibilityEventRecorder(manager), pid_(pid), selector_(selector) {
+    const ui::AXTreeSelector& selector)
+    : manager_(manager), pid_(pid), selector_(selector) {
   CHECK(!instance_) << "There can be only one instance of"
                     << " AccessibilityEventRecorder at a time.";
 
diff --git a/content/browser/accessibility/accessibility_event_recorder_auralinux.h b/content/browser/accessibility/accessibility_event_recorder_auralinux.h
index 6873935..5b78738 100644
--- a/content/browser/accessibility/accessibility_event_recorder_auralinux.h
+++ b/content/browser/accessibility/accessibility_event_recorder_auralinux.h
@@ -8,11 +8,16 @@
 #include <atk/atk.h>
 #include <atspi/atspi.h>
 
-#include "content/browser/accessibility/accessibility_event_recorder.h"
+#include "base/memory/raw_ptr.h"
+#include "base/process/process_handle.h"
 #include "content/common/content_export.h"
+#include "ui/accessibility/platform/inspect/ax_event_recorder.h"
+#include "ui/accessibility/platform/inspect/ax_inspect.h"
 
 namespace content {
 
+class BrowserAccessibilityManager;
+
 // This class has two distinct event recording code paths. When we are
 // recording events in-process (typically this is used for
 // DumpAccessibilityEvents tests), we use ATK's global event handlers. Since
@@ -23,12 +28,12 @@
 // in-process as well, thus it should be possible to remove the ATK code path
 // entirely.
 class CONTENT_EXPORT AccessibilityEventRecorderAuraLinux
-    : public AccessibilityEventRecorder {
+    : public ui::AXEventRecorder {
  public:
   explicit AccessibilityEventRecorderAuraLinux(
       BrowserAccessibilityManager* manager,
       base::ProcessId pid,
-      const AXTreeSelector& selector);
+      const ui::AXTreeSelector& selector);
 
   AccessibilityEventRecorderAuraLinux(
       const AccessibilityEventRecorderAuraLinux&) = delete;
@@ -60,6 +65,8 @@
   void RemoveATSPIEventListeners();
 
   AtspiEventListener* atspi_event_listener_ = nullptr;
+  // TODO: should be either removed or converted to a weakptr.
+  const raw_ptr<BrowserAccessibilityManager> manager_;
   base::ProcessId pid_;
   ui::AXTreeSelector selector_;
   static AccessibilityEventRecorderAuraLinux* instance_;
diff --git a/content/browser/accessibility/accessibility_event_recorder_fuchsia.cc b/content/browser/accessibility/accessibility_event_recorder_fuchsia.cc
index 9513381..663070c 100644
--- a/content/browser/accessibility/accessibility_event_recorder_fuchsia.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_fuchsia.cc
@@ -11,10 +11,8 @@
     AccessibilityEventRecorderFuchsia::instance_ = nullptr;
 
 AccessibilityEventRecorderFuchsia::AccessibilityEventRecorderFuchsia(
-    BrowserAccessibilityManager* manager,
     base::ProcessId pid,
-    const ui::AXTreeSelector& selector)
-    : AccessibilityEventRecorder(manager) {
+    const ui::AXTreeSelector& selector) {
   CHECK(!instance_) << "There can be only one instance of"
                     << " AccessibilityEventRecorder at a time.";
   instance_ = this;
diff --git a/content/browser/accessibility/accessibility_event_recorder_fuchsia.h b/content/browser/accessibility/accessibility_event_recorder_fuchsia.h
index 24ee840..4b33d5e 100644
--- a/content/browser/accessibility/accessibility_event_recorder_fuchsia.h
+++ b/content/browser/accessibility/accessibility_event_recorder_fuchsia.h
@@ -5,16 +5,17 @@
 #ifndef CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_FUCHSIA_H_
 #define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_FUCHSIA_H_
 
-#include "content/browser/accessibility/accessibility_event_recorder.h"
+#include "base/process/process_handle.h"
 #include "content/common/content_export.h"
+#include "ui/accessibility/platform/inspect/ax_event_recorder.h"
+#include "ui/accessibility/platform/inspect/ax_inspect.h"
 
 namespace content {
 
 class CONTENT_EXPORT AccessibilityEventRecorderFuchsia
-    : public AccessibilityEventRecorder {
+    : public ui::AXEventRecorder {
  public:
-  AccessibilityEventRecorderFuchsia(BrowserAccessibilityManager* manager,
-                                    base::ProcessId pid,
+  AccessibilityEventRecorderFuchsia(base::ProcessId pid,
                                     const ui::AXTreeSelector& selector);
 
   AccessibilityEventRecorderFuchsia(const AccessibilityEventRecorderFuchsia&) =
diff --git a/content/browser/accessibility/accessibility_event_recorder_mac.h b/content/browser/accessibility/accessibility_event_recorder_mac.h
index 7f74891..f26b4d79 100644
--- a/content/browser/accessibility/accessibility_event_recorder_mac.h
+++ b/content/browser/accessibility/accessibility_event_recorder_mac.h
@@ -6,9 +6,10 @@
 #define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_MAC_H_
 
 #include "base/mac/scoped_cftyperef.h"
-#include "content/browser/accessibility/accessibility_event_recorder.h"
 #include "content/browser/accessibility/browser_accessibility_cocoa.h"
 #include "content/common/content_export.h"
+#include "ui/accessibility/platform/inspect/ax_event_recorder.h"
+#include "ui/accessibility/platform/inspect/ax_inspect.h"
 
 @class BrowserAccessibilityCocoa;
 
@@ -17,11 +18,10 @@
 // Implementation of AccessibilityEventRecorder that uses AXObserver to
 // watch for NSAccessibility events.
 class CONTENT_EXPORT AccessibilityEventRecorderMac
-    : public AccessibilityEventRecorder {
+    : public ui::AXEventRecorder {
  public:
-  AccessibilityEventRecorderMac(BrowserAccessibilityManager* manager,
-                                base::ProcessId pid,
-                                const AXTreeSelector& selector);
+  AccessibilityEventRecorderMac(base::ProcessId pid,
+                                const ui::AXTreeSelector& selector);
 
   AccessibilityEventRecorderMac(const AccessibilityEventRecorderMac&) = delete;
   AccessibilityEventRecorderMac& operator=(
diff --git a/content/browser/accessibility/accessibility_event_recorder_mac.mm b/content/browser/accessibility/accessibility_event_recorder_mac.mm
index 9b8dc0ac..c686411 100644
--- a/content/browser/accessibility/accessibility_event_recorder_mac.mm
+++ b/content/browser/accessibility/accessibility_event_recorder_mac.mm
@@ -33,10 +33,9 @@
 }
 
 AccessibilityEventRecorderMac::AccessibilityEventRecorderMac(
-    BrowserAccessibilityManager* manager,
     base::ProcessId pid,
     const AXTreeSelector& selector)
-    : AccessibilityEventRecorder(manager), observer_run_loop_source_(nullptr) {
+    : observer_run_loop_source_(nullptr) {
   AXUIElementRef node = nil;
   if (pid) {
     node = AXUIElementCreateApplication(pid);
@@ -70,18 +69,41 @@
     @"AXLiveRegionCreated",
     @"AXLoadComplete",
     @"AXMenuItemSelected",
-    @"AXRowCollapsed",
-    @"AXRowExpanded",
     (NSString*)kAXMenuClosedNotification,
     (NSString*)kAXMenuOpenedNotification,
+    NSAccessibilityAnnouncementRequestedNotification,
+    NSAccessibilityApplicationActivatedNotification,
+    NSAccessibilityApplicationDeactivatedNotification,
+    NSAccessibilityApplicationHiddenNotification,
+    NSAccessibilityApplicationShownNotification,
+    NSAccessibilityCreatedNotification,
+    NSAccessibilityDrawerCreatedNotification,
     NSAccessibilityFocusedUIElementChangedNotification,
+    NSAccessibilityFocusedWindowChangedNotification,
+    NSAccessibilityHelpTagCreatedNotification,
+    NSAccessibilityLayoutChangedNotification,
+    NSAccessibilityMainWindowChangedNotification,
+    NSAccessibilityMovedNotification,
+    NSAccessibilityResizedNotification,
     NSAccessibilityRowCollapsedNotification,
     NSAccessibilityRowCountChangedNotification,
+    NSAccessibilityRowExpandedNotification,
+    NSAccessibilitySelectedCellsChangedNotification,
     NSAccessibilitySelectedChildrenChangedNotification,
+    NSAccessibilitySelectedChildrenMovedNotification,
+    NSAccessibilitySelectedColumnsChangedNotification,
     NSAccessibilitySelectedRowsChangedNotification,
     NSAccessibilitySelectedTextChangedNotification,
+    NSAccessibilitySheetCreatedNotification,
     NSAccessibilityTitleChangedNotification,
+    NSAccessibilityUIElementDestroyedNotification,
+    NSAccessibilityUnitsChangedNotification,
     NSAccessibilityValueChangedNotification,
+    NSAccessibilityWindowCreatedNotification,
+    NSAccessibilityWindowDeminiaturizedNotification,
+    NSAccessibilityWindowMiniaturizedNotification,
+    NSAccessibilityWindowMovedNotification,
+    NSAccessibilityWindowResizedNotification,
   ] retain];
 
   for (NSString* notification : notifications) {
diff --git a/content/browser/accessibility/accessibility_event_recorder_uia_win.cc b/content/browser/accessibility/accessibility_event_recorder_uia_win.cc
index fa07b03..14dfb0f5 100644
--- a/content/browser/accessibility/accessibility_event_recorder_uia_win.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_uia_win.cc
@@ -73,8 +73,7 @@
 AccessibilityEventRecorderUia::AccessibilityEventRecorderUia(
     BrowserAccessibilityManager* manager,
     base::ProcessId pid,
-    const base::StringPiece& application_name_match_pattern)
-    : AccessibilityEventRecorder(manager) {
+    const base::StringPiece& application_name_match_pattern) {
   CHECK(!base::subtle::NoBarrier_AtomicExchange(&instantiated_, 1))
       << "There can be only one instance at a time.";
 
diff --git a/content/browser/accessibility/accessibility_event_recorder_uia_win.h b/content/browser/accessibility/accessibility_event_recorder_uia_win.h
index e6c7f7d..4bef4d3 100644
--- a/content/browser/accessibility/accessibility_event_recorder_uia_win.h
+++ b/content/browser/accessibility/accessibility_event_recorder_uia_win.h
@@ -15,15 +15,19 @@
 
 #include "base/atomicops.h"
 #include "base/memory/raw_ptr.h"
+#include "base/process/process_handle.h"
 #include "base/run_loop.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/platform_thread.h"
 #include "base/win/atl.h"
-#include "content/browser/accessibility/accessibility_event_recorder.h"
+#include "ui/accessibility/platform/inspect/ax_event_recorder.h"
+#include "ui/accessibility/platform/inspect/ax_inspect.h"
 
 namespace content {
 
-class AccessibilityEventRecorderUia : public AccessibilityEventRecorder {
+class BrowserAccessibilityManager;
+
+class AccessibilityEventRecorderUia : public ui::AXEventRecorder {
  public:
   AccessibilityEventRecorderUia(
       BrowserAccessibilityManager* manager,
diff --git a/content/browser/accessibility/accessibility_event_recorder_win.cc b/content/browser/accessibility/accessibility_event_recorder_win.cc
index 8f2459c..b899352 100644
--- a/content/browser/accessibility/accessibility_event_recorder_win.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_win.cc
@@ -104,7 +104,7 @@
     BrowserAccessibilityManager* manager,
     base::ProcessId pid,
     const ui::AXTreeSelector& selector)
-    : AccessibilityEventRecorder(manager) {
+    : manager_(manager) {
   CHECK(!instance_) << "There can be only one instance of"
                     << " AccessibilityEventRecorder at a time.";
 
diff --git a/content/browser/accessibility/accessibility_event_recorder_win.h b/content/browser/accessibility/accessibility_event_recorder_win.h
index 5c1fa3a..4c90d22 100644
--- a/content/browser/accessibility/accessibility_event_recorder_win.h
+++ b/content/browser/accessibility/accessibility_event_recorder_win.h
@@ -7,13 +7,18 @@
 
 #include <oleacc.h>
 
-#include "content/browser/accessibility/accessibility_event_recorder.h"
+#include "base/memory/raw_ptr.h"
+#include "base/process/process_handle.h"
 #include "content/common/content_export.h"
+#include "ui/accessibility/platform/inspect/ax_event_recorder.h"
+#include "ui/accessibility/platform/inspect/ax_inspect.h"
 
 namespace content {
 
+class BrowserAccessibilityManager;
+
 class CONTENT_EXPORT AccessibilityEventRecorderWin
-    : public AccessibilityEventRecorder {
+    : public ui::AXEventRecorder {
  public:
   AccessibilityEventRecorderWin(BrowserAccessibilityManager* manager,
                                 base::ProcessId pid,
@@ -54,6 +59,8 @@
                                             void** ppvObject);
 
   HWINEVENTHOOK win_event_hook_handle_;
+  // TODO: should be either removed or converted to a weakptr.
+  const raw_ptr<BrowserAccessibilityManager> manager_;
   static AccessibilityEventRecorderWin* instance_;
 };
 
diff --git a/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
index dc33d36..17969549 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_mac.mm
+++ b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
@@ -15,6 +15,7 @@
 #include "content/browser/accessibility/browser_accessibility_mac.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/public/browser/ax_inspect_factory.h"
+#include "ui/accessibility/platform/ax_utils_mac.h"
 #include "ui/accessibility/platform/inspect/ax_inspect_utils.h"
 #include "ui/accessibility/platform/inspect/ax_inspect_utils_mac.h"
 #include "ui/accessibility/platform/inspect/ax_property_node.h"
@@ -276,13 +277,12 @@
   }
 
   // AXTextMarker
-  if (content::IsAXTextMarker(value)) {
-    return PopulateTextPosition(content::AXTextMarkerToAXPosition(value),
-                                indexer);
+  if (ui::IsAXTextMarker(value)) {
+    return PopulateTextPosition(ui::AXTextMarkerToAXPosition(value), indexer);
   }
 
   // AXTextMarkerRange
-  if (content::IsAXTextMarkerRange(value))
+  if (ui::IsAXTextMarkerRange(value))
     return PopulateTextMarkerRange(value, indexer);
 
   return AXNSObjectToBaseValue(value, indexer);
@@ -326,8 +326,8 @@
 base::Value AccessibilityTreeFormatterMac::PopulateTextMarkerRange(
     id marker_range,
     const AXTreeIndexerMac* indexer) const {
-  BrowserAccessibility::AXRange ax_range =
-      content::AXTextMarkerRangeToAXRange(marker_range);
+  ui::AXPlatformNodeDelegate::AXRange ax_range =
+      ui::AXTextMarkerRangeToAXRange(marker_range);
   if (ax_range.IsNull())
     return AXNilToBaseValue();
 
diff --git a/content/browser/accessibility/accessibility_tree_formatter_utils_mac.mm b/content/browser/accessibility/accessibility_tree_formatter_utils_mac.mm
index 696d87b..bcbbe83 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_utils_mac.mm
+++ b/content/browser/accessibility/accessibility_tree_formatter_utils_mac.mm
@@ -6,6 +6,7 @@
 
 #include "base/strings/sys_string_conversions.h"
 #include "content/browser/accessibility/browser_accessibility_mac.h"
+#include "ui/accessibility/platform/ax_utils_mac.h"
 #include "ui/accessibility/platform/inspect/ax_property_node.h"
 
 using ui::AXPropertyNode;
@@ -189,7 +190,7 @@
   if (IsNSAccessibilityElement(target) || IsAXUIElement(target))
     return InvokeForAXElement(target, property_node);
 
-  if (content::IsAXTextMarkerRange(target)) {
+  if (ui::IsAXTextMarkerRange(target)) {
     return InvokeForAXTextMarkerRange(target, property_node);
   }
 
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index 64dd6f0b..429ccb1 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -48,10 +48,6 @@
 // Web.
 class CONTENT_EXPORT BrowserAccessibility : public ui::AXPlatformNodeDelegate {
  public:
-  using AXPosition = ui::AXNodePosition::AXPositionInstance;
-  using SerializedPosition = ui::AXNodePosition::SerializedPosition;
-  using AXRange = ui::AXRange<AXPosition::element_type>;
-
   // Creates a platform specific BrowserAccessibility. Ownership passes to the
   // caller.
   static std::unique_ptr<BrowserAccessibility> Create(
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.h b/content/browser/accessibility/browser_accessibility_cocoa.h
index 8ba9cee..8a28477 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.h
+++ b/content/browser/accessibility/browser_accessibility_cocoa.h
@@ -40,20 +40,6 @@
 // Returns true if the given object is an NSRange instance.
 bool IsNSRange(id value);
 
-// Uses a system API to verify that the given object is an AXTextMarker object.
-bool IsAXTextMarker(id text_marker);
-
-// Uses a system API to verify that the given object is an AXTextMarkerRange
-// object.
-bool IsAXTextMarkerRange(id marker_range);
-
-// Returns the AXNodePosition representing the given AXTextMarker.
-CONTENT_EXPORT BrowserAccessibility::AXPosition AXTextMarkerToAXPosition(
-    id text_marker);
-
-// Returns the AXRange representing the given AXTextMarkerRange.
-BrowserAccessibility::AXRange AXTextMarkerRangeToAXRange(id marker_range);
-
 // Returns an AXTextMarker representing the given position in the tree.
 id AXTextMarkerFrom(const BrowserAccessibilityCocoa* anchor,
                     int offset,
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index db681c4..7182a01 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -36,11 +36,14 @@
 #include "ui/accessibility/ax_range.h"
 #include "ui/accessibility/ax_role_properties.h"
 #include "ui/accessibility/platform/ax_platform_node.h"
+#include "ui/accessibility/platform/ax_utils_mac.h"
 #include "ui/gfx/mac/coordinate_conversion.h"
 #include "ui/strings/grit/ax_strings.h"
 
 #import "ui/accessibility/platform/ax_platform_node_mac.h"
 
+using AXPosition = ui::AXPlatformNodeDelegate::AXPosition;
+using AXRange = ui::AXPlatformNodeDelegate::AXRange;
 using StringAttribute = ax::mojom::StringAttribute;
 using content::AccessibilityMatchPredicate;
 using content::BrowserAccessibility;
@@ -52,6 +55,8 @@
 using content::OneShotAccessibilityTreeSearch;
 using ui::AXNodeData;
 using ui::AXActionHandlerRegistry;
+using ui::AXTextMarkerRangeToAXRange;
+using ui::AXTextMarkerToAXPosition;
 
 static_assert(
     std::is_trivially_copyable<BrowserAccessibility::SerializedPosition>::value,
@@ -229,25 +234,19 @@
 using AXTextMarkerRangeRef = CFTypeRef;
 using AXTextMarkerRef = CFTypeRef;
 extern "C" {
-CFTypeID AXTextMarkerGetTypeID();
 AXTextMarkerRef AXTextMarkerCreate(CFAllocatorRef,
                                    const UInt8* bytes,
                                    CFIndex length);
-size_t AXTextMarkerGetLength(AXTextMarkerRef);
-const UInt8* AXTextMarkerGetBytePtr(AXTextMarkerRef);
 
-CFTypeID AXTextMarkerRangeGetTypeID();
 AXTextMarkerRangeRef AXTextMarkerRangeCreate(CFAllocatorRef,
                                              AXTextMarkerRef start,
                                              AXTextMarkerRef end);
-AXTextMarkerRef AXTextMarkerRangeCopyStartMarker(AXTextMarkerRangeRef);
-AXTextMarkerRef AXTextMarkerRangeCopyEndMarker(AXTextMarkerRangeRef);
 }  // extern "C"
 #endif
 
 // AXTextMarkerCreate is a system function that makes a copy of the data buffer
 // given to it.
-id CreateTextMarker(BrowserAccessibility::AXPosition position) {
+id CreateTextMarker(AXPosition position) {
   BrowserAccessibility::SerializedPosition serialized = position->Serialize();
   AXTextMarkerRef cf_text_marker = AXTextMarkerCreate(
       kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized),
@@ -255,7 +254,7 @@
   return [static_cast<id>(cf_text_marker) autorelease];
 }
 
-id CreateTextMarkerRange(const BrowserAccessibility::AXRange range) {
+id CreateTextMarkerRange(const AXRange range) {
   BrowserAccessibility::SerializedPosition serialized_anchor =
       range.anchor()->Serialize();
   BrowserAccessibility::SerializedPosition serialized_focus =
@@ -271,86 +270,41 @@
   return [static_cast<id>(cf_marker_range) autorelease];
 }
 
-BrowserAccessibility::AXPosition CreatePositionFromTextMarker(id text_marker) {
-  if (!content::IsAXTextMarker(text_marker))
-    return ui::AXNodePosition::CreateNullPosition();
-
-  AXTextMarkerRef cf_text_marker = static_cast<AXTextMarkerRef>(text_marker);
-  if (AXTextMarkerGetLength(cf_text_marker) !=
-      sizeof(BrowserAccessibility::SerializedPosition))
-    return ui::AXNodePosition::CreateNullPosition();
-
-  const UInt8* source_buffer = AXTextMarkerGetBytePtr(cf_text_marker);
-  if (!source_buffer)
-    return ui::AXNodePosition::CreateNullPosition();
-
-  return ui::AXNodePosition::Unserialize(
-      *reinterpret_cast<const BrowserAccessibility::SerializedPosition*>(
-          source_buffer));
-}
-
-BrowserAccessibility::AXRange CreateRangeFromTextMarkerRange(id marker_range) {
-  if (!content::IsAXTextMarkerRange(marker_range)) {
-    return BrowserAccessibility::AXRange();
-  }
-
-  AXTextMarkerRangeRef cf_marker_range =
-      static_cast<AXTextMarkerRangeRef>(marker_range);
-
-  base::ScopedCFTypeRef<AXTextMarkerRef> start_marker(
-      AXTextMarkerRangeCopyStartMarker(cf_marker_range));
-  base::ScopedCFTypeRef<AXTextMarkerRef> end_marker(
-      AXTextMarkerRangeCopyEndMarker(cf_marker_range));
-  if (!start_marker.get() || !end_marker.get())
-    return BrowserAccessibility::AXRange();
-
-  BrowserAccessibility::AXPosition anchor =
-      CreatePositionFromTextMarker(static_cast<id>(start_marker.get()));
-  BrowserAccessibility::AXPosition focus =
-      CreatePositionFromTextMarker(static_cast<id>(end_marker.get()));
-  // |BrowserAccessibility::AXRange| takes ownership of its anchor and focus.
-  return BrowserAccessibility::AXRange(std::move(anchor), std::move(focus));
-}
-
-BrowserAccessibility::AXPosition CreateTreePosition(
-    const BrowserAccessibility& object,
-    int offset) {
+AXPosition CreateTreePosition(const BrowserAccessibility& object, int offset) {
   const BrowserAccessibilityManager* manager = object.manager();
   DCHECK(manager);
   return ui::AXNodePosition::CreateTreePosition(manager->ax_tree_id(),
                                                 object.GetId(), offset);
 }
 
-BrowserAccessibility::AXPosition CreateTextPosition(
-    const BrowserAccessibility& object,
-    int offset,
-    ax::mojom::TextAffinity affinity) {
+AXPosition CreateTextPosition(const BrowserAccessibility& object,
+                              int offset,
+                              ax::mojom::TextAffinity affinity) {
   const BrowserAccessibilityManager* manager = object.manager();
   DCHECK(manager);
   return ui::AXNodePosition::CreateTextPosition(
       manager->ax_tree_id(), object.GetId(), offset, affinity);
 }
 
-BrowserAccessibility::AXRange CreateAXRange(
-    const BrowserAccessibility& start_object,
-    int start_offset,
-    ax::mojom::TextAffinity start_affinity,
-    const BrowserAccessibility& end_object,
-    int end_offset,
-    ax::mojom::TextAffinity end_affinity) {
-  BrowserAccessibility::AXPosition anchor =
+AXRange CreateAXRange(const BrowserAccessibility& start_object,
+                      int start_offset,
+                      ax::mojom::TextAffinity start_affinity,
+                      const BrowserAccessibility& end_object,
+                      int end_offset,
+                      ax::mojom::TextAffinity end_affinity) {
+  AXPosition anchor =
       start_object.IsLeaf()
           ? CreateTextPosition(start_object, start_offset, start_affinity)
           : CreateTreePosition(start_object, start_offset);
-  BrowserAccessibility::AXPosition focus =
+  AXPosition focus =
       end_object.IsLeaf()
           ? CreateTextPosition(end_object, end_offset, end_affinity)
           : CreateTreePosition(end_object, end_offset);
-  // |BrowserAccessibility::AXRange| takes ownership of its anchor and focus.
-  return BrowserAccessibility::AXRange(std::move(anchor), std::move(focus));
+  // |AXRange| takes ownership of its anchor and focus.
+  return AXRange(std::move(anchor), std::move(focus));
 }
 
-BrowserAccessibility::AXRange GetSelectedRange(BrowserAccessibility& owner) {
+AXRange GetSelectedRange(BrowserAccessibility& owner) {
   const BrowserAccessibilityManager* manager = owner.manager();
   if (!manager)
     return {};
@@ -385,11 +339,11 @@
                        *focus_object, focus_offset, focus_affinity);
 }
 
-void AddMisspelledTextAttributes(const BrowserAccessibility::AXRange& ax_range,
+void AddMisspelledTextAttributes(const AXRange& ax_range,
                                  NSMutableAttributedString* attributed_string) {
   int anchor_start_offset = 0;
   [attributed_string beginEditing];
-  for (const BrowserAccessibility::AXRange& leaf_text_range : ax_range) {
+  for (const AXRange& leaf_text_range : ax_range) {
     DCHECK(!leaf_text_range.IsNull());
     DCHECK_EQ(leaf_text_range.anchor()->GetAnchor(),
               leaf_text_range.focus()->GetAnchor())
@@ -431,16 +385,14 @@
 }
 
 NSString* GetTextForTextMarkerRange(id marker_range) {
-  BrowserAccessibility::AXRange range =
-      CreateRangeFromTextMarkerRange(marker_range);
+  AXRange range = AXTextMarkerRangeToAXRange(marker_range);
   if (range.IsNull())
     return nil;
   return base::SysUTF16ToNSString(range.GetText());
 }
 
 NSAttributedString* GetAttributedTextForTextMarkerRange(id marker_range) {
-  BrowserAccessibility::AXRange ax_range =
-      CreateRangeFromTextMarkerRange(marker_range);
+  AXRange ax_range = AXTextMarkerRangeToAXRange(marker_range);
   if (ax_range.IsNull())
     return nil;
 
@@ -716,41 +668,11 @@
          0 == strcmp([value objCType], @encode(NSRange));
 }
 
-bool content::IsAXTextMarker(id object) {
-  if (object == nil)
-    return false;
-
-  AXTextMarkerRef cf_text_marker = static_cast<AXTextMarkerRef>(object);
-  DCHECK(cf_text_marker);
-  return CFGetTypeID(cf_text_marker) == AXTextMarkerGetTypeID();
-}
-
-bool content::IsAXTextMarkerRange(id object) {
-  if (object == nil)
-    return false;
-
-  AXTextMarkerRangeRef cf_marker_range =
-      static_cast<AXTextMarkerRangeRef>(object);
-  DCHECK(cf_marker_range);
-  return CFGetTypeID(cf_marker_range) == AXTextMarkerRangeGetTypeID();
-}
-
-BrowserAccessibility::AXPosition content::AXTextMarkerToAXPosition(
-    id text_marker) {
-  return CreatePositionFromTextMarker(text_marker);
-}
-
-BrowserAccessibility::AXRange content::AXTextMarkerRangeToAXRange(
-    id text_marker_range) {
-  return CreateRangeFromTextMarkerRange(text_marker_range);
-}
-
 id content::AXTextMarkerFrom(const BrowserAccessibilityCocoa* anchor,
                              int offset,
                              ax::mojom::TextAffinity affinity) {
   BrowserAccessibility* anchor_node = [anchor owner];
-  BrowserAccessibility::AXPosition position =
-      CreateTextPosition(*anchor_node, offset, affinity);
+  AXPosition position = CreateTextPosition(*anchor_node, offset, affinity);
   return CreateTextMarker(std::move(position));
 }
 
@@ -1163,7 +1085,7 @@
 - (id)endTextMarker {
   if (![self instanceActive])
     return nil;
-  BrowserAccessibility::AXPosition position = _owner->CreateTextPositionAt(0);
+  AXPosition position = _owner->CreateTextPositionAt(0);
   return CreateTextMarker(position->CreatePositionAtEndOfContent());
 }
 
@@ -1348,16 +1270,15 @@
   if (!_owner->HasVisibleCaretOrSelection())
     return nil;
 
-  const BrowserAccessibility::AXRange range = GetSelectedRange(*_owner);
+  const AXRange range = GetSelectedRange(*_owner);
   // If the selection is not collapsed, then there is no visible caret.
   if (!range.IsCollapsed())
     return nil;
 
   // "ax::mojom::MoveDirection" is only relevant on platforms that use object
   // replacement characters in the accessibility tree. Mac is not one of them.
-  const BrowserAccessibility::AXPosition caretPosition =
-      range.focus()->LowestCommonAncestor(*_owner->CreateTextPositionAt(0),
-                                          ax::mojom::MoveDirection::kForward);
+  const AXPosition caretPosition = range.focus()->LowestCommonAncestor(
+      *_owner->CreateTextPositionAt(0), ax::mojom::MoveDirection::kForward);
   DCHECK(!caretPosition->IsNullPosition())
       << "Calling HasVisibleCaretOrSelection() should have ensured that there "
          "is a valid selection focus inside the current object.";
@@ -1893,7 +1814,7 @@
   if (!_owner->HasVisibleCaretOrSelection())
     return nil;
 
-  const BrowserAccessibility::AXRange range = GetSelectedRange(*_owner);
+  const AXRange range = GetSelectedRange(*_owner);
   if (range.IsNull())
     return nil;
   return base::SysUTF16ToNSString(range.GetText());
@@ -1913,16 +1834,14 @@
   if (!_owner->HasVisibleCaretOrSelection())
     return nil;
 
-  const BrowserAccessibility::AXRange range =
-      GetSelectedRange(*_owner).AsForwardRange();
+  const AXRange range = GetSelectedRange(*_owner).AsForwardRange();
   if (range.IsNull())
     return nil;
 
   // "ax::mojom::MoveDirection" is only relevant on platforms that use object
   // replacement characters in the accessibility tree. Mac is not one of them.
-  const BrowserAccessibility::AXPosition startPosition =
-      range.anchor()->LowestCommonAncestor(*_owner->CreateTextPositionAt(0),
-                                           ax::mojom::MoveDirection::kForward);
+  const AXPosition startPosition = range.anchor()->LowestCommonAncestor(
+      *_owner->CreateTextPositionAt(0), ax::mojom::MoveDirection::kForward);
   DCHECK(!startPosition->IsNullPosition())
       << "Calling HasVisibleCaretOrSelection() should have ensured that there "
          "is a valid selection anchor inside the current object.";
@@ -1946,7 +1865,7 @@
 - (id)selectedTextMarkerRange {
   if (![self instanceActive])
     return nil;
-  BrowserAccessibility::AXRange ax_range = GetSelectedRange(*_owner);
+  AXRange ax_range = GetSelectedRange(*_owner);
   if (ax_range.IsNull())
     return nil;
 
@@ -1991,7 +1910,7 @@
 - (id)startTextMarker {
   if (![self instanceActive])
     return nil;
-  BrowserAccessibility::AXPosition position = _owner->CreateTextPositionAt(0);
+  AXPosition position = _owner->CreateTextPositionAt(0);
   return CreateTextMarker(position->CreatePositionAtStartOfContent());
 }
 
@@ -2322,7 +2241,7 @@
       [[[NSMutableAttributedString alloc]
           initWithString:base::SysUTF16ToNSString(textContent)] autorelease];
   if (!_owner->IsText()) {
-    BrowserAccessibility::AXRange ax_range(
+    AXRange ax_range(
         _owner->CreateTextPositionAt(0),
         _owner->CreateTextPositionAt(static_cast<int>(textContent.length())));
     AddMisspelledTextAttributes(ax_range, attributedTextContent);
@@ -2464,8 +2383,7 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityUIElementForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (!position->IsNullPosition()) {
       BrowserAccessibility* ui_element =
           _owner->manager()->GetFromAXNode(position->GetAnchor());
@@ -2479,12 +2397,9 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityTextMarkerRangeForUIElementParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition startPosition =
-        _owner->CreateTextPositionAt(0);
-    BrowserAccessibility::AXPosition endPosition =
-        startPosition->CreatePositionAtEndOfAnchor();
-    BrowserAccessibility::AXRange range = BrowserAccessibility::AXRange(
-        std::move(startPosition), std::move(endPosition));
+    AXPosition startPosition = _owner->CreateTextPositionAt(0);
+    AXPosition endPosition = startPosition->CreatePositionAtEndOfAnchor();
+    AXRange range = AXRange(std::move(startPosition), std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
@@ -2501,8 +2416,7 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityNextTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreateNextCharacterPosition(
@@ -2512,8 +2426,7 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityPreviousTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreatePreviousCharacterPosition(
@@ -2523,52 +2436,43 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityLeftWordTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition endPosition =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition endPosition = AXTextMarkerToAXPosition(parameter);
     if (endPosition->IsNullPosition())
       return nil;
 
-    BrowserAccessibility::AXPosition startWordPosition =
-        endPosition->CreatePreviousWordStartPosition(
-            ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
-    BrowserAccessibility::AXPosition endWordPosition =
-        endPosition->CreatePreviousWordEndPosition(
-            ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
-    BrowserAccessibility::AXPosition startPosition =
-        *startWordPosition <= *endWordPosition ? std::move(endWordPosition)
-                                               : std::move(startWordPosition);
-    BrowserAccessibility::AXRange range(std::move(startPosition),
-                                        std::move(endPosition));
+    AXPosition startWordPosition = endPosition->CreatePreviousWordStartPosition(
+        ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
+    AXPosition endWordPosition = endPosition->CreatePreviousWordEndPosition(
+        ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
+    AXPosition startPosition = *startWordPosition <= *endWordPosition
+                                   ? std::move(endWordPosition)
+                                   : std::move(startWordPosition);
+    AXRange range(std::move(startPosition), std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityRightWordTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition startPosition =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition startPosition = AXTextMarkerToAXPosition(parameter);
     if (startPosition->IsNullPosition())
       return nil;
 
-    BrowserAccessibility::AXPosition endWordPosition =
-        startPosition->CreateNextWordEndPosition(
-            ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
-    BrowserAccessibility::AXPosition startWordPosition =
-        startPosition->CreateNextWordStartPosition(
-            ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
-    BrowserAccessibility::AXPosition endPosition =
-        *startWordPosition <= *endWordPosition ? std::move(startWordPosition)
-                                               : std::move(endWordPosition);
-    BrowserAccessibility::AXRange range(std::move(startPosition),
-                                        std::move(endPosition));
+    AXPosition endWordPosition = startPosition->CreateNextWordEndPosition(
+        ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
+    AXPosition startWordPosition = startPosition->CreateNextWordStartPosition(
+        ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
+    AXPosition endPosition = *startWordPosition <= *endWordPosition
+                                 ? std::move(startWordPosition)
+                                 : std::move(endWordPosition);
+    AXRange range(std::move(startPosition), std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityNextWordEndTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreateNextWordEndPosition(
@@ -2578,8 +2482,7 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityPreviousWordStartTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreatePreviousWordStartPosition(
@@ -2588,8 +2491,7 @@
 
   if ([attribute isEqualToString:
                      NSAccessibilityLineForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
 
@@ -2612,7 +2514,7 @@
       return nil;
 
     int lineStartOffset = lineStarts[lineIndex];
-    BrowserAccessibility::AXPosition lineStartPosition = CreateTextPosition(
+    AXPosition lineStartPosition = CreateTextPosition(
         *_owner, lineStartOffset, ax::mojom::TextAffinity::kDownstream);
     if (lineStartPosition->IsNullPosition())
       return nil;
@@ -2621,63 +2523,52 @@
     // current line.
     lineStartPosition = lineStartPosition->CreatePreviousLineStartPosition(
         ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-    BrowserAccessibility::AXPosition lineEndPosition =
-        lineStartPosition->CreateNextLineEndPosition(
-            ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
-    BrowserAccessibility::AXRange range(std::move(lineStartPosition),
-                                        std::move(lineEndPosition));
+    AXPosition lineEndPosition = lineStartPosition->CreateNextLineEndPosition(
+        ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
+    AXRange range(std::move(lineStartPosition), std::move(lineEndPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityLeftLineTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition endPosition =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition endPosition = AXTextMarkerToAXPosition(parameter);
     if (endPosition->IsNullPosition())
       return nil;
 
-    BrowserAccessibility::AXPosition startLinePosition =
-        endPosition->CreatePreviousLineStartPosition(
-            ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-    BrowserAccessibility::AXPosition endLinePosition =
-        endPosition->CreatePreviousLineEndPosition(
-            ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-    BrowserAccessibility::AXPosition startPosition =
-        *startLinePosition <= *endLinePosition ? std::move(endLinePosition)
-                                               : std::move(startLinePosition);
-    BrowserAccessibility::AXRange range(std::move(startPosition),
-                                        std::move(endPosition));
+    AXPosition startLinePosition = endPosition->CreatePreviousLineStartPosition(
+        ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+    AXPosition endLinePosition = endPosition->CreatePreviousLineEndPosition(
+        ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+    AXPosition startPosition = *startLinePosition <= *endLinePosition
+                                   ? std::move(endLinePosition)
+                                   : std::move(startLinePosition);
+    AXRange range(std::move(startPosition), std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityRightLineTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition startPosition =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition startPosition = AXTextMarkerToAXPosition(parameter);
     if (startPosition->IsNullPosition())
       return nil;
 
-    BrowserAccessibility::AXPosition startLinePosition =
-        startPosition->CreateNextLineStartPosition(
-            ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-    BrowserAccessibility::AXPosition endLinePosition =
-        startPosition->CreateNextLineEndPosition(
-            ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-    BrowserAccessibility::AXPosition endPosition =
-        *startLinePosition <= *endLinePosition ? std::move(startLinePosition)
-                                               : std::move(endLinePosition);
-    BrowserAccessibility::AXRange range(std::move(startPosition),
-                                        std::move(endPosition));
+    AXPosition startLinePosition = startPosition->CreateNextLineStartPosition(
+        ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+    AXPosition endLinePosition = startPosition->CreateNextLineEndPosition(
+        ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+    AXPosition endPosition = *startLinePosition <= *endLinePosition
+                                 ? std::move(startLinePosition)
+                                 : std::move(endLinePosition);
+    AXRange range(std::move(startPosition), std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityNextLineEndTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreateNextLineEndPosition(
@@ -2687,8 +2578,7 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityPreviousLineStartTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreatePreviousLineStartPosition(
@@ -2698,38 +2588,33 @@
   if ([attribute
           isEqualToString:
               NSAccessibilitySentenceTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
 
-    BrowserAccessibility::AXRange range =
-        position->ExpandToEnclosingTextBoundary(
-            ax::mojom::TextBoundary::kSentenceStartOrEnd,
-            ui::AXRangeExpandBehavior::kLeftFirst);
+    AXRange range = position->ExpandToEnclosingTextBoundary(
+        ax::mojom::TextBoundary::kSentenceStartOrEnd,
+        ui::AXRangeExpandBehavior::kLeftFirst);
     return CreateTextMarkerRange(std::move(range));
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityParagraphTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
 
-    BrowserAccessibility::AXRange range =
-        position->ExpandToEnclosingTextBoundary(
-            ax::mojom::TextBoundary::kParagraphStartOrEnd,
-            ui::AXRangeExpandBehavior::kLeftFirst);
+    AXRange range = position->ExpandToEnclosingTextBoundary(
+        ax::mojom::TextBoundary::kParagraphStartOrEnd,
+        ui::AXRangeExpandBehavior::kLeftFirst);
     return CreateTextMarkerRange(std::move(range));
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityNextParagraphEndTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreateNextParagraphEndPosition(
@@ -2739,8 +2624,7 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityPreviousParagraphStartTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreatePreviousParagraphStartPosition(
@@ -2750,19 +2634,15 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityStyleTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
 
-    BrowserAccessibility::AXPosition startPosition =
-        position->CreatePreviousFormatStartPosition(
-            ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-    BrowserAccessibility::AXPosition endPosition =
-        position->CreateNextFormatEndPosition(
-            ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-    BrowserAccessibility::AXRange range(std::move(startPosition),
-                                        std::move(endPosition));
+    AXPosition startPosition = position->CreatePreviousFormatStartPosition(
+        ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+    AXPosition endPosition = position->CreateNextFormatEndPosition(
+        ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+    AXRange range(std::move(startPosition), std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
@@ -2775,13 +2655,12 @@
 
   if ([attribute isEqualToString:
                      NSAccessibilityTextMarkerIsValidParameterizedAttribute]) {
-    return @(CreatePositionFromTextMarker(parameter)->IsNullPosition());
+    return @(AXTextMarkerToAXPosition(parameter)->IsNullPosition());
   }
 
   if ([attribute isEqualToString:
                      NSAccessibilityIndexForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
     return @(position->AsTextPosition()->text_offset());
@@ -2834,8 +2713,7 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityLineTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return nil;
 
@@ -2844,14 +2722,11 @@
     // the previous line. This is what Safari does.
     //
     // Note that hard line breaks are on a line of their own.
-    BrowserAccessibility::AXPosition startPosition =
-        position->CreatePreviousLineStartPosition(
-            ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-    BrowserAccessibility::AXPosition endPosition =
-        startPosition->CreateNextLineStartPosition(
-            ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-    BrowserAccessibility::AXRange range(std::move(startPosition),
-                                        std::move(endPosition));
+    AXPosition startPosition = position->CreatePreviousLineStartPosition(
+        ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+    AXPosition endPosition = startPosition->CreateNextLineStartPosition(
+        ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+    AXRange range(std::move(startPosition), std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
@@ -2861,8 +2736,7 @@
     BrowserAccessibility* startObject;
     BrowserAccessibility* endObject;
     int startOffset, endOffset;
-    BrowserAccessibility::AXRange range =
-        CreateRangeFromTextMarkerRange(parameter);
+    AXRange range = AXTextMarkerRangeToAXRange(parameter);
     if (range.IsNull())
       return nil;
 
@@ -2893,40 +2767,37 @@
     if ([textMarkerArray count] != 2)
       return nil;
 
-    BrowserAccessibility::AXPosition startPosition =
-        CreatePositionFromTextMarker([textMarkerArray objectAtIndex:0]);
-    BrowserAccessibility::AXPosition endPosition =
-        CreatePositionFromTextMarker([textMarkerArray objectAtIndex:1]);
+    AXPosition startPosition =
+        AXTextMarkerToAXPosition([textMarkerArray objectAtIndex:0]);
+    AXPosition endPosition =
+        AXTextMarkerToAXPosition([textMarkerArray objectAtIndex:1]);
     if (*startPosition <= *endPosition) {
-      return CreateTextMarkerRange(BrowserAccessibility::AXRange(
-          std::move(startPosition), std::move(endPosition)));
+      return CreateTextMarkerRange(
+          AXRange(std::move(startPosition), std::move(endPosition)));
     } else {
-      return CreateTextMarkerRange(BrowserAccessibility::AXRange(
-          std::move(endPosition), std::move(startPosition)));
+      return CreateTextMarkerRange(
+          AXRange(std::move(endPosition), std::move(startPosition)));
     }
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityTextMarkerDebugDescriptionParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     return base::SysUTF8ToNSString(position->ToString());
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityTextMarkerRangeDebugDescriptionParameterizedAttribute]) {
-    BrowserAccessibility::AXRange range =
-        CreateRangeFromTextMarkerRange(parameter);
+    AXRange range = AXTextMarkerRangeToAXRange(parameter);
     return base::SysUTF8ToNSString(range.ToString());
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityTextMarkerNodeDebugDescriptionParameterizedAttribute]) {
-    BrowserAccessibility::AXPosition position =
-        CreatePositionFromTextMarker(parameter);
+    AXPosition position = AXTextMarkerToAXPosition(parameter);
     if (position->IsNullPosition())
       return @"nil";
     DCHECK(position->GetAnchor());
@@ -3464,13 +3335,12 @@
   }
   if ([attribute
           isEqualToString:NSAccessibilitySelectedTextMarkerRangeAttribute]) {
-    BrowserAccessibility::AXRange range = CreateRangeFromTextMarkerRange(value);
+    AXRange range = AXTextMarkerRangeToAXRange(value);
     if (range.IsNull())
       return;
     BrowserAccessibilityManager* manager = _owner->manager();
-    manager->SetSelection(BrowserAccessibility::AXRange(
-        range.anchor()->AsTextSelectionPosition(),
-        range.focus()->AsTextSelectionPosition()));
+    manager->SetSelection(AXRange(range.anchor()->AsTextSelectionPosition(),
+                                  range.focus()->AsTextSelectionPosition()));
   }
 }
 
diff --git a/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm b/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm
index d60a9f2..050c797a 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm
@@ -22,6 +22,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
 #include "ui/accessibility/platform/ax_private_webkit_constants_mac.h"
+#include "ui/accessibility/platform/ax_utils_mac.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -125,9 +126,10 @@
   AXTextEdit text_edit = [cocoa_text_field computeTextEdit];
   EXPECT_NE(text_edit.edit_text_marker, nil);
 
-  EXPECT_EQ(AXTextMarkerToAXPosition(text_edit.edit_text_marker)->ToString(),
-            "TextPosition anchor_id=4 text_offset=1 affinity=downstream "
-            "annotated_text=B<>");
+  EXPECT_EQ(
+      ui::AXTextMarkerToAXPosition(text_edit.edit_text_marker)->ToString(),
+      "TextPosition anchor_id=4 text_offset=1 affinity=downstream "
+      "annotated_text=B<>");
 }
 
 IN_PROC_BROWSER_TEST_F(BrowserAccessibilityCocoaBrowserTest,
diff --git a/content/browser/accessibility/browser_accessibility_fuchsia.cc b/content/browser/accessibility/browser_accessibility_fuchsia.cc
index b4c4adb7..d050348 100644
--- a/content/browser/accessibility/browser_accessibility_fuchsia.cc
+++ b/content/browser/accessibility/browser_accessibility_fuchsia.cc
@@ -88,9 +88,14 @@
 
 std::vector<uint32_t> BrowserAccessibilityFuchsia::GetFuchsiaChildIDs() const {
   std::vector<uint32_t> child_ids;
-  for (const BrowserAccessibility& child : PlatformChildren()) {
-    child_ids.push_back(static_cast<const BrowserAccessibilityFuchsia&>(child)
-                            .GetFuchsiaNodeID());
+
+  // TODO(abrusher): Switch back to using platform children.
+  for (const auto* child : AllChildren()) {
+    const BrowserAccessibilityFuchsia* fuchsia_child =
+        static_cast<const BrowserAccessibilityFuchsia*>(child);
+    DCHECK(fuchsia_child);
+
+    child_ids.push_back(fuchsia_child->GetFuchsiaNodeID());
   }
 
   return child_ids;
diff --git a/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc b/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc
index 7031fb1..f746af5 100644
--- a/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc
@@ -585,6 +585,91 @@
   EXPECT_EQ(fuchsia_node_data.child_ids()[1], child_2->GetFuchsiaNodeID());
 }
 
+TEST_F(BrowserAccessibilityFuchsiaTest, ChildTree) {
+  // Create a child tree with multiple nodes.
+  ui::AXNodeData node;
+  node.id = 1;
+  node.child_ids = {2, 3};
+  ui::AXNodeData node_2;
+  node_2.id = 2;
+  ui::AXNodeData node_3;
+  node_3.id = 3;
+  std::unique_ptr<BrowserAccessibilityManager> child_manager(
+      BrowserAccessibilityManager::Create(
+          MakeAXTreeUpdate(node, node_2, node_3),
+          test_browser_accessibility_delegate_.get()));
+
+  // Create a parent tree that points to the child tree.
+  ui::AXNodeData node_4;
+  node_4.id = 4;
+  node_4.child_ids = {5};
+  ui::AXNodeData node_5;
+  node_5.id = 5;
+  node_5.AddChildTreeId(child_manager->ax_tree_id());
+  std::unique_ptr<BrowserAccessibilityManager> parent_manager(
+      BrowserAccessibilityManager::Create(
+          MakeAXTreeUpdate(node_4, node_5),
+          test_browser_accessibility_delegate_.get()));
+
+  // Update the child tree's parent tree ID.
+  ui::AXTreeData updated_data = child_manager->GetTreeData();
+  updated_data.parent_tree_id = parent_manager->ax_tree_id();
+  child_manager->ax_tree()->UpdateDataForTesting(updated_data);
+
+  // Get the parent node that points to the child tree.
+  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
+      ToBrowserAccessibilityFuchsia(parent_manager->GetFromID(5));
+
+  {
+    ASSERT_TRUE(browser_accessibility_fuchsia);
+    fuchsia::accessibility::semantics::Node fuchsia_node_data =
+        browser_accessibility_fuchsia->ToFuchsiaNodeData();
+
+    // Get the root of the child tree to verify that it's present in the parent
+    // node's children.
+    BrowserAccessibilityFuchsia* child_root =
+        ToBrowserAccessibilityFuchsia(child_manager->GetRoot());
+
+    ASSERT_EQ(fuchsia_node_data.child_ids().size(), 1u);
+    EXPECT_EQ(fuchsia_node_data.child_ids()[0], child_root->GetFuchsiaNodeID());
+  }
+
+  // Destroy the child tree, and ensure that the parent fuchsia node's child IDs
+  // no longer reference it.
+  child_manager.reset();
+
+  {
+    ASSERT_TRUE(browser_accessibility_fuchsia);
+    fuchsia::accessibility::semantics::Node fuchsia_node_data =
+        browser_accessibility_fuchsia->ToFuchsiaNodeData();
+
+    EXPECT_TRUE(fuchsia_node_data.child_ids().empty());
+  }
+}
+
+TEST_F(BrowserAccessibilityFuchsiaTest, ChildTreeMissing) {
+  // Create a parent tree that points to a non-existent child tree.
+  ui::AXNodeData node_4;
+  node_4.id = 4;
+  node_4.child_ids = {5};
+  ui::AXNodeData node_5;
+  node_5.id = 5;
+  node_5.AddChildTreeId(ui::AXTreeID::CreateNewAXTreeID());
+  std::unique_ptr<BrowserAccessibilityManager> parent_manager(
+      BrowserAccessibilityManager::Create(
+          MakeAXTreeUpdate(node_4, node_5),
+          test_browser_accessibility_delegate_.get()));
+
+  // Get the parent node that points to the child tree.
+  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
+      ToBrowserAccessibilityFuchsia(parent_manager->GetFromID(5));
+
+  ASSERT_TRUE(browser_accessibility_fuchsia);
+  auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
+
+  EXPECT_TRUE(fuchsia_node_data.child_ids().empty());
+}
+
 TEST_F(BrowserAccessibilityFuchsiaTest, GetFuchsiaNodeIDNonRootTree) {
   // We want to verify that the root of a non-root tree will NOT be assigned ID
   // = 0, so Specify that this tree is not the root.
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.cc b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
index ee90cd4..47496f2 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.cc
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
@@ -18,7 +18,6 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "content/browser/accessibility/accessibility_event_recorder.h"
 #include "content/browser/accessibility/browser_accessibility.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
diff --git a/content/browser/attribution_reporting/attribution_host.cc b/content/browser/attribution_reporting/attribution_host.cc
index 64bc7e7..e7599468 100644
--- a/content/browser/attribution_reporting/attribution_host.cc
+++ b/content/browser/attribution_reporting/attribution_host.cc
@@ -271,12 +271,20 @@
 
   // Only allow conversion registration on secure pages with a secure conversion
   // redirects.
-  if (!network::IsOriginPotentiallyTrustworthy(conversion_origin) ||
-      !network::IsOriginPotentiallyTrustworthy(conversion->reporting_origin) ||
-      !network::IsOriginPotentiallyTrustworthy(main_frame_origin)) {
+  if (!network::IsOriginPotentiallyTrustworthy(conversion_origin)) {
     mojo::ReportBadMessage(
-        "blink.mojom.ConversionHost can only be used in secure contexts with a "
-        "secure conversion registration origin.");
+        "blink.mojom.ConversionHost can only be used in secure contexts.");
+        return;
+  }
+  if (!network::IsOriginPotentiallyTrustworthy(conversion->reporting_origin)) {
+    mojo::ReportBadMessage("blink.mojom.ConversionHost can only be used with "
+        "a secure conversion registration origin.");
+        return;
+  }
+  if (!network::IsOriginPotentiallyTrustworthy(main_frame_origin)) {
+    mojo::ReportBadMessage(
+        "blink.mojom.ConversionHost can only be used with a secure top-level "
+        "frame.");
     return;
   }
 
diff --git a/content/browser/attribution_reporting/attribution_host_unittest.cc b/content/browser/attribution_reporting/attribution_host_unittest.cc
index 9cb9afd..f87cde8f 100644
--- a/content/browser/attribution_reporting/attribution_host_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_host_unittest.cc
@@ -202,8 +202,8 @@
       url::Origin::Create(GURL("https://secure.com"));
   conversion_host_mojom()->RegisterConversion(std::move(conversion));
   EXPECT_EQ(
-      "blink.mojom.ConversionHost can only be used in secure contexts with a "
-      "secure conversion registration origin.",
+      "blink.mojom.ConversionHost can only be used with a secure top-level "
+      "frame.",
       bad_message_observer.WaitForBadMessage());
 }
 
@@ -261,8 +261,7 @@
   // Message should be ignored because it was registered from an insecure page.
   conversion_host_mojom()->RegisterConversion(std::move(conversion));
   EXPECT_EQ(
-      "blink.mojom.ConversionHost can only be used in secure contexts with a "
-      "secure conversion registration origin.",
+      "blink.mojom.ConversionHost can only be used in secure contexts.",
       bad_message_observer.WaitForBadMessage());
 }
 
@@ -281,8 +280,8 @@
   // redirect.
   conversion_host_mojom()->RegisterConversion(std::move(conversion));
   EXPECT_EQ(
-      "blink.mojom.ConversionHost can only be used in secure contexts with a "
-      "secure conversion registration origin.",
+      "blink.mojom.ConversionHost can only be used with a secure conversion "
+      "registration origin.",
       bad_message_observer.WaitForBadMessage());
 }
 
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 64dce16..326279ad 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -84,6 +84,10 @@
 
 namespace content {
 
+using NotStoredReasons =
+    BackForwardCacheCanStoreDocumentResult::NotStoredReasons;
+using NotRestoredReason = BackForwardCacheMetrics::NotRestoredReason;
+
 namespace {
 
 class DOMContentLoadedObserver : public WebContentsObserver {
@@ -2471,6 +2475,114 @@
   }
 }
 
+testing::Matcher<BackForwardCacheCanStoreTreeResult> MatchesTreeResult(
+    testing::Matcher<bool> same_origin,
+    GURL url) {
+  return testing::AllOf(
+      testing::Property("IsSameOrigin",
+                        &BackForwardCacheCanStoreTreeResult::IsSameOrigin,
+                        same_origin),
+      testing::Property("GetUrl", &BackForwardCacheCanStoreTreeResult::GetUrl,
+                        url));
+}
+
+RenderFrameHostImpl* ChildFrame(RenderFrameHostImpl* rfh, int child_index) {
+  return rfh->child_at(child_index)->current_frame_host();
+}
+
+// Verifies that the reasons match those given and no others.
+testing::Matcher<BackForwardCacheCanStoreDocumentResult> MatchesDocumentResult(
+    testing::Matcher<NotStoredReasons> not_stored,
+    BlockListedFeatures block_listed) {
+  return testing::AllOf(
+      testing::Property(
+          "not_stored_reasons",
+          &BackForwardCacheCanStoreDocumentResult::not_stored_reasons,
+          not_stored),
+      testing::Property(
+          "blocklisted_features",
+          &BackForwardCacheCanStoreDocumentResult::blocklisted_features,
+          block_listed),
+      testing::Property(
+          "disabled_reasons",
+          &BackForwardCacheCanStoreDocumentResult::disabled_reasons,
+          std::set<BackForwardCache::DisabledReason>()),
+      testing::Property(
+          "disallow_activation_reasons",
+          &BackForwardCacheCanStoreDocumentResult::disallow_activation_reasons,
+          std::set<uint64_t>()));
+}
+
+// Check the contents of the BackForwardCacheCanStoreTreeResult of a page.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, TreeResult1) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(a, b, c)"));
+
+  // 1) Navigate to a(a, b, c).
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh = current_frame_host();
+
+  // 2) Add a blocking feature to the main frame A and the sub frame B.
+  current_frame_host()
+      ->UseDummyStickyBackForwardCacheDisablingFeatureForTesting();
+  current_frame_host()
+      ->child_at(1)
+      ->current_frame_host()
+      ->UseDummyStickyBackForwardCacheDisablingFeatureForTesting();
+
+  // 3) Initialize the reasons tree.
+  BackForwardCacheCanStoreDocumentResultWithTree can_store_result =
+      web_contents()->GetController().GetBackForwardCache().CanStorePageNow(
+          rfh);
+
+  // 4) Check IsSameOrigin() and GetUrl().
+  // a
+  EXPECT_THAT(*can_store_result.tree_reasons,
+              MatchesTreeResult(/*same_origin=*/true,
+                                /*url=*/rfh->GetLastCommittedURL()));
+  // a->a
+  EXPECT_THAT(
+      *can_store_result.tree_reasons->GetChildren().at(0),
+      MatchesTreeResult(/*same_origin=*/true,
+                        /*url=*/ChildFrame(rfh, 0)->GetLastCommittedURL()));
+  // a->b
+  EXPECT_THAT(
+      *can_store_result.tree_reasons->GetChildren().at(1),
+      MatchesTreeResult(/*same_origin=*/false,
+                        /*url=*/ChildFrame(rfh, 1)->GetLastCommittedURL()));
+  // a->c
+  EXPECT_THAT(
+      *can_store_result.tree_reasons->GetChildren().at(2),
+      MatchesTreeResult(/*same_origin=*/false,
+                        /*url=*/ChildFrame(rfh, 2)->GetLastCommittedURL()));
+
+  // 5) Check that the blocking reasons match.
+  // a
+  EXPECT_THAT(can_store_result.tree_reasons->GetDocumentResult(),
+              MatchesDocumentResult(
+                  NotStoredReasons(NotRestoredReason::kBlocklistedFeatures),
+                  BlockListedFeatures(
+                      blink::scheduler::WebSchedulerTrackedFeature::kDummy)));
+  // a->a
+  EXPECT_THAT(
+      can_store_result.tree_reasons->GetChildren().at(0)->GetDocumentResult(),
+      MatchesDocumentResult(NotStoredReasons(),
+                            BlockListedFeatures(BlockListedFeatures())));
+  // a->b
+  EXPECT_THAT(
+      can_store_result.tree_reasons->GetChildren().at(1)->GetDocumentResult(),
+      MatchesDocumentResult(
+          NotStoredReasons(NotRestoredReason::kBlocklistedFeatures),
+          BlockListedFeatures(
+              blink::scheduler::WebSchedulerTrackedFeature::kDummy)));
+  // a->c
+  EXPECT_THAT(
+      can_store_result.tree_reasons->GetChildren().at(2)->GetDocumentResult(),
+      MatchesDocumentResult(NotStoredReasons(),
+                            BlockListedFeatures(BlockListedFeatures())));
+}
+
 class BackForwardCacheOptInBrowserTest : public BackForwardCacheBrowserTest {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -2753,12 +2865,12 @@
       base::BindLambdaForTesting([&](RenderFrameHost*) {
         // 5) Test that page cannot be stored in bfcache when subframe is
         // pending commit.
-        BackForwardCacheCanStoreDocumentResult can_store_result =
+        BackForwardCacheCanStoreDocumentResultWithTree can_store_result =
             web_contents()
                 ->GetController()
                 .GetBackForwardCache()
                 .CanStorePageNow(static_cast<RenderFrameHostImpl*>(main_frame));
-        EXPECT_TRUE(can_store_result.HasNotStoredReason(
+        EXPECT_TRUE(can_store_result.flattened_reasons.HasNotStoredReason(
             BackForwardCacheMetrics::NotRestoredReason::kSubframeIsNavigating));
       }));
 
diff --git a/content/browser/back_forward_cache_no_store_browsertest.cc b/content/browser/back_forward_cache_no_store_browsertest.cc
index b6f38f3..a68aebc 100644
--- a/content/browser/back_forward_cache_no_store_browsertest.cc
+++ b/content/browser/back_forward_cache_no_store_browsertest.cc
@@ -58,7 +58,7 @@
   delete_observer_rfh_a.WaitUntilDeleted();
 }
 
-// Flaky. See crbug.com/1116190.
+// Disabled for being flaky. See crbug.com/1116190.
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, SubframeWithNoStoreCached) {
   // iframe will try to load title1.html.
   net::test_server::ControllableHttpResponse response(embedded_test_server(),
@@ -105,8 +105,16 @@
 // TODO(https://crbug.com/1231849): flaky on Cast Linux.
 // Test that a page with cache-control:no-store enters bfcache with the flag on,
 // but does not get restored and gets evicted.
+// Turned off on cast for https://crbug.com/1281665 , along with others.
+#if BUILDFLAG(IS_CHROMECAST)
+#define MAYBE_PagesWithCacheControlNoStoreEnterBfcacheAndEvicted \
+        DISABLED_PagesWithCacheControlNoStoreEnterBfcacheAndEvicted
+#else
+#define MAYBE_PagesWithCacheControlNoStoreEnterBfcacheAndEvicted \
+        PagesWithCacheControlNoStoreEnterBfcacheAndEvicted
+#endif
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestAllowCacheControlNoStore,
-                       PagesWithCacheControlNoStoreEnterBfcacheAndEvicted) {
+                       MAYBE_PagesWithCacheControlNoStoreEnterBfcacheAndEvicted) {
   net::test_server::ControllableHttpResponse response(embedded_test_server(),
                                                       "/main_document");
   net::test_server::ControllableHttpResponse response2(embedded_test_server(),
@@ -145,9 +153,17 @@
 // Test that a page with cache-control:no-store enters bfcache with the flag on,
 // and if a cookie is modified while it is in bfcache via JavaScript, gets
 // evicted with cookie modified marked.
+// Turned off on cast for https://crbug.com/1281665 .
+#if BUILDFLAG(IS_CHROMECAST)
+#define MAYBE_PagesWithCacheControlNoStoreCookieModifiedThroughJavaScript \
+        DISABLED_PagesWithCacheControlNoStoreCookieModifiedThroughJavaScript
+#else
+#define MAYBE_PagesWithCacheControlNoStoreCookieModifiedThroughJavaScript \
+        PagesWithCacheControlNoStoreCookieModifiedThroughJavaScript
+#endif
 IN_PROC_BROWSER_TEST_F(
     BackForwardCacheBrowserTestAllowCacheControlNoStore,
-    PagesWithCacheControlNoStoreCookieModifiedThroughJavaScript) {
+    MAYBE_PagesWithCacheControlNoStoreCookieModifiedThroughJavaScript) {
   net::test_server::ControllableHttpResponse response(embedded_test_server(),
                                                       "/main_document");
   net::test_server::ControllableHttpResponse response2(embedded_test_server(),
@@ -199,13 +215,21 @@
                     {}, {}, {}, {}, FROM_HERE);
 }
 
-// Flaky on Cast Audio Linux https://crbug.com/1229182
+// Disabled due to flakiness on Cast Audio Linux https://crbug.com/1229182
+#if BUILDFLAG(IS_CHROMECAST)
+#define MAYBE_PagesWithCacheControlNoStoreCookieModifiedBackTwice \
+  DISABLED_PagesWithCacheControlNoStoreCookieModifiedBackTwice
+#else
+#define MAYBE_PagesWithCacheControlNoStoreCookieModifiedBackTwice \
+  PagesWithCacheControlNoStoreCookieModifiedBackTwice
+#endif
 // Test that a page with cache-control:no-store enters bfcache with the flag on,
 // and if a cookie is modified, it gets evicted with cookie changed, but if
 // navigated away again and navigated back, it gets evicted without cookie
 // change marked.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestAllowCacheControlNoStore,
-                       PagesWithCacheControlNoStoreCookieModifiedBackTwice) {
+IN_PROC_BROWSER_TEST_F(
+    BackForwardCacheBrowserTestAllowCacheControlNoStore,
+    MAYBE_PagesWithCacheControlNoStoreCookieModifiedBackTwice) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url_a(embedded_test_server()->GetURL(
@@ -260,9 +284,17 @@
 // Test that a page with cache-control:no-store enters bfcache with the flag on,
 // and even if a cookie is modified on a different domain than the entry, the
 // entry is not marked as cookie modified.
+// Turned off on cast for https://crbug.com/1281665 .
+#if BUILDFLAG(IS_CHROMECAST)
+#define MAYBE_PagesWithCacheControlNoStoreCookieModifiedThroughJavaScriptOnDifferentDomain \
+        DISABLED_PagesWithCacheControlNoStoreCookieModifiedThroughJavaScriptOnDifferentDomain
+#else
+#define MAYBE_PagesWithCacheControlNoStoreCookieModifiedThroughJavaScriptOnDifferentDomain \
+        PagesWithCacheControlNoStoreCookieModifiedThroughJavaScriptOnDifferentDomain
+#endif
 IN_PROC_BROWSER_TEST_F(
     BackForwardCacheBrowserTestAllowCacheControlNoStore,
-    PagesWithCacheControlNoStoreCookieModifiedThroughJavaScriptOnDifferentDomain) {
+    MAYBE_PagesWithCacheControlNoStoreCookieModifiedThroughJavaScriptOnDifferentDomain) {
   net::test_server::ControllableHttpResponse response(embedded_test_server(),
                                                       "/main_document");
   net::test_server::ControllableHttpResponse response2(embedded_test_server(),
@@ -403,11 +435,20 @@
     "The server speaks HTTP!";
 }  // namespace
 
+// Disabled due to flakiness on Cast Audio Linux https://crbug.com/1229182
+#if BUILDFLAG(IS_CHROMECAST)
+#define MAYBE_PagesWithCacheControlNoStoreSetFromResponseHeader \
+  DISABLED_PagesWithCacheControlNoStoreSetFromResponseHeader
+#else
+#define MAYBE_PagesWithCacheControlNoStoreSetFromResponseHeader \
+  PagesWithCacheControlNoStoreSetFromResponseHeader
+#endif
 // Test that a page with cache-control:no-store enters bfcache with the flag on,
 // and if a cookie is modified while it is in bfcache via response header, gets
 // evicted with cookie modified marked.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestAllowCacheControlNoStore,
-                       PagesWithCacheControlNoStoreSetFromResponseHeader) {
+IN_PROC_BROWSER_TEST_F(
+    BackForwardCacheBrowserTestAllowCacheControlNoStore,
+    MAYBE_PagesWithCacheControlNoStoreSetFromResponseHeader) {
   net::test_server::ControllableHttpResponse response(embedded_test_server(),
                                                       "/main_document");
   net::test_server::ControllableHttpResponse response2(embedded_test_server(),
@@ -457,13 +498,20 @@
                     {}, {}, {}, {}, FROM_HERE);
 }
 
-// Flaky on Cast Audio Linux https://crbug.com/1229182
+// Disabled due to flakiness on Cast Audio Linux https://crbug.com/1229182
+#if BUILDFLAG(IS_CHROMECAST)
+#define MAYBE_PagesWithCacheControlNoStoreSetFromResponseHeaderHTTPOnlyCookie \
+  DISABLED_PagesWithCacheControlNoStoreSetFromResponseHeaderHTTPOnlyCookie
+#else
+#define MAYBE_PagesWithCacheControlNoStoreSetFromResponseHeaderHTTPOnlyCookie \
+  PagesWithCacheControlNoStoreSetFromResponseHeaderHTTPOnlyCookie
+#endif
 // Test that a page with cache-control:no-store enters bfcache with the flag on,
 // and if HTTPOnly cookie is modified while it is in bfcache, gets evicted with
 // HTTPOnly cookie modified marked.
 IN_PROC_BROWSER_TEST_F(
     BackForwardCacheBrowserTestAllowCacheControlNoStore,
-    PagesWithCacheControlNoStoreSetFromResponseHeaderHTTPOnlyCookie) {
+    MAYBE_PagesWithCacheControlNoStoreSetFromResponseHeaderHTTPOnlyCookie) {
   // HTTPOnly cookie can be only set over HTTPS.
   CreateHttpsServer();
   net::test_server::ControllableHttpResponse response(https_server(),
@@ -517,14 +565,21 @@
                     {}, {}, {}, {}, FROM_HERE);
 }
 
-// Flaky on Cast Audio Linux https://crbug.com/1229182
+// Disabled due to flakiness on Cast Audio Linux https://crbug.com/1229182
+#if BUILDFLAG(IS_CHROMECAST)
+#define MAYBE_PagesWithCacheControlNoStoreHTTPOnlyCookieModifiedBackTwice \
+  DISABLED_PagesWithCacheControlNoStoreHTTPOnlyCookieModifiedBackTwice
+#else
+#define MAYBE_PagesWithCacheControlNoStoreHTTPOnlyCookieModifiedBackTwice \
+  PagesWithCacheControlNoStoreHTTPOnlyCookieModifiedBackTwice
+#endif
 // Test that a page with cache-control:no-store enters bfcache with the flag on,
 // and if a HTTPOnly cookie is modified, it gets evicted with cookie changed,
 // but if navigated away again and navigated back, it gets evicted without
 // HTTPOnly cookie change marked.
 IN_PROC_BROWSER_TEST_F(
     BackForwardCacheBrowserTestAllowCacheControlNoStore,
-    PagesWithCacheControlNoStoreHTTPOnlyCookieModifiedBackTwice) {
+    MAYBE_PagesWithCacheControlNoStoreHTTPOnlyCookieModifiedBackTwice) {
   CreateHttpsServer();
   net::test_server::ControllableHttpResponse response(https_server(),
                                                       "/main_document");
@@ -609,9 +664,16 @@
 // TODO(https://crbug.com/1231849): flaky on Cast Linux.
 // Test that a page with cache-control:no-store enters bfcache with the flag on,
 // and gets restored if cookies do not change.
+#if BUILDFLAG(IS_CHROMECAST)
+#define MAYBE_PagesWithCacheControlNoStoreRestoreFromBackForwardCache \
+  DISABLED_PagesWithCacheControlNoStoreRestoreFromBackForwardCache
+#else
+#define MAYBE_PagesWithCacheControlNoStoreRestoreFromBackForwardCache \
+  PagesWithCacheControlNoStoreRestoreFromBackForwardCache
+#endif
 IN_PROC_BROWSER_TEST_F(
     BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
-    PagesWithCacheControlNoStoreRestoreFromBackForwardCache) {
+    MAYBE_PagesWithCacheControlNoStoreRestoreFromBackForwardCache) {
   net::test_server::ControllableHttpResponse response(embedded_test_server(),
                                                       "/main_document");
   net::test_server::ControllableHttpResponse response2(embedded_test_server(),
@@ -642,9 +704,17 @@
 // Flaky on Cast: crbug.com/1229182
 // Test that a page with cache-control:no-store enters bfcache with the flag on,
 // but gets evicted if cookies change.
+// Turned off on cast for https://crbug.com/1281665 .
+#if BUILDFLAG(IS_CHROMECAST)
+#define MAYBE_PagesWithCacheControlNoStoreEvictedIfCookieChange \
+        DISABLED_PagesWithCacheControlNoStoreEvictedIfCookieChange
+#else
+#define MAYBE_PagesWithCacheControlNoStoreEvictedIfCookieChange \
+        PagesWithCacheControlNoStoreEvictedIfCookieChange
+#endif
 IN_PROC_BROWSER_TEST_F(
     BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
-    PagesWithCacheControlNoStoreEvictedIfCookieChange) {
+    MAYBE_PagesWithCacheControlNoStoreEvictedIfCookieChange) {
   net::test_server::ControllableHttpResponse response(embedded_test_server(),
                                                       "/main_document");
   net::test_server::ControllableHttpResponse response2(embedded_test_server(),
@@ -697,12 +767,20 @@
 }
 
 // TODO(https://crbug.com/1231849): flaky on Cast Linux.
+#if defined(OS_LINUX)
+#define MAYBE_PagesWithCacheControlNoStoreEvictedWithBothCookieReasons \
+  DISABLED_PagesWithCacheControlNoStoreEvictedWithBothCookieReasons
+#else
+#define MAYBE_PagesWithCacheControlNoStoreEvictedWithBothCookieReasons \
+  PagesWithCacheControlNoStoreEvictedWithBothCookieReasons
+#endif
+
 // Test that a page with cache-control:no-store enters bfcache with the flag on,
 // and gets evicted with both JavaScript and HTTPOnly cookie changes. Only
 // HTTPOnly cookie reason should be recorded.
 IN_PROC_BROWSER_TEST_F(
     BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
-    PagesWithCacheControlNoStoreEvictedWithBothCookieReasons) {
+    MAYBE_PagesWithCacheControlNoStoreEvictedWithBothCookieReasons) {
   CreateHttpsServer();
   net::test_server::ControllableHttpResponse response(https_server(),
                                                       "/main_document");
@@ -768,11 +846,18 @@
 };
 
 // TODO(https://crbug.com/1231849): flaky on Cast Linux.
+#if defined(OS_LINUX)
+#define MAYBE_NoCacheControlNoStoreButHTTPOnlyCookieChange \
+  DISABLED_NoCacheControlNoStoreButHTTPOnlyCookieChange
+#else
+#define MAYBE_NoCacheControlNoStoreButHTTPOnlyCookieChange \
+  NoCacheControlNoStoreButHTTPOnlyCookieChange
+#endif
 // Test that a page without cache-control:no-store can enter BackForwardCache
 // and gets restored if HTTPOnly Cookie changes.
 IN_PROC_BROWSER_TEST_F(
     BackForwardCacheBrowserTestRestoreUnlessHTTPOnlyCookieChange,
-    NoCacheControlNoStoreButHTTPOnlyCookieChange) {
+    MAYBE_NoCacheControlNoStoreButHTTPOnlyCookieChange) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url_a(embedded_test_server()->GetURL(
@@ -803,11 +888,18 @@
 }
 
 // TODO(https://crbug.com/1231849): flaky on Cast Linux.
+#if defined(OS_LINUX)
+#define MAYBE_PagesWithCacheControlNoStoreNotEvictedIfNormalCookieChange \
+  DISABLED_PagesWithCacheControlNoStoreNotEvictedIfNormalCookieChange
+#else
+#define MAYBE_PagesWithCacheControlNoStoreNotEvictedIfNormalCookieChange \
+  PagesWithCacheControlNoStoreNotEvictedIfNormalCookieChange
+#endif
 // Test that a page with cache-control:no-store enters bfcache with the flag on,
 // and does not get evicted if normal cookies change.
 IN_PROC_BROWSER_TEST_F(
     BackForwardCacheBrowserTestRestoreUnlessHTTPOnlyCookieChange,
-    PagesWithCacheControlNoStoreNotEvictedIfNormalCookieChange) {
+    MAYBE_PagesWithCacheControlNoStoreNotEvictedIfNormalCookieChange) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url_a(embedded_test_server()->GetURL(
@@ -844,11 +936,18 @@
 }
 
 // TODO(https://crbug.com/1231849): flaky on Cast Linux.
+#if defined(OS_LINUX)
+#define MAYBE_PagesWithCacheControlNoStoreEvictedIfHTTPOnlyCookieChange \
+  DISABLED_PagesWithCacheControlNoStoreEvictedIfHTTPOnlyCookieChange
+#else
+#define MAYBE_PagesWithCacheControlNoStoreEvictedIfHTTPOnlyCookieChange \
+  PagesWithCacheControlNoStoreEvictedIfHTTPOnlyCookieChange
+#endif
 // Test that a page with cache-control:no-store enters bfcache with the flag on,
 // and gets evicted if HTTPOnly cookie changes.
 IN_PROC_BROWSER_TEST_F(
     BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
-    PagesWithCacheControlNoStoreEvictedIfHTTPOnlyCookieChange) {
+    MAYBE_PagesWithCacheControlNoStoreEvictedIfHTTPOnlyCookieChange) {
   CreateHttpsServer();
   net::test_server::ControllableHttpResponse response(https_server(),
                                                       "/main_document");
@@ -900,11 +999,18 @@
 }
 
 // TODO(https://crbug.com/1231849): flaky on Cast Linux.
+#if defined(OS_LINUX)
+#define MAYBE_PagesWithCacheControlNoStoreEvictedIfJSAndHTTPOnlyCookieChange \
+  DISABLED_PagesWithCacheControlNoStoreEvictedIfJSAndHTTPOnlyCookieChange
+#else
+#define MAYBE_PagesWithCacheControlNoStoreEvictedIfJSAndHTTPOnlyCookieChange \
+  PagesWithCacheControlNoStoreEvictedIfJSAndHTTPOnlyCookieChange
+#endif
 // Test that a page with cache-control:no-store enters bfcache with the flag on,
 // and gets evicted if HTTPOnly cookie changes.
 IN_PROC_BROWSER_TEST_F(
     BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
-    PagesWithCacheControlNoStoreEvictedIfJSAndHTTPOnlyCookieChange) {
+    MAYBE_PagesWithCacheControlNoStoreEvictedIfJSAndHTTPOnlyCookieChange) {
   CreateHttpsServer();
   net::test_server::ControllableHttpResponse response(https_server(),
                                                       "/main_document");
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 9f5eef8a..7728866 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -188,6 +188,11 @@
 #include "third_party/blink/public/mojom/input/text_input_host.mojom.h"
 #endif
 
+#if defined(OS_CHROMEOS)
+#include "content/browser/lock_screen/lock_screen_service_impl.h"
+#include "third_party/blink/public/mojom/lock_screen/lock_screen.mojom.h"
+#endif  // defined(OS_CHROMEOS)
+
 namespace blink {
 class StorageKey;
 }  // namespace blink
@@ -1096,6 +1101,13 @@
       base::BindRepeating(&SpeculationHostImpl::Bind));
   GetContentClient()->browser()->RegisterBrowserInterfaceBindersForFrame(host,
                                                                          map);
+
+#if defined(OS_CHROMEOS)
+  if (base::FeatureList::IsEnabled(features::kWebLockScreenApi)) {
+    map->Add<blink::mojom::LockScreenService>(
+        base::BindRepeating(&LockScreenServiceImpl::Create));
+  }
+#endif
 }
 
 void PopulateBinderMap(RenderFrameHostImpl* host, mojo::BinderMap* map) {
diff --git a/content/browser/child_process_launcher_helper_fuchsia.cc b/content/browser/child_process_launcher_helper_fuchsia.cc
index 9aeb7b5..57bfae1 100644
--- a/content/browser/child_process_launcher_helper_fuchsia.cc
+++ b/content/browser/child_process_launcher_helper_fuchsia.cc
@@ -27,6 +27,8 @@
       return "utility";
     case sandbox::mojom::Sandbox::kService:
       return "service";
+    case sandbox::mojom::Sandbox::kServiceWithJit:
+      return "service-with-jit";
     case sandbox::mojom::Sandbox::kGpu:
       return "gpu";
     case sandbox::mojom::Sandbox::kNetwork:
diff --git a/content/browser/interest_group/auction_process_manager.cc b/content/browser/interest_group/auction_process_manager.cc
index cb76d45..20365428 100644
--- a/content/browser/interest_group/auction_process_manager.cc
+++ b/content/browser/interest_group/auction_process_manager.cc
@@ -12,7 +12,9 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/timer/timer.h"
+#include "build/build_config.h"
 #include "content/public/browser/service_process_host.h"
+#include "content/public/common/child_process_host.h"
 #include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -181,7 +183,13 @@
     const std::string& display_name) {
   content::ServiceProcessHost::Launch(
       std::move(auction_worklet_service_receiver),
-      ServiceProcessHost::Options().WithDisplayName(display_name).Pass());
+      ServiceProcessHost::Options()
+          .WithDisplayName(display_name)
+#if defined(OS_MAC)
+          // TODO(https://crbug.com/1281311) add a utility helper for Jit.
+          .WithChildFlags(ChildProcessHost::CHILD_RENDERER)
+#endif
+          .Pass());
 }
 
 std::string AuctionProcessManager::ComputeDisplayName(
diff --git a/content/browser/lock_screen/OWNERS b/content/browser/lock_screen/OWNERS
new file mode 100644
index 0000000..b9c72ea
--- /dev/null
+++ b/content/browser/lock_screen/OWNERS
@@ -0,0 +1,3 @@
+glenrob@chromium.org
+mgiuca@chromium.org
+raymes@chromium.org
diff --git a/content/browser/lock_screen/lock_screen_service_impl.cc b/content/browser/lock_screen/lock_screen_service_impl.cc
new file mode 100644
index 0000000..65236cc
--- /dev/null
+++ b/content/browser/lock_screen/lock_screen_service_impl.cc
@@ -0,0 +1,63 @@
+// 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 "content/browser/lock_screen/lock_screen_service_impl.h"
+
+#include <map>
+#include <memory>
+
+#include "content/browser/lock_screen/lock_screen_storage_impl.h"
+#include "content/public/browser/lock_screen_storage.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "url/origin.h"
+
+namespace content {
+
+LockScreenServiceImpl::LockScreenServiceImpl(
+    content::RenderFrameHost* render_frame_host,
+    mojo::PendingReceiver<blink::mojom::LockScreenService> receiver)
+    : DocumentService(render_frame_host, std::move(receiver)),
+      lock_screen_storage_(LockScreenStorageImpl::GetInstance()) {}
+
+LockScreenServiceImpl::~LockScreenServiceImpl() = default;
+
+// static
+void LockScreenServiceImpl::Create(
+    content::RenderFrameHost* render_frame_host,
+    mojo::PendingReceiver<blink::mojom::LockScreenService> receiver) {
+  // The object is bound to the lifetime of |render_frame_host| and the mojo
+  // connection. See DocumentService for details.
+  new LockScreenServiceImpl(render_frame_host, std::move(receiver));
+}
+
+void LockScreenServiceImpl::GetKeys(GetKeysCallback callback) {
+  if (!IsAllowed()) {
+    std::move(callback).Run(std::vector<std::string>());
+    return;
+  }
+  lock_screen_storage_->GetKeys(origin(), std::move(callback));
+}
+
+void LockScreenServiceImpl::SetData(const std::string& key,
+                                    const std::string& data,
+                                    SetDataCallback callback) {
+  if (!IsAllowed()) {
+    std::move(callback).Run(
+        blink::mojom::LockScreenServiceStatus::kNotAllowedFromContext);
+    return;
+  }
+  lock_screen_storage_->SetData(origin(), key, data, std::move(callback));
+}
+
+bool LockScreenServiceImpl::IsAllowed() {
+  // TODO(crbug.com/1278144): Ideally we wouldn't even need to bind the
+  // interface in the cases below.
+  if (origin().opaque())
+    return false;
+  return lock_screen_storage_->IsAllowedBrowserContext(
+      render_frame_host()->GetProcess()->GetBrowserContext());
+}
+
+}  // namespace content
\ No newline at end of file
diff --git a/content/browser/lock_screen/lock_screen_service_impl.h b/content/browser/lock_screen/lock_screen_service_impl.h
new file mode 100644
index 0000000..dcc42e5
--- /dev/null
+++ b/content/browser/lock_screen/lock_screen_service_impl.h
@@ -0,0 +1,58 @@
+// 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 CONTENT_BROWSER_LOCK_SCREEN_LOCK_SCREEN_SERVICE_IMPL_H_
+#define CONTENT_BROWSER_LOCK_SCREEN_LOCK_SCREEN_SERVICE_IMPL_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/document_service.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "third_party/blink/public/mojom/lock_screen/lock_screen.mojom.h"
+
+namespace content {
+
+class LockScreenStorageImpl;
+class RenderFrameHost;
+
+class CONTENT_EXPORT LockScreenServiceImpl
+    : public DocumentService<blink::mojom::LockScreenService> {
+ public:
+  explicit LockScreenServiceImpl(
+      content::RenderFrameHost* render_frame_host,
+      mojo::PendingReceiver<blink::mojom::LockScreenService> receiver);
+  LockScreenServiceImpl(const LockScreenServiceImpl&) = delete;
+  LockScreenServiceImpl& operator=(const LockScreenServiceImpl&) = delete;
+
+  static void Create(
+      content::RenderFrameHost* render_frame_host,
+      mojo::PendingReceiver<blink::mojom::LockScreenService> receiver);
+
+  // blink::mojom::LockScreenService:
+  void GetKeys(GetKeysCallback callback) override;
+  void SetData(const std::string& key,
+               const std::string& data,
+               SetDataCallback) override;
+
+ private:
+  friend class LockScreenServiceImplBrowserTest;
+
+  // |this| can only be destructed as a DocumentService.
+  ~LockScreenServiceImpl() override;
+
+  bool IsAllowed();
+
+  raw_ptr<LockScreenStorageImpl> lock_screen_storage_;
+
+  base::WeakPtrFactory<LockScreenServiceImpl> weak_factory_{this};
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_LOCK_SCREEN_LOCK_SCREEN_SERVICE_IMPL_H_
diff --git a/content/browser/lock_screen/lock_screen_service_impl_browsertest.cc b/content/browser/lock_screen/lock_screen_service_impl_browsertest.cc
new file mode 100644
index 0000000..564595b
--- /dev/null
+++ b/content/browser/lock_screen/lock_screen_service_impl_browsertest.cc
@@ -0,0 +1,218 @@
+// 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 <memory>
+
+#include "base/base_paths.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/threading/thread_restrictions.h"
+#include "content/browser/lock_screen/lock_screen_service_impl.h"
+#include "content/browser/lock_screen/lock_screen_storage_impl.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/common/content_features.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "net/dns/mock_host_resolver.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/mojom/lock_screen/lock_screen.mojom.h"
+
+namespace content {
+
+class LockScreenServiceImplBrowserTest : public ContentBrowserTest {
+ public:
+  LockScreenServiceImplBrowserTest() {
+    feature_list_.InitAndEnableFeature(features::kWebLockScreenApi);
+  }
+
+  LockScreenServiceImplBrowserTest(const LockScreenServiceImplBrowserTest&) =
+      delete;
+  LockScreenServiceImplBrowserTest& operator=(
+      const LockScreenServiceImplBrowserTest&) = delete;
+
+  ~LockScreenServiceImplBrowserTest() override = default;
+
+  base::FilePath GetStoragePath() {
+    base::FilePath path;
+    EXPECT_TRUE(base::PathService::Get(base::DIR_TEMP, &path));
+    path = path.AppendASCII("web_lock_screen_api_data");
+    path = path.AppendASCII("test-user");
+    return path;
+  }
+
+  void SetUpOnMainThread() override {
+    ContentBrowserTest::SetUpOnMainThread();
+    EXPECT_TRUE(base::DeletePathRecursively(GetStoragePath()));
+    LockScreenStorageImpl::GetInstance()->InitForTesting(
+        shell()->web_contents()->GetBrowserContext(), GetStoragePath());
+    host_resolver()->AddRule("*", "127.0.0.1");
+    content::SetupCrossSiteRedirector(embedded_test_server());
+    ASSERT_TRUE(embedded_test_server()->Start());
+    GURL url = embedded_test_server()->GetURL("/lock_screen/simple.html");
+    lock_screen_service_ = NavigateAndCreateService(url);
+  }
+
+  void TearDownOnMainThread() override {
+    ContentBrowserTest::TearDownOnMainThread();
+  }
+
+  mojo::Remote<blink::mojom::LockScreenService> NavigateAndCreateService(
+      const GURL& url) {
+    Shell* shell = CreateBrowser();
+    EXPECT_TRUE(NavigateToURL(shell, url));
+    RenderFrameHost* rfh = shell->web_contents()->GetMainFrame();
+    mojo::Remote<blink::mojom::LockScreenService> service;
+    LockScreenServiceImpl::Create(rfh, service.BindNewPipeAndPassReceiver());
+    return service;
+  }
+
+  blink::mojom::LockScreenService* service() {
+    return lock_screen_service_.get();
+  }
+
+  blink::mojom::LockScreenServiceStatus AwaitSetData(
+      blink::mojom::LockScreenService* service,
+      const std::string& key,
+      const std::string& data) {
+    base::RunLoop run_loop;
+    blink::mojom::LockScreenServiceStatus result;
+    service->SetData(key, data,
+                     base::BindLambdaForTesting(
+                         [&](blink::mojom::LockScreenServiceStatus status) {
+                           result = status;
+                           run_loop.Quit();
+                         }));
+    run_loop.Run();
+    return result;
+  }
+
+  std::vector<std::string> AwaitGetKeys(
+      blink::mojom::LockScreenService* service) {
+    base::RunLoop run_loop;
+    std::vector<std::string> result;
+    service->GetKeys(
+        base::BindLambdaForTesting([&](const std::vector<std::string>& keys) {
+          result = keys;
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+    return result;
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  mojo::Remote<blink::mojom::LockScreenService> lock_screen_service_;
+};
+
+IN_PROC_BROWSER_TEST_F(LockScreenServiceImplBrowserTest,
+                       CorrectDirectoryCreated) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::FilePath expected_dir = GetStoragePath();
+  EXPECT_FALSE(base::PathExists(expected_dir));
+  // TODO(crbug.com/1268227): Consider testing write failure case.
+  ASSERT_EQ(blink::mojom::LockScreenServiceStatus::kSuccess,
+            AwaitSetData(service(), "key1", "data1"));
+  ASSERT_TRUE(base::PathExists(expected_dir));
+
+  base::FileEnumerator e(expected_dir, false,
+                         base::FileEnumerator::DIRECTORIES);
+  std::vector<base::FilePath> directories;
+  for (base::FilePath name = e.Next(); !name.empty(); name = e.Next()) {
+    directories.push_back(name);
+  }
+  ASSERT_EQ(1u, directories.size());
+  EXPECT_EQ(64u, directories[0].BaseName().MaybeAsASCII().size());
+}
+
+IN_PROC_BROWSER_TEST_F(LockScreenServiceImplBrowserTest, SetDataOpaqueOrigin) {
+  auto service = NavigateAndCreateService(GURL("about:blank"));
+  ASSERT_EQ(blink::mojom::LockScreenServiceStatus::kNotAllowedFromContext,
+            AwaitSetData(service.get(), "key1", "data1"));
+  ASSERT_EQ(blink::mojom::LockScreenServiceStatus::kNotAllowedFromContext,
+            AwaitSetData(service.get(), "key2", "data2"));
+
+  std::vector<std::string> result = AwaitGetKeys(service.get());
+  ASSERT_EQ(0u, result.size());
+}
+
+IN_PROC_BROWSER_TEST_F(LockScreenServiceImplBrowserTest, GetKeys) {
+  std::vector<std::string> result = AwaitGetKeys(service());
+  ASSERT_EQ(0u, result.size());
+
+  AwaitSetData(service(), "key1", "data1");
+  AwaitSetData(service(), "key2", "data2");
+  result = AwaitGetKeys(service());
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("key1", result[0]);
+  EXPECT_EQ("key2", result[1]);
+
+  AwaitSetData(service(), "key2", "data3");
+  result = AwaitGetKeys(service());
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("key1", result[0]);
+  EXPECT_EQ("key2", result[1]);
+}
+
+IN_PROC_BROWSER_TEST_F(LockScreenServiceImplBrowserTest,
+                       DataNotSharedBetweenDifferentOrigins) {
+  GURL url_a =
+      embedded_test_server()->GetURL("a.com", "/lock_screen/simple.html");
+  GURL url_b =
+      embedded_test_server()->GetURL("b.com", "/lock_screen/simple.html");
+  auto service_a = NavigateAndCreateService(url_a);
+  auto service_b = NavigateAndCreateService(url_b);
+
+  AwaitSetData(service_a.get(), "key1", "a");
+  AwaitSetData(service_a.get(), "key2", "a");
+  std::vector<std::string> result = AwaitGetKeys(service_a.get());
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("key1", result[0]);
+  EXPECT_EQ("key2", result[1]);
+
+  AwaitSetData(service_b.get(), "key1", "b");
+  AwaitSetData(service_b.get(), "key3", "b");
+  result = AwaitGetKeys(service_b.get());
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("key1", result[0]);
+  EXPECT_EQ("key3", result[1]);
+}
+
+IN_PROC_BROWSER_TEST_F(LockScreenServiceImplBrowserTest,
+                       DataSharedBetweenSameOrigins) {
+  GURL url_a = embedded_test_server()->GetURL("/lock_screen/simple.html");
+  GURL url_b = embedded_test_server()->GetURL("/lock_screen/simple.html?abcd");
+  auto service_a = NavigateAndCreateService(url_a);
+  auto service_b = NavigateAndCreateService(url_b);
+
+  AwaitSetData(service_a.get(), "key1", "a");
+  AwaitSetData(service_a.get(), "key2", "a");
+  std::vector<std::string> result = AwaitGetKeys(service_a.get());
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("key1", result[0]);
+  EXPECT_EQ("key2", result[1]);
+
+  result = AwaitGetKeys(service_b.get());
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("key1", result[0]);
+  EXPECT_EQ("key2", result[1]);
+
+  AwaitSetData(service_b.get(), "key1", "b");
+  AwaitSetData(service_b.get(), "key3", "b");
+  result = AwaitGetKeys(service_a.get());
+  ASSERT_EQ(3u, result.size());
+  EXPECT_EQ("key1", result[0]);
+  EXPECT_EQ("key2", result[1]);
+  EXPECT_EQ("key3", result[2]);
+}
+
+}  // namespace content
\ No newline at end of file
diff --git a/content/browser/lock_screen/lock_screen_storage_impl.cc b/content/browser/lock_screen/lock_screen_storage_impl.cc
new file mode 100644
index 0000000..fb6eb29
--- /dev/null
+++ b/content/browser/lock_screen/lock_screen_storage_impl.cc
@@ -0,0 +1,195 @@
+// 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 "content/browser/lock_screen/lock_screen_storage_impl.h"
+
+#include <map>
+#include <memory>
+
+#include "base/memory/ref_counted_delete_on_sequence.h"
+#include "base/memory/singleton.h"
+#include "base/path_service.h"
+#include "base/sequence_checker.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/values.h"
+#include "components/value_store/value_store.h"
+#include "components/value_store/value_store_factory.h"
+#include "components/value_store/value_store_factory_impl.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "crypto/sha2.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "url/origin.h"
+
+using value_store::ValueStore;
+
+namespace content {
+
+namespace {
+
+// See Extensions.Database.Open in histograms.xml.
+const char kValueStoreDatabaseUMAClientName[] = "WebAppsLockScreen";
+
+}  // namespace
+
+// Helper class for running blocking tasks on a thread pool.
+class LockScreenStorageHelper {
+ public:
+  LockScreenStorageHelper();
+  ~LockScreenStorageHelper() = default;
+
+  void Init(const base::FilePath& base_path);
+  std::vector<std::string> GetKeys(const url::Origin& origin);
+  bool SetData(const url::Origin& origin,
+               const std::string& key,
+               const std::string& data);
+
+ private:
+  ValueStore* GetValueStoreForOrigin(const url::Origin& origin);
+
+  scoped_refptr<value_store::ValueStoreFactory> value_store_factory_;
+  // Maps storage directory filename to ValueStore for a particular origin.
+  // TODO(crbug.com/1268227): If there can only be one lock screen app at a
+  // time, this does not need to be a map. Otherwise, there should be a clean
+  // way of evicting value stores databases from this cache.
+  std::map<std::string, std::unique_ptr<ValueStore>> storage_map_;
+};
+
+LockScreenStorageHelper::LockScreenStorageHelper() {}
+
+void LockScreenStorageHelper::Init(const base::FilePath& base_path) {
+  value_store_factory_ =
+      base::MakeRefCounted<value_store::ValueStoreFactoryImpl>(base_path);
+}
+
+std::vector<std::string> LockScreenStorageHelper::GetKeys(
+    const url::Origin& origin) {
+  ValueStore* value_store = GetValueStoreForOrigin(origin);
+  ValueStore::ReadResult read = value_store->Get();
+  std::vector<std::string> result;
+  if (!read.status().ok())
+    return result;
+  for (auto kv : read.settings().DictItems()) {
+    result.push_back(kv.first);
+  }
+
+  return result;
+}
+
+bool LockScreenStorageHelper::SetData(const url::Origin& origin,
+                                      const std::string& key,
+                                      const std::string& data) {
+  ValueStore* value_store = GetValueStoreForOrigin(origin);
+  ValueStore::WriteResult write =
+      value_store->Set(ValueStore::DEFAULTS, key, base::Value(data));
+  return write.status().ok();
+}
+
+ValueStore* LockScreenStorageHelper::GetValueStoreForOrigin(
+    const url::Origin& origin) {
+  DCHECK(!origin.opaque());
+
+  // ValueStore will create a directory for storing its data. The directory name
+  // is passed in. We want to key data by origin, so we use a hash of the origin
+  // as the directory name under which to store the data. Origin.Serialize()
+  // should just concatenate the scheme/host/port, which are the components that
+  // need to appear identical if the two origins need to compare equal. Hence
+  // if two origins are equal, the serialized origins should also be equal.
+  std::string serialized_origin = origin.Serialize();
+  uint8_t hash[crypto::kSHA256Length];
+  crypto::SHA256HashString(serialized_origin, hash, sizeof(hash));
+  std::string filename = base::HexEncode(hash, crypto::kSHA256Length);
+
+  auto iter = storage_map_.find(filename);
+  if (iter != storage_map_.end())
+    return iter->second.get();
+
+  base::FilePath value_store_path(filename);
+  std::unique_ptr<ValueStore> value_store =
+      value_store_factory_->CreateValueStore(value_store_path,
+                                             kValueStoreDatabaseUMAClientName);
+  ValueStore* result = value_store.get();
+  storage_map_.emplace(filename, std::move(value_store));
+  return result;
+}
+
+// static
+LockScreenStorage* LockScreenStorage::GetInstance() {
+  return LockScreenStorageImpl::GetInstance();
+}
+
+// static
+LockScreenStorageImpl* LockScreenStorageImpl::GetInstance() {
+  return base::Singleton<
+      LockScreenStorageImpl,
+      base::LeakySingletonTraits<LockScreenStorageImpl>>::get();
+}
+
+LockScreenStorageImpl::LockScreenStorageImpl()
+    : helper_(base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})) {
+}
+
+LockScreenStorageImpl::~LockScreenStorageImpl() = default;
+
+void LockScreenStorageImpl::Init(content::BrowserContext* browser_context,
+                                 const base::FilePath& base_path) {
+  DCHECK(!browser_context_);
+  DCHECK(!browser_context->IsOffTheRecord());
+  browser_context_ = browser_context;
+  helper_.AsyncCall(&LockScreenStorageHelper::Init).WithArgs(base_path);
+}
+
+void LockScreenStorageImpl::GetKeys(
+    const url::Origin& origin,
+    blink::mojom::LockScreenService::GetKeysCallback callback) {
+  helper_.AsyncCall(&LockScreenStorageHelper::GetKeys)
+      .WithArgs(origin)
+      .Then(base::BindOnce(&LockScreenStorageImpl::OnGetKeys,
+                           weak_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void LockScreenStorageImpl::SetData(
+    const url::Origin& origin,
+    const std::string& key,
+    const std::string& data,
+    blink::mojom::LockScreenService::SetDataCallback callback) {
+  helper_.AsyncCall(&LockScreenStorageHelper::SetData)
+      .WithArgs(origin, key, data)
+      .Then(base::BindOnce(&LockScreenStorageImpl::OnSetData,
+                           weak_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+bool LockScreenStorageImpl::IsAllowedBrowserContext(
+    content::BrowserContext* browser_context) {
+  return browser_context == browser_context_;
+}
+
+void LockScreenStorageImpl::OnGetKeys(
+    blink::mojom::LockScreenService::GetKeysCallback callback,
+    const std::vector<std::string>& result) {
+  std::move(callback).Run(result);
+}
+
+void LockScreenStorageImpl::OnSetData(
+    blink::mojom::LockScreenService::SetDataCallback callback,
+    bool success) {
+  if (success) {
+    std::move(callback).Run(blink::mojom::LockScreenServiceStatus::kSuccess);
+  } else {
+    std::move(callback).Run(blink::mojom::LockScreenServiceStatus::kWriteError);
+  }
+}
+
+void LockScreenStorageImpl::InitForTesting(
+    content::BrowserContext* browser_context,
+    const base::FilePath& base_path) {
+  browser_context_ = nullptr;
+  Init(browser_context, base_path);
+}
+
+}  // namespace content
\ No newline at end of file
diff --git a/content/browser/lock_screen/lock_screen_storage_impl.h b/content/browser/lock_screen/lock_screen_storage_impl.h
new file mode 100644
index 0000000..9cf662a4
--- /dev/null
+++ b/content/browser/lock_screen/lock_screen_storage_impl.h
@@ -0,0 +1,76 @@
+// 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 CONTENT_BROWSER_LOCK_SCREEN_LOCK_SCREEN_STORAGE_IMPL_H_
+#define CONTENT_BROWSER_LOCK_SCREEN_LOCK_SCREEN_STORAGE_IMPL_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/sequence_bound.h"
+#include "content/public/browser/lock_screen_storage.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "third_party/blink/public/mojom/lock_screen/lock_screen.mojom.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}  // namespace base
+
+namespace content {
+
+class LockScreenStorageHelper;
+
+// Global storage for lock screen data stored by websites. This isn't
+// BrowserContext keyed because there is only ever one lock screen profile (the
+// primary user's BrowserContext).
+class CONTENT_EXPORT LockScreenStorageImpl : public LockScreenStorage {
+ public:
+  static LockScreenStorageImpl* GetInstance();
+
+  LockScreenStorageImpl(const LockScreenStorageImpl&) = delete;
+  LockScreenStorageImpl& operator=(const LockScreenStorageImpl&) = delete;
+  virtual ~LockScreenStorageImpl();
+
+  // LockScreenStorage overrides.
+  void Init(content::BrowserContext* browser_context,
+            const base::FilePath& base_path) override;
+
+  void GetKeys(const url::Origin& origin,
+               blink::mojom::LockScreenService::GetKeysCallback callback);
+  void SetData(const url::Origin& origin,
+               const std::string& key,
+               const std::string& data,
+               blink::mojom::LockScreenService::SetDataCallback);
+
+  // Whether the BrowserContext is allowed to store/retrieve lock screen data.
+  bool IsAllowedBrowserContext(content::BrowserContext* profile);
+
+ private:
+  LockScreenStorageImpl();
+
+  void OnGetKeys(blink::mojom::LockScreenService::GetKeysCallback callback,
+                 const std::vector<std::string>& result);
+  void OnSetData(blink::mojom::LockScreenService::SetDataCallback callback,
+                 bool success);
+
+  // Reinitialize the storage for testing.
+  void InitForTesting(content::BrowserContext* browser_context,
+                      const base::FilePath& base_path);
+
+  raw_ptr<content::BrowserContext> browser_context_ = nullptr;
+  base::SequenceBound<LockScreenStorageHelper> helper_;
+
+  base::WeakPtrFactory<LockScreenStorageImpl> weak_factory_{this};
+
+  friend class LockScreenServiceImplBrowserTest;
+  friend struct base::DefaultSingletonTraits<LockScreenStorageImpl>;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_LOCK_SCREEN_LOCK_SCREEN_STORAGE_IMPL_H_
diff --git a/content/browser/mojo_binder_policy_map_impl.cc b/content/browser/mojo_binder_policy_map_impl.cc
index c2dde0f..8efc645d 100644
--- a/content/browser/mojo_binder_policy_map_impl.cc
+++ b/content/browser/mojo_binder_policy_map_impl.cc
@@ -25,6 +25,7 @@
 #include "third_party/blink/public/mojom/media/renderer_audio_output_stream_factory.mojom.h"
 #include "third_party/blink/public/mojom/native_io/native_io.mojom.h"
 #include "third_party/blink/public/mojom/notifications/notification_service.mojom.h"
+#include "third_party/blink/public/mojom/page/display_cutout.mojom.h"
 
 namespace content {
 
@@ -117,6 +118,16 @@
   // playing media. See `RenderFrameImpl::DeferMediaLoad` for more information.
   map.SetAssociatedPolicy<media::mojom::MediaPlayerHost>(
       MojoBinderAssociatedPolicy::kGrant);
+
+  // DisplayCutout supports the CSS viewport-fit property. It tracks
+  // the current viewport-fit on a per-document basis, but only calls
+  // the WebContents::NotifyViewportFitChanged and informs WebContents's
+  // observers when the document is fullscreened. Prerendered documents cannot
+  // enter fullscreen because they do not have transient activation, nor are
+  // they active documents (see RenderFrameHostImpl::EnterFullscreen), so it is
+  // safe to allow a prerendered document to use it.
+  map.SetAssociatedPolicy<blink::mojom::DisplayCutoutHost>(
+      MojoBinderAssociatedPolicy::kGrant);
 }
 
 // Register mojo binder policies for same-origin prerendering for content/
diff --git a/content/browser/portal/portal.cc b/content/browser/portal/portal.cc
index 1f1fc03..e35202d 100644
--- a/content/browser/portal/portal.cc
+++ b/content/browser/portal/portal.cc
@@ -186,10 +186,8 @@
     }
   }
 
-  FrameTreeNode* frame_tree_node =
-      portal_contents_->GetMainFrame()->frame_tree_node();
   RenderFrameProxyHost* proxy_host =
-      frame_tree_node->render_manager()->GetProxyToOuterDelegate();
+      portal_contents_->GetMainFrame()->GetProxyToOuterDelegate();
   proxy_host->SetRenderFrameProxyCreated(true);
   portal_contents_->ReattachToOuterWebContentsFrame();
 
diff --git a/content/browser/prerender/prerender_browsertest.cc b/content/browser/prerender/prerender_browsertest.cc
index cad0904c..f3dc476 100644
--- a/content/browser/prerender/prerender_browsertest.cc
+++ b/content/browser/prerender/prerender_browsertest.cc
@@ -89,6 +89,7 @@
 #include "third_party/blink/public/common/loader/loader_constants.h"
 #include "third_party/blink/public/mojom/browser_interface_broker.mojom.h"
 #include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
+#include "third_party/blink/public/mojom/page/display_cutout.mojom.h"
 #include "ui/shell_dialogs/select_file_dialog.h"
 #include "ui/shell_dialogs/select_file_dialog_factory.h"
 #include "url/gurl.h"
@@ -325,6 +326,11 @@
     // Useful for testing CSP:prefetch-src
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kEnableExperimentalWebPlatformFeatures);
+    // The viewport meta tag is only enabled on Android.
+#if defined(OS_ANDROID)
+    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
+                                    "DisplayCutoutAPI");
+#endif
   }
 
   void TestNavigationHistory(const GURL& expected_current_url,
@@ -2911,6 +2917,88 @@
   EXPECT_TRUE(ExecJs(web_contents()->GetMainFrame(), audio_script));
 }
 
+// The viewport meta tag is only enabled on Android.
+#if defined(OS_ANDROID)
+namespace {
+
+// Used to observe the viewport change in the WebContents.
+class TestViewportWebContentsObserver : public WebContentsObserver {
+ public:
+  TestViewportWebContentsObserver(WebContents* web_contents,
+                                  blink::mojom::ViewportFit wanted_value)
+      : WebContentsObserver(web_contents), wanted_value_(wanted_value) {}
+
+  TestViewportWebContentsObserver(const TestViewportWebContentsObserver&) =
+      delete;
+  TestViewportWebContentsObserver& operator=(
+      const TestViewportWebContentsObserver&) = delete;
+
+  // WebContentsObserver implementation.
+  void ViewportFitChanged(blink::mojom::ViewportFit value) override {
+    value_ = value;
+    if (waiting_for_wanted_value_ && value == wanted_value_) {
+      std::move(waiting_for_wanted_value_).Run();
+    }
+  }
+
+  void WaitForWantedValue() {
+    if (value_.has_value() && value_.value() == wanted_value_) {
+      return;
+    }
+    base::RunLoop loop;
+    waiting_for_wanted_value_ = loop.QuitClosure();
+    loop.Run();
+  }
+
+ private:
+  base::OnceClosure waiting_for_wanted_value_;
+  absl::optional<blink::mojom::ViewportFit> value_;
+  const blink::mojom::ViewportFit wanted_value_;
+};
+
+}  // namespace
+
+// Tests that the viewport-fit property works well on prerendering page:
+// * The property in prerendering page shouldn't affect the primary page.
+// * After activating the prerendered page, WebContents's viewport property can
+//   be updated.
+IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, ViewportFit) {
+  const GURL kInitialUrl = GetUrl("/prerender/viewport.html");
+  const GURL kPrerenderingUrl = GetUrl("/prerender/viewport.html?prerendering");
+
+  // Navigate to an initial page.
+  ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl));
+  int host_id = AddPrerender(kPrerenderingUrl);
+  test::PrerenderHostObserver host_observer(*web_contents(), host_id);
+  RenderFrameHostImpl* prerender_rfh = GetPrerenderedMainFrameHost(host_id);
+  RenderFrameHostImpl* primary_rfh = web_contents_impl()->GetMainFrame();
+
+  {
+    // Set viewport-fit property in the primary page and the prerendering page.
+    // Prerendering shouldn't be cancelled, nor should its property affect the
+    // corresponding WebContents's property.
+    TestViewportWebContentsObserver observer(web_contents_impl(),
+                                             blink::mojom::ViewportFit::kCover);
+    EXPECT_TRUE(ExecJs(prerender_rfh, "setViewportFit('contain')"));
+    EXPECT_TRUE(ExecJs(primary_rfh, "setViewportFit('cover')"));
+    web_contents_impl()->FullscreenStateChanged(
+        primary_rfh, true, blink::mojom::FullscreenOptions::New());
+    observer.WaitForWantedValue();
+  }
+  {
+    // After the prerendering page is activated, the WebContents's property
+    // should be updated.
+    TestViewportWebContentsObserver observer(
+        web_contents_impl(), blink::mojom::ViewportFit::kContain);
+    prerender_helper()->NavigatePrimaryPage(kPrerenderingUrl);
+    web_contents_impl()->FullscreenStateChanged(
+        prerender_rfh, true, blink::mojom::FullscreenOptions::New());
+    observer.WaitForWantedValue();
+  }
+  EXPECT_TRUE(host_observer.was_activated());
+}
+#endif  // defined(OS_ANDROID)
+
 // End: Tests for feature restrictions in prerendered pages ====================
 
 // Tests prerendering for low-end devices.
diff --git a/content/browser/quota/quota_internals_browsertest.cc b/content/browser/quota/quota_internals_browsertest.cc
index fac6bbb..bcf56f70 100644
--- a/content/browser/quota/quota_internals_browsertest.cc
+++ b/content/browser/quota/quota_internals_browsertest.cc
@@ -47,6 +47,23 @@
   EXPECT_TRUE(ExecJsInWebUI("document.getElementById('total-space') >= 0;"));
   EXPECT_TRUE(
       ExecJsInWebUI("document.getElementById('available-space') >= 0;"));
+  EXPECT_TRUE(
+      ExecJsInWebUI("document.body.innerHTML.search('Errors on Getting Usage "
+                    "and Quota') >= 0;"));
+  EXPECT_TRUE(
+      ExecJsInWebUI("document.body.innerHTML.search('Evicted Buckets') >= 0;"));
+  EXPECT_TRUE(
+      ExecJsInWebUI("document.body.innerHTML.search('Eviction Rounds') >= 0;"));
+  EXPECT_TRUE(ExecJsInWebUI(
+      "document.body.innerHTML.search('Skipped Eviction Rounds') >= 0;"));
+  EXPECT_TRUE(ExecJsInWebUI(
+      "document.getElementById('errors-on-getting-usage-and-quota') >= 0;"));
+  EXPECT_TRUE(ExecJsInWebUI(
+      "document.getElementById('skipped-eviction-rounds') >= 0;"));
+  EXPECT_TRUE(
+      ExecJsInWebUI("document.getElementById('eviction-rounds') >= 0;"));
+  EXPECT_TRUE(
+      ExecJsInWebUI("document.getElementById('evicted-buckets') >= 0;"));
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/back_forward_cache_impl.cc b/content/browser/renderer_host/back_forward_cache_impl.cc
index 4cfd55d..d7af15d 100644
--- a/content/browser/renderer_host/back_forward_cache_impl.cc
+++ b/content/browser/renderer_host/back_forward_cache_impl.cc
@@ -617,46 +617,45 @@
   }
 }
 
-BackForwardCacheCanStoreDocumentResult
-BackForwardCacheImpl::CanRestorePageNowForTesting(
-    RenderFrameHostImpl* render_frame_host) {
-  BackForwardCacheCanStoreDocumentResult can_store =
-      CanStorePageNow(render_frame_host);
-  UpdateCanStoreToIncludeCacheControlNoStore(can_store, render_frame_host);
-  return can_store;
-}
+BackForwardCacheCanStoreDocumentResultWithTree
+BackForwardCacheImpl::CanStorePageNow(RenderFrameHostImpl* rfh) {
+  BackForwardCacheCanStoreDocumentResult flattened_result;
+  std::unique_ptr<BackForwardCacheCanStoreTreeResult> tree =
+      PopulateReasonsForDocumentAndDescendants(rfh, rfh->GetLastCommittedURL(),
+                                               flattened_result,
+                                               /*include_non_sticky=*/true,
+                                               /*create_tree=*/true);
 
-BackForwardCacheCanStoreDocumentResult BackForwardCacheImpl::CanStorePageNow(
-    RenderFrameHostImpl* rfh) {
-  BackForwardCacheCanStoreDocumentResult result;
-  PopulateReasonsForMainDocument(result, rfh);
-  PopulateReasonsForDocumentAndDescendants(result, rfh,
-                                           /*include_non_sticky=*/true);
+  // TODO(https://crbug.com/1280150): Call
+  // UpdateCanStoreToIncludeCacheControlNoStore() for tree structure.
 
   // With the flag on, |result| does not contain CacheControlNoStore, so it may
   // need to be updated.
   if (AllowStoringPagesWithCacheControlNoStore()) {
-    // If there are no reasons apart from CacheControlNoStore then allow the
-    // page to be stored. (Do not only report CacheControlNoStore as the
-    // page should enter bfcache in that case.)
-    if (!result.CanStore()) {
-      UpdateCanStoreToIncludeCacheControlNoStore(result, rfh);
+    if (!flattened_result.CanStore()) {
+      // If there are no reasons apart from CacheControlNoStore then allow the
+      // page to be stored. (Do not only report CacheControlNoStore as the
+      // page should enter bfcache in that case.)
+      UpdateCanStoreToIncludeCacheControlNoStore(flattened_result, rfh);
     }
   }
   DVLOG(1) << "CanStorePageNow: " << rfh->GetLastCommittedURL() << " : "
-           << result.ToString();
+           << flattened_result.ToString();
   TRACE_EVENT("navigation", "BackForwardCacheImpl::CanPotentiallyStorePageNow",
               ChromeTrackEvent::kBackForwardCacheCanStoreDocumentResult,
-              result);
-  return result;
+              flattened_result);
+
+  return BackForwardCacheCanStoreDocumentResultWithTree(flattened_result,
+                                                        std::move(tree));
 }
 
 BackForwardCacheCanStoreDocumentResult
 BackForwardCacheImpl::CanPotentiallyStorePageLater(RenderFrameHostImpl* rfh) {
   BackForwardCacheCanStoreDocumentResult result;
-  PopulateReasonsForMainDocument(result, rfh);
-  PopulateReasonsForDocumentAndDescendants(result, rfh,
-                                           /*include_non_sticky=*/false);
+  PopulateReasonsForDocumentAndDescendants(rfh, rfh->GetLastCommittedURL(),
+                                           result,
+                                           /*include_non_sticky = */ false,
+                                           /*create_tree = */ false);
   DVLOG(1) << "CanPotentiallyStorePageLater: " << rfh->GetLastCommittedURL()
            << " : " << result.ToString();
   TRACE_EVENT(
@@ -928,20 +927,53 @@
   }
 }
 
-void BackForwardCacheImpl::PopulateReasonsForDocumentAndDescendants(
-    BackForwardCacheCanStoreDocumentResult& result,
+// TODO(https://crbug.com/1275977): Remove |first_call| and move
+// |PopulateReasonsForMainDocument| to |PopulateReasonsForDocument|.
+std::unique_ptr<BackForwardCacheCanStoreTreeResult>
+BackForwardCacheImpl::PopulateReasonsForDocumentAndDescendants(
     RenderFrameHostImpl* rfh,
-    bool include_non_sticky) {
-  PopulateReasonsForDocument(result, rfh, include_non_sticky);
-  for (size_t i = 0; i < rfh->child_count(); i++)
-    PopulateReasonsForDocumentAndDescendants(
-        result, rfh->child_at(i)->current_frame_host(), include_non_sticky);
+    const GURL main_url,
+    BackForwardCacheCanStoreDocumentResult& flattened_result,
+    bool include_non_sticky,
+    bool create_tree,
+    bool first_call) {
+  BackForwardCacheCanStoreDocumentResult result_for_this_document;
+  // Stores the reasons for flattened_result
+  if (first_call) {
+    PopulateReasonsForMainDocument(result_for_this_document, rfh);
+  }
+  PopulateReasonsForDocument(result_for_this_document, rfh, include_non_sticky);
+  flattened_result.AddReasonsFrom(result_for_this_document);
+
+  // Finds the reasons recursively and create the reason subtree for the
+  // children if needed.
+  BackForwardCacheCanStoreTreeResult::ChildrenVector children_result;
+  for (size_t i = 0; i < rfh->child_count(); i++) {
+    std::unique_ptr<BackForwardCacheCanStoreTreeResult> child =
+        PopulateReasonsForDocumentAndDescendants(
+            rfh->child_at(i)->current_frame_host(), main_url, flattened_result,
+            include_non_sticky, create_tree,
+            /*first_call=*/false);
+    if (create_tree) {
+      children_result.emplace_back(std::move(child));
+    }
+  }
+
+  if (!create_tree)
+    return nullptr;
+
+  std::unique_ptr<BackForwardCacheCanStoreTreeResult> tree(
+      new BackForwardCacheCanStoreTreeResult(
+          rfh, main_url, result_for_this_document, std::move(children_result)));
+  return tree;
 }
 
 void BackForwardCacheImpl::StoreEntry(
     std::unique_ptr<BackForwardCacheImpl::Entry> entry) {
   TRACE_EVENT("navigation", "BackForwardCache::StoreEntry", "entry", entry);
-  DCHECK(CanStorePageNow(entry->render_frame_host()));
+  BackForwardCacheCanStoreDocumentResultWithTree result =
+      CanStorePageNow(entry->render_frame_host());
+  DCHECK(result);
 
 #if defined(OS_ANDROID)
   if (!IsProcessBindingEnabled()) {
@@ -1169,13 +1201,15 @@
           .Has(WebSchedulerTrackedFeature::
                    kMainResourceHasCacheControlNoStore)) {
     auto* render_frame_host = (*matching_entry)->render_frame_host();
-    BackForwardCacheCanStoreDocumentResult can_store =
+    BackForwardCacheCanStoreDocumentResultWithTree can_store =
         CanStorePageNow(render_frame_host);
-    UpdateCanStoreToIncludeCacheControlNoStore(can_store, render_frame_host);
+    UpdateCanStoreToIncludeCacheControlNoStore(can_store.flattened_reasons,
+                                               render_frame_host);
     if (!can_store) {
       (*matching_entry)
           ->render_frame_host()
-          ->EvictFromBackForwardCacheWithReasons(can_store);
+          ->EvictFromBackForwardCacheWithReasons(
+              can_store.flattened_reasons, std::move(can_store.tree_reasons));
     }
   }
 
@@ -1355,4 +1389,28 @@
   return !(*this == other);
 }
 
+BackForwardCacheCanStoreTreeResult::BackForwardCacheCanStoreTreeResult(
+    RenderFrameHostImpl* rfh,
+    const GURL main_document_url,
+    BackForwardCacheCanStoreDocumentResult& result_for_this_document,
+    BackForwardCacheCanStoreTreeResult::ChildrenVector children)
+    : document_result_(std::move(result_for_this_document)),
+      children_(std::move(children)),
+      is_same_origin_(
+          url::IsSameOriginWith(rfh->GetLastCommittedURL(), main_document_url)),
+      url_(rfh->GetLastCommittedURL()) {}
+
+BackForwardCacheCanStoreTreeResult::~BackForwardCacheCanStoreTreeResult() =
+    default;
+
+BackForwardCacheCanStoreDocumentResultWithTree::
+    BackForwardCacheCanStoreDocumentResultWithTree(
+        BackForwardCacheCanStoreDocumentResult& flattened_reasons,
+        std::unique_ptr<BackForwardCacheCanStoreTreeResult> tree_reasons)
+    : flattened_reasons(std::move(flattened_reasons)),
+      tree_reasons(std::move(tree_reasons)) {}
+
+BackForwardCacheCanStoreDocumentResultWithTree::
+    ~BackForwardCacheCanStoreDocumentResultWithTree() = default;
+
 }  // namespace content
diff --git a/content/browser/renderer_host/back_forward_cache_impl.h b/content/browser/renderer_host/back_forward_cache_impl.h
index 8ab9afe..dfc424b7 100644
--- a/content/browser/renderer_host/back_forward_cache_impl.h
+++ b/content/browser/renderer_host/back_forward_cache_impl.h
@@ -66,6 +66,25 @@
     "BackForwardCacheMediaSessionPlaybackStateChange",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Combines a flattened list and a tree of the reasons why each document cannot
+// enter the back/forward cache (might be empty if it can). The tree saves the
+// reasons for each document in the tree (including those without the reasons)
+// in a tree format, with each node corresponding to one document. The flattened
+// list is the combination of all reasons for all documents in the tree.
+// CONTENT_EXPORT is for exporting only for testing.
+struct CONTENT_EXPORT BackForwardCacheCanStoreDocumentResultWithTree {
+  BackForwardCacheCanStoreDocumentResultWithTree(
+      BackForwardCacheCanStoreDocumentResult& flattened_reasons,
+      std::unique_ptr<BackForwardCacheCanStoreTreeResult> tree_reasons);
+  ~BackForwardCacheCanStoreDocumentResultWithTree();
+
+  BackForwardCacheCanStoreDocumentResult flattened_reasons;
+  std::unique_ptr<BackForwardCacheCanStoreTreeResult> tree_reasons;
+  // If BFCache is available, it returns true. If there are reasons that BFCache
+  // is not available, it returns false.
+  explicit operator bool() const { return flattened_reasons; }
+};
+
 // BackForwardCache:
 //
 // After the user navigates away from a document, the old one goes into the
@@ -170,13 +189,13 @@
   // Returns whether MediaSession's service is allowed for the BackForwardCache.
   static bool IsMediaSessionServiceAllowed();
 
-  // Returns whether a RenderFrameHost can be stored into the BackForwardCache
-  // right now. Depends on the |render_frame_host| and its children's state.
-  // Should only be called after we've navigated away from |render_frame_host|,
-  // which means nothing about the page can change (usage of blocklisted
-  // features, pending navigations, load state, etc.) anymore.
+  // Returns the reasons (if any) why this document and its children cannot
+  // enter the back/forward cache. Depends on the |render_frame_host| and its
+  // children's state. Should only be called after we've navigated away from
+  // |render_frame_host|, which means nothing about the page can change (usage
+  // of blocklisted features, pending navigations, load state, etc.) anymore.
   // Note that criteria for storing and restoring can be different.
-  BackForwardCacheCanStoreDocumentResult CanStorePageNow(
+  BackForwardCacheCanStoreDocumentResultWithTree CanStorePageNow(
       RenderFrameHostImpl* render_frame_host);
 
   // Whether a RenderFrameHost could be stored into the BackForwardCache at some
@@ -292,11 +311,6 @@
   // background limits (if finch parameter "foreground_cache_size" > 0).
   static bool UsingForegroundBackgroundCacheSizeLimit();
 
-  // Used only for testing. This will include cache-control:no-store reasons if
-  // there are any.
-  BackForwardCacheCanStoreDocumentResult CanRestorePageNowForTesting(
-      RenderFrameHostImpl* render_frame_host);
-
   // Returns true if one of the BFCache entries has a matching
   // BrowsingInstanceId/SiteInstanceId/RenderFrameProxyHost.
   // TODO(https://crbug.com/1243541): Remove these once the bug is fixed.
@@ -323,12 +337,21 @@
       RenderFrameHostImpl* rfh,
       bool include_non_sticky);
 
-  // Calls `PopulateReasonsForDocument` recursively on `rfh` and its
-  // descendants.
-  void PopulateReasonsForDocumentAndDescendants(
-      BackForwardCacheCanStoreDocumentResult& result,
+  // Populates the reasons why this RenderFrameHost and its children cannot
+  // enter the back/forward cache.
+  // If |create_tree| is true, returns a tree of reasons by the document.
+  // |main_url| is the URL of the outermost document. |include_non_sticky|
+  // controls whether we include non-sticky reasons in the result. This is a
+  // recursive method and |first_call| indicates whether we have recursed yet as
+  // we treat the top document differently from the descendants.
+  std::unique_ptr<BackForwardCacheCanStoreTreeResult>
+  PopulateReasonsForDocumentAndDescendants(
       RenderFrameHostImpl* rfh,
-      bool include_non_sticky);
+      const GURL main_url,
+      BackForwardCacheCanStoreDocumentResult& flattened_result,
+      bool include_non_sticky,
+      bool create_tree,
+      bool first_call = true);
 
   // Populates the sticky reasons for `rfh` without recursing into subframes.
   // Sticky features can't be unregistered and remain active for the rest of the
@@ -438,6 +461,62 @@
       BackForwardCache::DisabledReason reason) = 0;
 };
 
+// Represents the reasons that a page cannot enter BFCache as a tree with a node
+// for every document in the page, in frame tree order. It also includes
+// documents that have no blocking reason.
+class CONTENT_EXPORT BackForwardCacheCanStoreTreeResult {
+ public:
+  friend class BackForwardCacheImpl;
+
+  using ChildrenVector =
+      std::vector<std::unique_ptr<BackForwardCacheCanStoreTreeResult>>;
+
+  BackForwardCacheCanStoreTreeResult() = delete;
+  BackForwardCacheCanStoreTreeResult(BackForwardCacheCanStoreTreeResult&) =
+      delete;
+  BackForwardCacheCanStoreTreeResult& operator=(
+      BackForwardCacheCanStoreTreeResult&&) = delete;
+  ~BackForwardCacheCanStoreTreeResult();
+
+  // The reasons for the document corresponding to this node.
+  const BackForwardCacheCanStoreDocumentResult& GetDocumentResult() const {
+    return document_result_;
+  }
+
+  // The children nodes. We can access the children nodes of this
+  // node/document from this vector.
+  const ChildrenVector& GetChildren() const { return children_; }
+
+  // Whether this document is the same origin with the origin of the root of
+  // this reason tree. Returns false if this document is cross-origin.
+  bool IsSameOrigin() const { return is_same_origin_; }
+
+  // The URL of the document corresponding to this node.
+  const GURL& GetUrl() const { return url_; }
+
+ private:
+  BackForwardCacheCanStoreTreeResult(
+      RenderFrameHostImpl* rfh,
+      const GURL main_document_url,
+      BackForwardCacheCanStoreDocumentResult& result_for_this_document,
+      ChildrenVector children);
+
+  // See |GetDocumentResult|
+  const BackForwardCacheCanStoreDocumentResult document_result_;
+
+  // See |GetChildren|
+  const ChildrenVector children_;
+
+  // See |IsSameOrigin|
+  const bool is_same_origin_;
+
+  // See |GetUrl|
+  const GURL& url_;
+
+  // TODO(crbug.com/1278620): Add the value of the id attribute of the iframe
+  // element.
+};
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_RENDERER_HOST_BACK_FORWARD_CACHE_IMPL_H_
diff --git a/content/browser/renderer_host/back_forward_cache_metrics.cc b/content/browser/renderer_host/back_forward_cache_metrics.cc
index babe1af..be0359c 100644
--- a/content/browser/renderer_host/back_forward_cache_metrics.cc
+++ b/content/browser/renderer_host/back_forward_cache_metrics.cc
@@ -9,6 +9,7 @@
 #include "base/metrics/metrics_hashes.h"
 #include "base/metrics/sparse_histogram.h"
 #include "content/browser/devtools/devtools_instrumentation.h"
+#include "content/browser/renderer_host/back_forward_cache_impl.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
 #include "content/browser/renderer_host/navigation_entry_impl.h"
 #include "content/browser/renderer_host/navigation_request.h"
@@ -299,6 +300,13 @@
   }
 }
 
+void BackForwardCacheMetrics::FinalizeNotRestoredReasons(
+    const BackForwardCacheCanStoreDocumentResult& can_store_flat,
+    std::unique_ptr<BackForwardCacheCanStoreTreeResult> can_store_tree) {
+  page_store_tree_result_ = std::move(can_store_tree);
+  MarkNotRestoredWithReason(can_store_flat);
+}
+
 void BackForwardCacheMetrics::MarkNotRestoredWithReason(
     const BackForwardCacheCanStoreDocumentResult& can_store) {
   page_store_result_->AddReasonsFrom(can_store);
diff --git a/content/browser/renderer_host/back_forward_cache_metrics.h b/content/browser/renderer_host/back_forward_cache_metrics.h
index 05289d0..763ebd1 100644
--- a/content/browser/renderer_host/back_forward_cache_metrics.h
+++ b/content/browser/renderer_host/back_forward_cache_metrics.h
@@ -24,6 +24,7 @@
 
 namespace content {
 class BackForwardCacheCanStoreDocumentResult;
+class BackForwardCacheCanStoreTreeResult;
 class NavigationEntryImpl;
 class NavigationRequest;
 class RenderFrameHostImpl;
@@ -219,6 +220,13 @@
   void MarkNotRestoredWithReason(
       const BackForwardCacheCanStoreDocumentResult& can_store);
 
+  // TODO: Take BackForwardCacheCanStoreDocumentResultWithTree as an argument
+  // instead of using BackForwardCacheCanStoreDocumentResult and
+  // BackForwardCacheCanStoreTreeResult as arguments.
+  void FinalizeNotRestoredReasons(
+      const BackForwardCacheCanStoreDocumentResult& can_store_flat,
+      std::unique_ptr<BackForwardCacheCanStoreTreeResult> can_store_tree);
+
   // Exported for testing.
   // The DisabledReason's source and id combined to give a unique uint64.
   CONTENT_EXPORT static uint64_t MetricValue(BackForwardCache::DisabledReason);
@@ -285,7 +293,11 @@
   absl::optional<base::TimeTicks> started_navigation_timestamp_;
   absl::optional<base::TimeTicks> navigated_away_from_main_document_timestamp_;
 
+  // TODO: Store BackForwardCacheCanStoreDocumentResultWithTree instead of
+  // storing unique_ptr of BackForwardCacheCanStoreDocumentResult and
+  // BackForwardCacheCanStoreTreeResult respectively.
   std::unique_ptr<BackForwardCacheCanStoreDocumentResult> page_store_result_;
+  std::unique_ptr<BackForwardCacheCanStoreTreeResult> page_store_tree_result_;
 
   // This value is updated only for navigations which are not same-document and
   // main-frame navigations.
diff --git a/content/browser/renderer_host/browsing_context_state.cc b/content/browser/renderer_host/browsing_context_state.cc
index f2dd0de..7d4865b 100644
--- a/content/browser/renderer_host/browsing_context_state.cc
+++ b/content/browser/renderer_host/browsing_context_state.cc
@@ -48,6 +48,14 @@
   }
 }
 
+RenderFrameProxyHost* BrowsingContextState::GetRenderFrameProxyHost(
+    SiteInstanceGroup* site_instance_group) const {
+  auto it = proxy_hosts_.find(site_instance_group->GetId());
+  if (it != proxy_hosts_.end())
+    return it->second.get();
+  return nullptr;
+}
+
 void BrowsingContextState::SetCurrentOrigin(
     const url::Origin& origin,
     bool is_potentially_trustworthy_unique_origin) {
@@ -111,4 +119,27 @@
   }
 }
 
+void BrowsingContextState::ActiveFrameCountIsZero(
+    SiteInstanceImpl* site_instance) {
+  // |site_instance| no longer contains any active RenderFrameHosts, so we don't
+  // need to maintain a proxy there anymore.
+  RenderFrameProxyHost* proxy = GetRenderFrameProxyHost(site_instance->group());
+  CHECK(proxy);
+
+  DeleteRenderFrameProxyHost(site_instance);
+}
+
+void BrowsingContextState::RenderProcessGone(
+    SiteInstanceImpl* instance,
+    const ChildProcessTerminationInfo& info) {
+  GetRenderFrameProxyHost(instance->group())->SetRenderFrameProxyCreated(false);
+}
+
+void BrowsingContextState::DeleteRenderFrameProxyHost(
+    SiteInstance* site_instance) {
+  static_cast<SiteInstanceImpl*>(site_instance)->RemoveObserver(this);
+  proxy_hosts_.erase(
+      static_cast<SiteInstanceImpl*>(site_instance)->group()->GetId());
+}
+
 }  // namespace content
\ No newline at end of file
diff --git a/content/browser/renderer_host/browsing_context_state.h b/content/browser/renderer_host/browsing_context_state.h
index a2dfc10..c635e0e 100644
--- a/content/browser/renderer_host/browsing_context_state.h
+++ b/content/browser/renderer_host/browsing_context_state.h
@@ -59,7 +59,8 @@
 // kLegacyOneToOneWithFrameTreeNode is currently enabled and will be removed
 // once the functionality gated behind kSwapForCrossBrowsingInstanceNavigations
 // is implemented.
-class BrowsingContextState : public base::RefCounted<BrowsingContextState> {
+class BrowsingContextState : public base::RefCounted<BrowsingContextState>,
+                             public SiteInstanceImpl::Observer {
  public:
   using RenderFrameProxyHostMap =
       std::unordered_map<SiteInstanceGroupId,
@@ -129,6 +130,9 @@
                          bool did_change_container_policy,
                          bool did_change_required_document_policy);
 
+  RenderFrameProxyHost* GetRenderFrameProxyHost(
+      SiteInstanceGroup* site_instance_group) const;
+
   // Set the current origin and notify proxies about the update.
   void SetCurrentOrigin(const url::Origin& origin,
                         bool is_potentially_trustworthy_unique_origin);
@@ -150,6 +154,14 @@
   // update.
   void SetIsAdSubframe(bool is_ad_subframe);
 
+  // Delete a RenderFrameProxyHost owned by this object.
+  void DeleteRenderFrameProxyHost(SiteInstance* site_instance);
+
+  // SiteInstanceImpl::Observer
+  void ActiveFrameCountIsZero(SiteInstanceImpl* site_instance) override;
+  void RenderProcessGone(SiteInstanceImpl* site_instance,
+                         const ChildProcessTerminationInfo& info) override;
+
  protected:
   friend class base::RefCounted<BrowsingContextState>;
 
diff --git a/content/browser/renderer_host/media/video_capture_host.cc b/content/browser/renderer_host/media/video_capture_host.cc
index 93134eef..4255f5b 100644
--- a/content/browser/renderer_host/media/video_capture_host.cc
+++ b/content/browser/renderer_host/media/video_capture_host.cc
@@ -93,7 +93,7 @@
 
 VideoCaptureHost::~VideoCaptureHost() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  for (auto it = controllers_.begin(); it != controllers_.end(); ) {
+  for (auto it = controllers_.begin(); it != controllers_.end();) {
     const base::WeakPtr<VideoCaptureController>& controller = it->second;
     if (controller) {
       const VideoCaptureControllerID controller_id(it->first);
@@ -189,7 +189,8 @@
 
   if (base::Contains(device_id_to_observer_map_, controller_id)) {
     device_id_to_observer_map_[controller_id]->OnStateChanged(
-        media::mojom::VideoCaptureState::STARTED);
+        media::mojom::VideoCaptureResult::NewState(
+            media::mojom::VideoCaptureState::STARTED));
     NotifyStreamAdded();
   }
 }
@@ -218,7 +219,8 @@
   const VideoCaptureControllerID controller_id(device_id);
   if (controllers_.find(controller_id) != controllers_.end()) {
     device_id_to_observer_map_[device_id]->OnStateChanged(
-        media::mojom::VideoCaptureState::STARTED);
+        media::mojom::VideoCaptureResult::NewState(
+            media::mojom::VideoCaptureState::STARTED));
     NotifyStreamAdded();
     return;
   }
@@ -238,7 +240,8 @@
 
   if (base::Contains(device_id_to_observer_map_, device_id)) {
     device_id_to_observer_map_[device_id]->OnStateChanged(
-        media::mojom::VideoCaptureState::STOPPED);
+        media::mojom::VideoCaptureResult::NewState(
+            media::mojom::VideoCaptureState::STOPPED));
   }
   device_id_to_observer_map_.erase(controller_id);
 
@@ -259,7 +262,8 @@
       it->second.get(), controller_id, this);
   if (base::Contains(device_id_to_observer_map_, device_id)) {
     device_id_to_observer_map_[device_id]->OnStateChanged(
-        media::mojom::VideoCaptureState::PAUSED);
+        media::mojom::VideoCaptureResult::NewState(
+            media::mojom::VideoCaptureState::PAUSED));
   }
 }
 
@@ -283,7 +287,8 @@
       session_id, params, it->second.get(), controller_id, this);
   if (base::Contains(device_id_to_observer_map_, device_id)) {
     device_id_to_observer_map_[device_id]->OnStateChanged(
-        media::mojom::VideoCaptureState::RESUMED);
+        media::mojom::VideoCaptureResult::NewState(
+            media::mojom::VideoCaptureState::RESUMED));
   }
 }
 
@@ -342,7 +347,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   media::VideoCaptureFormats formats_in_use;
   if (!media_stream_manager_->video_capture_manager()->GetDeviceFormatsInUse(
-           session_id, &formats_in_use)) {
+          session_id, &formats_in_use)) {
     DLOG(WARNING) << "Could not retrieve device format(s) in use";
   }
   std::move(callback).Run(formats_in_use);
@@ -386,7 +391,7 @@
 
   if (base::Contains(device_id_to_observer_map_, controller_id)) {
     device_id_to_observer_map_[controller_id]->OnStateChanged(
-        media::mojom::VideoCaptureState::FAILED);
+        media::mojom::VideoCaptureResult::NewErrorCode(error));
   }
 
   DeleteVideoCaptureController(controller_id, error);
@@ -401,7 +406,8 @@
 
   if (base::Contains(device_id_to_observer_map_, controller_id)) {
     device_id_to_observer_map_[controller_id]->OnStateChanged(
-        media::mojom::VideoCaptureState::ENDED);
+        media::mojom::VideoCaptureResult::NewState(
+            media::mojom::VideoCaptureState::ENDED));
   }
 
   DeleteVideoCaptureController(controller_id, media::VideoCaptureError::kNone);
@@ -426,7 +432,9 @@
   if (!controller) {
     if (base::Contains(device_id_to_observer_map_, controller_id)) {
       device_id_to_observer_map_[device_id]->OnStateChanged(
-          media::mojom::VideoCaptureState::FAILED);
+          media::mojom::VideoCaptureResult::NewErrorCode(
+              media::VideoCaptureError::
+                  kVideoCaptureControllerInvalidOrUnsupportedVideoCaptureParametersRequested));
     }
     controllers_.erase(controller_id);
     return;
diff --git a/content/browser/renderer_host/media/video_capture_unittest.cc b/content/browser/renderer_host/media/video_capture_unittest.cc
index c3a3246..b422cd22 100644
--- a/content/browser/renderer_host/media/video_capture_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_unittest.cc
@@ -186,7 +186,15 @@
 
  protected:
   // media::mojom::VideoCaptureObserver implementation.
-  MOCK_METHOD1(OnStateChanged, void(media::mojom::VideoCaptureState));
+  void OnStateChanged(media::mojom::VideoCaptureResultPtr result) override {
+    if (result->which() == media::mojom::VideoCaptureResult::Tag::STATE)
+      DoOnStateChanged(result->get_state());
+    else
+      DoOnVideoCaptureError(result->get_error_code());
+  }
+  MOCK_METHOD1(DoOnStateChanged, void(media::mojom::VideoCaptureState));
+  MOCK_METHOD1(DoOnVideoCaptureError, void(media::VideoCaptureError));
+
   void OnNewBuffer(int32_t buffer_id,
                    media::mojom::VideoBufferHandlePtr buffer_handle) override {
     DoOnNewBuffer(buffer_id);
@@ -207,7 +215,7 @@
         gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420);
 
     EXPECT_CALL(*this,
-                OnStateChanged(media::mojom::VideoCaptureState::STARTED));
+                DoOnStateChanged(media::mojom::VideoCaptureState::STARTED));
     EXPECT_CALL(*this, DoOnNewBuffer(_))
         .Times(AnyNumber())
         .WillRepeatedly(Return());
@@ -221,6 +229,21 @@
     run_loop.Run();
   }
 
+  void StartCaptureWithInvalidSession() {
+    media::VideoCaptureParams params;
+    params.requested_format = media::VideoCaptureFormat(
+        gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420);
+
+    EXPECT_CALL(
+        *this,
+        DoOnVideoCaptureError(
+            media::VideoCaptureError::
+                kVideoCaptureControllerInvalidOrUnsupportedVideoCaptureParametersRequested))
+        .Times(1);
+    host_->Start(DeviceId(), base::UnguessableToken(), params,
+                 observer_receiver_.BindNewPipeAndPassRemote());
+  }
+
   void StartAndImmediateStopCapture() {
     // Quickly start and then stop capture, without giving much chance for
     // asynchronous capture operations to produce frames.
@@ -233,13 +256,14 @@
 
     // |STARTED| is reported asynchronously, which may not be received if
     // capture is stopped immediately.
-    EXPECT_CALL(*this, OnStateChanged(media::mojom::VideoCaptureState::STARTED))
+    EXPECT_CALL(*this,
+                DoOnStateChanged(media::mojom::VideoCaptureState::STARTED))
         .Times(AtMost(1));
     host_->Start(DeviceId(), opened_session_id_, params,
                  observer_receiver_.BindNewPipeAndPassRemote());
 
     EXPECT_CALL(*this,
-                OnStateChanged(media::mojom::VideoCaptureState::STOPPED));
+                DoOnStateChanged(media::mojom::VideoCaptureState::STOPPED));
     host_->Stop(DeviceId());
     run_loop.RunUntilIdle();
   }
@@ -248,7 +272,8 @@
     InSequence s;
     base::RunLoop run_loop;
 
-    EXPECT_CALL(*this, OnStateChanged(media::mojom::VideoCaptureState::PAUSED));
+    EXPECT_CALL(*this,
+                DoOnStateChanged(media::mojom::VideoCaptureState::PAUSED));
     host_->Pause(DeviceId());
 
     media::VideoCaptureParams params;
@@ -256,7 +281,7 @@
         gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420);
 
     EXPECT_CALL(*this,
-                OnStateChanged(media::mojom::VideoCaptureState::RESUMED));
+                DoOnStateChanged(media::mojom::VideoCaptureState::RESUMED));
     host_->Resume(DeviceId(), opened_session_id_, params);
     run_loop.RunUntilIdle();
   }
@@ -264,7 +289,8 @@
   void StopCapture() {
     base::RunLoop run_loop;
 
-    EXPECT_CALL(*this, OnStateChanged(media::mojom::VideoCaptureState::STOPPED))
+    EXPECT_CALL(*this,
+                DoOnStateChanged(media::mojom::VideoCaptureState::STOPPED))
         .WillOnce(ExitMessageLoop(task_runner_, run_loop.QuitClosure()));
     host_->Stop(DeviceId());
 
@@ -284,7 +310,10 @@
   }
 
   void SimulateError() {
-    EXPECT_CALL(*this, OnStateChanged(media::mojom::VideoCaptureState::FAILED));
+    EXPECT_CALL(
+        *this,
+        DoOnVideoCaptureError(
+            media::VideoCaptureError::kIntentionalErrorRaisedByUnitTest));
     host_->OnError(DeviceId(),
                    media::VideoCaptureError::kIntentionalErrorRaisedByUnitTest);
     base::RunLoop().RunUntilIdle();
@@ -343,8 +372,13 @@
   StopCapture();
 }
 
+TEST_F(VideoCaptureTest, StartWithInvalidSessionId) {
+  StartCaptureWithInvalidSession();
+  StopCapture();
+}
+
 TEST_F(VideoCaptureTest, StartAndCaptureAndError) {
-  EXPECT_CALL(*this, OnStateChanged(media::mojom::VideoCaptureState::STOPPED))
+  EXPECT_CALL(*this, DoOnStateChanged(media::mojom::VideoCaptureState::STOPPED))
       .Times(0);
   StartCapture();
   WaitForOneCapturedBuffer();
@@ -363,7 +397,7 @@
 
   // When the session is closed via the stream without stopping capture, the
   // ENDED event is sent.
-  EXPECT_CALL(*this, OnStateChanged(media::mojom::VideoCaptureState::ENDED));
+  EXPECT_CALL(*this, DoOnStateChanged(media::mojom::VideoCaptureState::ENDED));
   CloseSession();
   base::RunLoop().RunUntilIdle();
 }
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index ea2ee416..5dd95be 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -3191,63 +3191,28 @@
   auto cross_origin_embedder_policy =
       CoepFromMainResponse(url, response_head_.get());
 
-  if (network::IsUrlPotentiallyTrustworthy(url)) {
-    // https://mikewest.github.io/corpp/#process-navigation-response
-    auto* parent = GetParentFrame();
-    // Fenced Frames should respect the outer frame's COEP.
-    // Note: we only check the outer document for fenced frames, because it's
-    // unclear if other embedded cases like Portals should inherit COEP from the
-    // embedder as well.
-    // TODO(https://crbug.com/1278207) add other embedded cases if needed.
-    if (GetNavigatingFrameType() == FrameType::kFencedFrameRoot) {
-      parent = GetParentFrameOrOuterDocument();
-    }
-    if (parent) {
-      const auto& parent_coep = parent->cross_origin_embedder_policy();
-      CrossOriginEmbedderPolicyReporter* parent_coep_reporter =
-          parent->coep_reporter();
-
-      // Some special URLs not loaded using the network are inheriting the
-      // Cross-Origin-Embedder-Policy header from their parent.
-      // TODO(https://crbug.com/1153648) Add COEP into the PolicyContainer and
-      // remove this fragile inheritance mechanism.
-      const bool inherit_coep_from_parent =
-          url.SchemeIsBlob() || url.SchemeIs(url::kDataScheme) ||
-          GetContentClient()
-              ->browser()
-              ->ShouldInheritCrossOriginEmbedderPolicyImplicitly(url);
-      if (inherit_coep_from_parent)
-        cross_origin_embedder_policy.value = parent_coep.value;
-
-      if (CoepBlockIframe(parent_coep.report_only_value,
-                          cross_origin_embedder_policy.value, anonymous())) {
-        if (parent_coep_reporter) {
-          parent_coep_reporter->QueueNavigationReport(redirect_chain_[0],
-                                                      /*report_only=*/true);
-        }
-      }
-
-      if (CoepBlockIframe(parent_coep.value, cross_origin_embedder_policy.value,
-                          anonymous())) {
-        if (parent_coep_reporter) {
-          parent_coep_reporter->QueueNavigationReport(redirect_chain_[0],
-                                                      /*report_only=*/false);
-        }
-        // TODO(https://crbug.com/1172169): Investigate what must be done in
-        // case of a download.
-        OnRequestFailedInternal(network::URLLoaderCompletionStatus(
-                                    network::mojom::BlockedByResponseReason::
-                                        kCoepFrameResourceNeedsCoepHeader),
-                                false /* skip_throttles */,
-                                absl::nullopt /* error_page_content */,
-                                false /* collapse_frame */);
-        // DO NOT ADD CODE after this. The previous call to
-        // OnRequestFailedInternal has destroyed the NavigationRequest.
-        return;
-      }
-    }
-  } else {
+  // [spec]: https://html.spec.whatwg.org/C/#obtain-an-embedder-policy
+  //
+  // 1. Let policy be a new embedder policy.
+  // 2. If environment is a non-secure context, then return policy.
+  if (!network::IsUrlPotentiallyTrustworthy(url))
     cross_origin_embedder_policy = network::CrossOriginEmbedderPolicy();
+
+  // [spec]: https://html.spec.whatwg.org/C/#process-a-navigate-response
+  // 4. if [...] the result of checking a navigation response's adherence to its
+  // embedder policy [...], then set failure to true.
+  if (!CheckResponseAdherenceToCoep(&cross_origin_embedder_policy, url)) {
+    // TODO(https://crbug.com/1172169): Investigate what must be done in
+    // case of a download.
+    OnRequestFailedInternal(network::URLLoaderCompletionStatus(
+                                network::mojom::BlockedByResponseReason::
+                                    kCoepFrameResourceNeedsCoepHeader),
+                            false /* skip_throttles */,
+                            absl::nullopt /* error_page_content */,
+                            false /* collapse_frame */);
+    return;
+    // DO NOT ADD CODE after this. The previous call to
+    // OnRequestFailedInternal has destroyed the NavigationRequest.
   }
 
   // Select an appropriate renderer to commit the navigation.
@@ -6696,9 +6661,85 @@
   commit_params_->force_enabled_origin_trials = trials;
 }
 
+// [spec]:
+// https://html.spec.whatwg.org/C/#check-a-navigation-response's-adherence-to-its-embedder-policy
+//
+// Return whether the child's |coep| is compatible with its parent's COEP. It
+// also sends COEP reports if needed.
+bool NavigationRequest::CheckResponseAdherenceToCoep(
+    network::CrossOriginEmbedderPolicy* coep,
+    const GURL& url) {
+  // TODO(arthursonzogni): This check looks fishy to me. Consider removing it.
+  // It was kept during refactoring, in order not to break WPT test:
+  // web-bundle/subresource-loading/link-coep.https.tentative.html
+  // This allows non trustworthy URL like urn: URL to bypass COEP check.
+  if (!network::IsUrlPotentiallyTrustworthy(url))
+    return true;
+
+  // Fenced Frames should respect the outer frame's COEP.
+  // Note: we only check the outer document for fenced frames, because it's
+  // unclear if other embedded cases like Portals should inherit COEP from
+  // the embedder as well.
+  // TODO(https://crbug.com/1278207) add other embedded cases if needed.
+  RenderFrameHostImpl* const parent =
+      GetNavigatingFrameType() == FrameType::kFencedFrameRoot
+          ? GetParentFrameOrOuterDocument()
+          : GetParentFrame();
+
+  // [spec]: 1. If target is not a child browsing context, then return true.
+  if (!parent)
+    return true;
+
+  // [spec]: 2. Let parentPolicy be target's container document's policy
+  //            container's embedder policy.
+  const auto& parent_coep = parent->cross_origin_embedder_policy();
+  CrossOriginEmbedderPolicyReporter* parent_coep_reporter =
+      parent->coep_reporter();
+
+  // Some special URLs not loaded using the network are inheriting the
+  // Cross-Origin-Embedder-Policy header from their parent.
+  // TODO(https://crbug.com/1153648) Add COEP into the PolicyContainer and
+  // remove this fragile inheritance mechanism.
+  const bool inherit_coep_from_parent =
+      url.SchemeIsBlob() || url.SchemeIs(url::kDataScheme) ||
+      GetContentClient()
+          ->browser()
+          ->ShouldInheritCrossOriginEmbedderPolicyImplicitly(url);
+  if (inherit_coep_from_parent)
+    coep->value = parent_coep.value;
+
+  // [spec]: 3. If parentPolicy's report-only value is compatible with
+  // cross-origin isolation and responsePolicy's value is not, then queue a
+  // cross-origin embedder policy inheritance violation [...].
+  if (CoepBlockIframe(parent_coep.report_only_value, coep->value,
+                      anonymous())) {
+    if (parent_coep_reporter) {
+      parent_coep_reporter->QueueNavigationReport(redirect_chain_[0],
+                                                  /*report_only=*/true);
+    }
+  }
+
+  // [spec]: 4. If parentPolicy's value is not compatible with cross-origin
+  // isolation or responsePolicy's value is compatible with cross-origin
+  // isolation, then return true.
+  if (!CoepBlockIframe(parent_coep.value, coep->value, anonymous()))
+    return true;
+
+  // [spec]: 5 Queue a cross-origin embedder policy inheritance violation with
+  // response, "navigation", parentPolicy's reporting endpoint, "enforce", and
+  // target's container document's relevant settings object.
+  if (parent_coep_reporter) {
+    parent_coep_reporter->QueueNavigationReport(redirect_chain_[0],
+                                                /*report_only=*/false);
+  }
+
+  // [spec]: 6. Return false.
+  return false;
+}
+
 absl::optional<network::mojom::BlockedByResponseReason>
 NavigationRequest::EnforceCOEP() {
-  // https://html.spec.whatwg.org/#check-a-navigation-response's-adherence-to-its-embedder-policy
+  // https://html.spec.whatwg.org/C/#check-a-navigation-response's-adherence-to-its-embedder-policy
   auto* parent_frame = GetParentFrame();
   if (!parent_frame) {
     return absl::nullopt;
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index 375f9e0..bfe2dae 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -1342,6 +1342,9 @@
 
   void CreateCoepReporter(StoragePartition* storage_partition);
 
+  bool CheckResponseAdherenceToCoep(network::CrossOriginEmbedderPolicy* coep,
+                                    const GURL& url);
+
   absl::optional<network::mojom::BlockedByResponseReason> EnforceCOEP();
 
   // Check the COOP value of the page is compatible with the COEP value of each
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 385e19b1..008c1b1 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -3220,6 +3220,32 @@
   }
 }
 
+RenderFrameProxyHost* RenderFrameHostImpl::GetProxyToParent() {
+  if (is_main_frame())
+    return nullptr;
+
+  return browsing_context_state_->GetRenderFrameProxyHost(
+      GetParent()->GetSiteInstance()->group());
+}
+
+RenderFrameProxyHost* RenderFrameHostImpl::GetProxyToOuterDelegate() {
+  // Only the main frame should be able to reach the outer WebContents.
+  DCHECK(is_main_frame());
+  int outer_contents_frame_tree_node_id =
+      frame_tree_node_->frame_tree()
+          ->delegate()
+          ->GetOuterDelegateFrameTreeNodeId();
+  FrameTreeNode* outer_contents_frame_tree_node =
+      FrameTreeNode::GloballyFindByID(outer_contents_frame_tree_node_id);
+  if (!outer_contents_frame_tree_node ||
+      !outer_contents_frame_tree_node->parent()) {
+    return nullptr;
+  }
+
+  return browsing_context_state_->GetRenderFrameProxyHost(
+      outer_contents_frame_tree_node->parent()->GetSiteInstance()->group());
+}
+
 void RenderFrameHostImpl::PropagateEmbeddingTokenToParentFrame() {
   // Protect against calls from WebContentsImpl::AttachInnerWebContents() that
   // happen before RFHI::SetEmbeddingToken() gets called, which is where the
@@ -3241,14 +3267,12 @@
 
   if (RequiresProxyToParent()) {
     // This subframe should have a remote parent frame.
-    target_render_frame_proxy =
-        frame_tree_node()->render_manager()->GetProxyToParent();
+    target_render_frame_proxy = GetProxyToParent();
     DCHECK(target_render_frame_proxy);
   } else if (is_main_frame()) {
     // The main frame in an inner web contents could have a delegate in the
     // outer web contents, so we need to account for that as well.
-    target_render_frame_proxy =
-        frame_tree_node()->render_manager()->GetProxyToOuterDelegate();
+    target_render_frame_proxy = GetProxyToOuterDelegate();
   }
 
   // Propagate the token to the right process, if a proxy was found.
@@ -4813,8 +4837,7 @@
   RenderFrameHostImpl* parent_or_outer_document =
       GetParentOrOuterDocumentOrEmbedder();
   if (parent_or_outer_document) {
-    RenderFrameProxyHost* proxy_host =
-        frame_tree_node()->render_manager()->GetProxyToOuterDelegate();
+    RenderFrameProxyHost* proxy_host = GetProxyToOuterDelegate();
     DCHECK(proxy_host);
     parent_or_outer_document->AdvanceFocus(
         reverse ? blink::mojom::FocusType::kBackward
@@ -5464,8 +5487,7 @@
           DisallowActivationReasonId::kSetNeedsOcclusionTracking))
     return;
 
-  RenderFrameProxyHost* proxy =
-      frame_tree_node()->render_manager()->GetProxyToParent();
+  RenderFrameProxyHost* proxy = GetProxyToParent();
   if (!proxy) {
     bad_message::ReceivedBadMessage(GetProcess(),
                                     bad_message::RFH_NO_PROXY_TO_PARENT);
@@ -5624,8 +5646,7 @@
 
   // Only frames with an out-of-process parent frame should be sending this
   // message.
-  RenderFrameProxyHost* proxy =
-      frame_tree_node()->render_manager()->GetProxyToParent();
+  RenderFrameProxyHost* proxy = GetProxyToParent();
   if (!proxy) {
     bad_message::ReceivedBadMessage(GetProcess(),
                                     bad_message::RFH_NO_PROXY_TO_PARENT);
@@ -5759,8 +5780,7 @@
   DCHECK(lifecycle_state() == LifecycleStateImpl::kActive ||
          lifecycle_state() == LifecycleStateImpl::kPrerendering);
 
-  RenderFrameProxyHost* proxy =
-      frame_tree_node()->render_manager()->GetProxyToParent();
+  RenderFrameProxyHost* proxy = GetProxyToParent();
   if (!proxy) {
     bad_message::ReceivedBadMessage(GetProcess(),
                                     bad_message::RFH_NO_PROXY_TO_PARENT);
@@ -5853,21 +5873,24 @@
   // details.
   DCHECK_NE(reason,
             BackForwardCacheMetrics::NotRestoredReason::kIgnoreEventAndEvict);
-  auto can_store =
+  BackForwardCacheCanStoreDocumentResultWithTree can_store =
       frame_tree()->controller().GetBackForwardCache().CanStorePageNow(
           GetMainFrame());
-  can_store.No(reason);
-  EvictFromBackForwardCacheWithReasons(can_store);
+  can_store.flattened_reasons.No(reason);
+  EvictFromBackForwardCacheWithReasons(can_store.flattened_reasons,
+                                       std::move(can_store.tree_reasons));
 }
 
 void RenderFrameHostImpl::EvictFromBackForwardCacheWithReasons(
-    const BackForwardCacheCanStoreDocumentResult& can_store) {
+    const BackForwardCacheCanStoreDocumentResult& can_store_flattened,
+    std::unique_ptr<BackForwardCacheCanStoreTreeResult> can_store_tree) {
   TRACE_EVENT2("navigation", "RenderFrameHostImpl::EvictFromBackForwardCache",
-               "can_store", can_store.ToString(), "rfh",
+               "can_store", can_store_flattened.ToString(), "rfh",
                static_cast<void*>(this));
-  TRACE_EVENT(
-      "navigation", "RenderFrameHostImpl::EvictFromBackForwardCacheWithReasons",
-      ChromeTrackEvent::kBackForwardCacheCanStoreDocumentResult, can_store);
+  TRACE_EVENT("navigation",
+              "RenderFrameHostImpl::EvictFromBackForwardCacheWithReasons",
+              ChromeTrackEvent::kBackForwardCacheCanStoreDocumentResult,
+              can_store_flattened);
   DCHECK(IsBackForwardCacheEnabled());
 
   if (is_evicted_from_back_forward_cache_)
@@ -5884,8 +5907,10 @@
   // TODO(hajimehoshi): Record the 'race condition' by JavaScript execution when
   // |in_back_forward_cache| is false.
   BackForwardCacheMetrics* metrics = top_document->GetBackForwardCacheMetrics();
-  if (in_back_forward_cache && metrics)
-    metrics->MarkNotRestoredWithReason(can_store);
+  if (in_back_forward_cache && metrics) {
+    metrics->FinalizeNotRestoredReasons(can_store_flattened,
+                                        std::move(can_store_tree));
+  }
 
   if (!in_back_forward_cache) {
     TRACE_EVENT0("navigation", "BackForwardCache_EvictAfterDocumentRestored");
@@ -6013,7 +6038,7 @@
       continue;
 
     RenderFrameProxyHost* child_proxy =
-        rfh->frame_tree_node()->render_manager()->GetRenderFrameProxyHost(
+        rfh->browsing_context_state()->GetRenderFrameProxyHost(
             static_cast<SiteInstanceImpl*>(parent_site_instance)->group());
     child_proxy->GetAssociatedRemoteFrame()->WillEnterFullscreen(
         options.Clone());
@@ -6212,8 +6237,7 @@
 void RenderFrameHostImpl::ScrollRectToVisibleInParentFrame(
     const gfx::Rect& rect_to_scroll,
     blink::mojom::ScrollIntoViewParamsPtr params) {
-  RenderFrameProxyHost* proxy =
-      frame_tree_node_->render_manager()->GetProxyToParent();
+  RenderFrameProxyHost* proxy = GetProxyToParent();
   if (!proxy)
     return;
   proxy->ScrollRectToVisible(rect_to_scroll, std::move(params));
@@ -6227,8 +6251,7 @@
           DisallowActivationReasonId::kDispatchLoad))
     return;
 
-  RenderFrameProxyHost* proxy =
-      frame_tree_node()->render_manager()->GetProxyToParent();
+  RenderFrameProxyHost* proxy = GetProxyToParent();
   if (!proxy) {
     // Only frames with an out-of-process parent frame should be sending this
     // message.
@@ -6916,9 +6939,8 @@
     mojo::PendingAssociatedReceiver<blink::mojom::FencedFrameOwnerHost>
         pending_receiver,
     CreateFencedFrameCallback callback) {
-  // TODO(btiszka): Add blink::features::IsFencedFramesEnabled() check
-  // after kFencedFrames has been switched to FEATURE_DISABLED_BY_DEFAULT
-  if (!blink::features::IsFencedFramesMPArchBased()) {
+  if (!blink::features::IsFencedFramesEnabled() ||
+      !blink::features::IsFencedFramesMPArchBased()) {
     bad_message::ReceivedBadMessage(
         GetProcess(), bad_message::RFH_FENCED_FRAME_MOJO_WHEN_DISABLED);
     return;
@@ -12088,17 +12110,19 @@
   RenderFrameHostImpl* top_document = this;
   while (RenderFrameHostImpl* parent = top_document->GetParent())
     top_document = parent;
-  BackForwardCacheCanStoreDocumentResult can_store =
+  BackForwardCacheCanStoreDocumentResultWithTree can_store =
       frame_tree()->controller().GetBackForwardCache().CanStorePageNow(
           top_document);
 
   TRACE_EVENT("navigation",
               "RenderFrameHostImpl::MaybeEvictFromBackForwardCache",
-              "render_frame_host", this, "can_store", can_store.ToString());
+              "render_frame_host", this, "can_store",
+              can_store.flattened_reasons.ToString());
 
   if (can_store)
     return;
-  EvictFromBackForwardCacheWithReasons(can_store);
+  EvictFromBackForwardCacheWithReasons(can_store.flattened_reasons,
+                                       std::move(can_store.tree_reasons));
 }
 
 void RenderFrameHostImpl::LogCannotCommitOriginCrashKeys(
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index d2687cc..0a2fe1a 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -482,7 +482,9 @@
   void EvictFromBackForwardCacheWithReason(
       BackForwardCacheMetrics::NotRestoredReason reason);
   void EvictFromBackForwardCacheWithReasons(
-      const BackForwardCacheCanStoreDocumentResult& can_store);
+      const BackForwardCacheCanStoreDocumentResult& can_store_flat,
+      std::unique_ptr<BackForwardCacheCanStoreTreeResult> can_store_tree =
+          nullptr);
 
   // Only for testing sticky WebBackForwardCacheDisablingFeature.
   void UseDummyStickyBackForwardCacheDisablingFeatureForTesting();
@@ -2412,6 +2414,11 @@
     return browsing_context_state_;
   }
 
+  // Retrieve proxies in a way that is no longer dependent on access to
+  // FrameTreeNode or RenderFrameHostManager.
+  RenderFrameProxyHost* GetProxyToParent();
+  RenderFrameProxyHost* GetProxyToOuterDelegate();
+
  protected:
   friend class RenderFrameHostFactory;
 
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc
index 4864b53..dabd059 100644
--- a/content/browser/renderer_host/render_frame_host_manager.cc
+++ b/content/browser/renderer_host/render_frame_host_manager.cc
@@ -647,7 +647,8 @@
   // RenderFrameHostManager, which also removes their respective
   // SiteInstanceImpl::Observer.
   for (auto& it : *proxy_hosts)
-    DeleteRenderFrameProxyHost(it.second->GetSiteInstance());
+    browsing_context_state_->DeleteRenderFrameProxyHost(
+        it.second->GetSiteInstance());
 }
 
 std::unique_ptr<StoredPage> RenderFrameHostManager::CollectPage(
@@ -725,11 +726,11 @@
     BackForwardCacheImpl& back_forward_cache =
         GetNavigationController().GetBackForwardCache();
 
-    auto can_store =
+    BackForwardCacheCanStoreDocumentResultWithTree can_store =
         back_forward_cache.CanStorePageNow(old_render_frame_host.get());
     TRACE_EVENT("navigation", "BackForwardCache_MaybeStorePage",
                 "old_render_frame_host", old_render_frame_host, "can_store",
-                can_store.ToString());
+                can_store.flattened_reasons.ToString());
     if (can_store) {
       auto stored_page = CollectPage(std::move(old_render_frame_host));
       auto entry =
@@ -742,8 +743,10 @@
       return;
     }
 
-    if (old_page_back_forward_cache_metrics)
-      old_page_back_forward_cache_metrics->MarkNotRestoredWithReason(can_store);
+    if (old_page_back_forward_cache_metrics) {
+      old_page_back_forward_cache_metrics->FinalizeNotRestoredReasons(
+          can_store.flattened_reasons, std::move(can_store.tree_reasons));
+    }
   }
 
   // Create a replacement proxy for the old RenderFrameHost when we're switching
@@ -859,10 +862,14 @@
   stored_page_to_restore_ = std::move(stored_page);
 }
 
+// TODO(crbug.com/1270671): When this method is migrated to BrowsingContextState
+// we need to determine where/when we need to reset proxy hosts. In particular
+// we should consider how pending-delete RenderFrameHosts are handled after
+// cross-BCG navigations.
 void RenderFrameHostManager::ResetProxyHosts() {
   for (const auto& pair : browsing_context_state_->proxy_hosts()) {
     static_cast<SiteInstanceImpl*>(pair.second->GetSiteInstance())
-        ->RemoveObserver(this);
+        ->RemoveObserver(browsing_context_state_.get());
   }
   browsing_context_state_->proxy_hosts().clear();
 }
@@ -1423,12 +1430,6 @@
       dest_url_info(dest_url_info),
       relation(relation_to_current) {}
 
-void RenderFrameHostManager::RenderProcessGone(
-    SiteInstanceImpl* instance,
-    const ChildProcessTerminationInfo& info) {
-  GetRenderFrameProxyHost(instance->group())->SetRenderFrameProxyCreated(false);
-}
-
 void RenderFrameHostManager::CancelPendingIfNecessary(
     RenderFrameHostImpl* render_frame_host) {
   if (render_frame_host == speculative_render_frame_host_.get()) {
@@ -1477,16 +1478,6 @@
   }
 }
 
-void RenderFrameHostManager::ActiveFrameCountIsZero(
-    SiteInstanceImpl* site_instance) {
-  // |site_instance| no longer contains any active RenderFrameHosts, so we don't
-  // need to maintain a proxy there anymore.
-  RenderFrameProxyHost* proxy = GetRenderFrameProxyHost(site_instance->group());
-  CHECK(proxy);
-
-  DeleteRenderFrameProxyHost(site_instance);
-}
-
 RenderFrameProxyHost* RenderFrameHostManager::CreateRenderFrameProxyHost(
     SiteInstance* site_instance,
     scoped_refptr<RenderViewHostImpl> rvh) {
@@ -1499,7 +1490,8 @@
       new RenderFrameProxyHost(site_instance, std::move(rvh), frame_tree_node_);
   browsing_context_state_->proxy_hosts()[site_instance_group_id] =
       base::WrapUnique(proxy_host);
-  static_cast<SiteInstanceImpl*>(site_instance)->AddObserver(this);
+  static_cast<SiteInstanceImpl*>(site_instance)
+      ->AddObserver(browsing_context_state_.get());
 
   TRACE_EVENT_INSTANT("navigation",
                       "RenderFrameHostManager::CreateRenderFrameProxyHost",
@@ -1509,9 +1501,7 @@
 
 void RenderFrameHostManager::DeleteRenderFrameProxyHost(
     SiteInstance* site_instance) {
-  static_cast<SiteInstanceImpl*>(site_instance)->RemoveObserver(this);
-  browsing_context_state_->proxy_hosts().erase(
-      static_cast<SiteInstanceImpl*>(site_instance)->group()->GetId());
+  browsing_context_state_->DeleteRenderFrameProxyHost(site_instance);
 }
 
 ShouldSwapBrowsingInstance
@@ -3354,7 +3344,7 @@
         CHECK(!base::Contains(browsing_context_state_->proxy_hosts(),
                               proxy.second->site_instance_group()->GetId()));
         static_cast<SiteInstanceImpl*>(proxy.second->GetSiteInstance())
-            ->AddObserver(this);
+            ->AddObserver(browsing_context_state_.get());
         TRACE_EVENT_INSTANT(
             "navigation", "RenderFrameHostManager::CommitPending_RestoreProxy",
             ChromeTrackEvent::kRenderFrameProxyHost, *proxy.second);
@@ -3655,11 +3645,10 @@
 
 RenderFrameProxyHost* RenderFrameHostManager::GetRenderFrameProxyHost(
     SiteInstanceGroup* site_instance_group) const {
-  auto it =
-      browsing_context_state_->proxy_hosts().find(site_instance_group->GetId());
-  if (it != browsing_context_state_->proxy_hosts().end())
-    return it->second.get();
-  return nullptr;
+  // TODO(crbug.com/1270671): update all call sites of this method when the
+  // browsing_context_state_ is removed, so that the correct RenderFrameHost and
+  // associated BrowsingContextState are used.
+  return browsing_context_state_->GetRenderFrameProxyHost(site_instance_group);
 }
 
 size_t RenderFrameHostManager::GetProxyCount() {
diff --git a/content/browser/renderer_host/render_frame_host_manager.h b/content/browser/renderer_host/render_frame_host_manager.h
index e116184..d2a65ba 100644
--- a/content/browser/renderer_host/render_frame_host_manager.h
+++ b/content/browser/renderer_host/render_frame_host_manager.h
@@ -109,8 +109,7 @@
 //   RenderFrameProxyHost, to be used (for example) if the user goes back. The
 //   process only stays live if another tab is using it, but if so, the existing
 //   frame relationships will be maintained.
-class CONTENT_EXPORT RenderFrameHostManager
-    : public SiteInstanceImpl::Observer {
+class CONTENT_EXPORT RenderFrameHostManager {
  public:
   // Functions implemented by our owner that we need.
   //
@@ -486,11 +485,6 @@
     return browsing_context_state_->proxy_hosts();
   }
 
-  // SiteInstanceImpl::Observer
-  void ActiveFrameCountIsZero(SiteInstanceImpl* site_instance) override;
-  void RenderProcessGone(SiteInstanceImpl* site_instance,
-                         const ChildProcessTerminationInfo& info) override;
-
   // Cancels and destroys the pending or speculative RenderFrameHost if they
   // match the provided |render_frame_host|.
   void CancelPendingIfNecessary(RenderFrameHostImpl* render_frame_host);
diff --git a/content/browser/renderer_host/render_frame_proxy_host.cc b/content/browser/renderer_host/render_frame_proxy_host.cc
index 2f70f61..71ead2f 100644
--- a/content/browser/renderer_host/render_frame_proxy_host.cc
+++ b/content/browser/renderer_host/render_frame_proxy_host.cc
@@ -263,8 +263,7 @@
     // never have a RenderFrameHost as a parent.
     RenderFrameProxyHost* parent_proxy =
         frame_tree_node_->parent()
-            ->frame_tree_node()
-            ->render_manager()
+            ->browsing_context_state()
             ->GetRenderFrameProxyHost(site_instance_group_.get());
     CHECK(parent_proxy);
 
@@ -610,9 +609,8 @@
       // proxy wasn't created), use an empty |translated_source_token| (see
       // https://crbug.com/485520 for discussion on why this is ok).
       RenderFrameProxyHost* source_proxy_in_target_site_instance_group =
-          source_rfh->frame_tree_node()
-              ->render_manager()
-              ->GetRenderFrameProxyHost(target_site_instance->group());
+          source_rfh->browsing_context_state()->GetRenderFrameProxyHost(
+              target_site_instance->group());
       if (source_proxy_in_target_site_instance_group) {
         translated_source_token =
             source_proxy_in_target_site_instance_group->GetFrameToken();
@@ -769,11 +767,10 @@
   RenderFrameHostImpl* source_rfh = RenderFrameHostImpl::FromFrameToken(
       GetProcess()->GetID(), source_frame_token);
   RenderFrameProxyHost* source_proxy =
-      source_rfh ? source_rfh->frame_tree_node()
-                       ->render_manager()
-                       ->GetRenderFrameProxyHost(
-                           target_rfh->GetSiteInstance()->group())
-                 : nullptr;
+      source_rfh
+          ? source_rfh->browsing_context_state()->GetRenderFrameProxyHost(
+                target_rfh->GetSiteInstance()->group())
+          : nullptr;
 
   target_rfh->AdvanceFocus(focus_type, source_proxy);
   frame_tree_node_->current_frame_host()->delegate()->OnAdvanceFocus(
diff --git a/content/browser/resources/quota/OWNERS b/content/browser/resources/quota/OWNERS
new file mode 100644
index 0000000..fa6aa59
--- /dev/null
+++ b/content/browser/resources/quota/OWNERS
@@ -0,0 +1 @@
+file://storage/browser/quota/OWNERS
\ No newline at end of file
diff --git a/content/browser/resources/quota/quota_internals.css b/content/browser/resources/quota/quota_internals.css
index 8ff7f8a1..14b40aa 100644
--- a/content/browser/resources/quota/quota_internals.css
+++ b/content/browser/resources/quota/quota_internals.css
@@ -26,6 +26,11 @@
   text-align: center;
 }
 
+#eviction-tbody {
+  margin: auto;
+  text-align: center;
+}
+
 hr {
   border-top: 1px solid rgb(48, 57, 66);
   width: 100%;
diff --git a/content/browser/resources/quota/quota_internals.html b/content/browser/resources/quota/quota_internals.html
index e491a6f..09be6533 100644
--- a/content/browser/resources/quota/quota_internals.html
+++ b/content/browser/resources/quota/quota_internals.html
@@ -21,12 +21,31 @@
     <tbody id="listeners-tbody"></tbody>
   </table>
 
+  <h2>Eviction Statistics</h2>
+  <table>
+    <thead>
+      <th>Errors on Getting Usage and Quota</th>
+      <th>Evicted Buckets</th>
+      <th>Eviction Rounds</th>
+      <th>Skipped Eviction Rounds</th>
+    </thead>
+    <tbody id="eviction-tbody"></tbody>
+  </table>
+
   <template id="listener-row">
     <tr>
       <td class="total-space"></td>
       <td class="available-space"></td>
     </tr>
   </template>
+  <template id="eviction-row">
+    <tr>
+      <td class="errors-on-getting-usage-and-quota"></td>
+      <td class="evicted-buckets"></td>
+      <td class="eviction-rounds"></td>
+      <td class="skipped-eviction-rounds"></td>
+    </tr>
+  </template>
 </body>
 </html>
 
diff --git a/content/browser/resources/quota/quota_internals.js b/content/browser/resources/quota/quota_internals.js
index 7fb64d08..6605851 100644
--- a/content/browser/resources/quota/quota_internals.js
+++ b/content/browser/resources/quota/quota_internals.js
@@ -5,7 +5,10 @@
 import {$} from 'chrome://resources/js/util.m.js';
 import {QuotaInternalsHandler, QuotaInternalsHandlerRemote} from './quota_internals.mojom-webui.js';
 
-function RenderData(response) {
+/**
+ * @param response
+ */
+function renderDiskAvailability(response) {
   const rowTemplate = /** @type {!HTMLTemplateElement} */ ($('listener-row'));
   const tableBody = /** @type {!HTMLTableElement} */ ($('listeners-tbody'));
   const listenerRowTemplate =
@@ -27,13 +30,37 @@
   tableBody.append(listenerRow);
 }
 
+/**
+ * @param response
+ */
+function renderEvictionStats(response) {
+  const rowTemplate = /** @type {!HTMLTemplateElement} */ ($('eviction-row'));
+  const evictionRowTemplate =
+      /** @type {!HTMLTemplateElement} */ (rowTemplate.cloneNode(true));
+  const evictionRow = evictionRowTemplate.content;
+
+  const tableBody = /** @type {!HTMLTableElement} */ ($('eviction-tbody'));
+  tableBody.innerHTML = trustedTypes.emptyHTML;
+
+  evictionRow.querySelector('.errors-on-getting-usage-and-quota').textContent =
+      response.evictionStatistics['errors-on-getting-usage-and-quota'];
+  evictionRow.querySelector('.evicted-buckets').textContent =
+      response.evictionStatistics['evicted-buckets'];
+  evictionRow.querySelector('.eviction-rounds').textContent =
+      response.evictionStatistics['eviction-rounds'];
+  evictionRow.querySelector('.skipped-eviction-rounds').textContent =
+      response.evictionStatistics['skipped-eviction-rounds'];
+
+  tableBody.appendChild(evictionRow);
+}
+
 document.addEventListener('DOMContentLoaded', () => {
   /**
    * @type {!QuotaInternalsHandlerRemote}
    */
   const pageHandler = QuotaInternalsHandler.getRemote();
 
-  pageHandler.getDiskAvailability().then((response) => {
-    RenderData(response);
-  });
+  pageHandler.getDiskAvailability().then(renderDiskAvailability);
+  pageHandler.getStatistics().then(renderEvictionStats);
+
 });
\ No newline at end of file
diff --git a/content/browser/sandbox_mac_unittest.mm b/content/browser/sandbox_mac_unittest.mm
index b200b623..a69810d9 100644
--- a/content/browser/sandbox_mac_unittest.mm
+++ b/content/browser/sandbox_mac_unittest.mm
@@ -96,6 +96,7 @@
         sandbox::mojom::Sandbox::kPrintCompositor,
         sandbox::mojom::Sandbox::kRenderer,
         sandbox::mojom::Sandbox::kService,
+        sandbox::mojom::Sandbox::kServiceWithJit,
         sandbox::mojom::Sandbox::kUtility,
     };
 
diff --git a/content/browser/sandbox_parameters_mac.mm b/content/browser/sandbox_parameters_mac.mm
index 8550e68..76a22420 100644
--- a/content/browser/sandbox_parameters_mac.mm
+++ b/content/browser/sandbox_parameters_mac.mm
@@ -218,6 +218,7 @@
     case sandbox::mojom::Sandbox::kPrintCompositor:
     case sandbox::mojom::Sandbox::kRenderer:
     case sandbox::mojom::Sandbox::kService:
+    case sandbox::mojom::Sandbox::kServiceWithJit:
     case sandbox::mojom::Sandbox::kUtility:
       SetupCommonSandboxParameters(client);
       break;
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index a533221..90e22cb 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -1855,14 +1855,14 @@
 }
 
 // Ensure that we kill the renderer process if we try to create a
-// fenced-frame when the blink::features::kFencedFrames/mparch
-// feature is not enabled.
+// fenced-frame when the blink::features::kFencedFrames feature is not enabled
+// or the blink::features::kFencedFrames/mparch feature is not enabled.
 IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest,
                        CreateFencedFrameWhenFeatureDisabled) {
   GURL foo("http://foo.com/simple_page.html");
   EXPECT_TRUE(NavigateToURL(shell(), foo));
   EXPECT_EQ(u"OK", shell()->web_contents()->GetTitle());
-  EXPECT_FALSE(blink::features::IsFencedFramesMPArchBased());
+  EXPECT_FALSE(blink::features::IsFencedFramesEnabled());
 
   RenderFrameHostImpl* compromised_rfh = static_cast<RenderFrameHostImpl*>(
       shell()->web_contents()->GetMainFrame());
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 110a43f..ee2df90 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -4923,12 +4923,21 @@
   EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), cross_url);
 }
 
+// crbug.com/1281755
+#if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+#define MAYBE_NavigateProxyAndDetachBeforeProvisionalFrameCreation \
+  DISABLED_NavigateProxyAndDetachBeforeProvisionalFrameCreation
+#else
+#define MAYBE_NavigateProxyAndDetachBeforeProvisionalFrameCreation \
+  NavigateProxyAndDetachBeforeProvisionalFrameCreation
+#endif
 // Test for https://crbug.com/526304, where a parent frame executes a
 // remote-to-local navigation on a child frame and immediately removes the same
 // child frame.  This test exercises the path where the detach happens before
 // the provisional local frame is created.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       NavigateProxyAndDetachBeforeProvisionalFrameCreation) {
+IN_PROC_BROWSER_TEST_P(
+    SitePerProcessBrowserTest,
+    MAYBE_NavigateProxyAndDetachBeforeProvisionalFrameCreation) {
   GURL main_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
index 48492fa..5f1847c 100644
--- a/content/browser/utility_process_host.cc
+++ b/content/browser/utility_process_host.cc
@@ -182,6 +182,10 @@
       process_->EnableWarmUpConnection();
     }
 #else
+#if defined(OS_MAC)
+    if (sandbox_type_ == sandbox::mojom::Sandbox::kServiceWithJit)
+      DCHECK_EQ(child_flags_, ChildProcessHost::CHILD_RENDERER);
+#endif  // defined(OS_MAC)
     int child_flags = child_flags_;
 
     // When running under gdb, forking /proc/self/exe ends up forking the gdb
diff --git a/content/browser/utility_process_sandbox_browsertest.cc b/content/browser/utility_process_sandbox_browsertest.cc
index f1b00959..35ca5a4c 100644
--- a/content/browser/utility_process_sandbox_browsertest.cc
+++ b/content/browser/utility_process_sandbox_browsertest.cc
@@ -111,6 +111,7 @@
 #endif
       case Sandbox::kPrintCompositor:
       case Sandbox::kService:
+      case Sandbox::kServiceWithJit:
       case Sandbox::kUtility: {
         constexpr int kExpectedFullSandboxFlags =
             SandboxLinux::kPIDNS | SandboxLinux::kNetNS |
diff --git a/content/browser/utility_sandbox_delegate.cc b/content/browser/utility_sandbox_delegate.cc
index 87f200b..eb2de49c 100644
--- a/content/browser/utility_sandbox_delegate.cc
+++ b/content/browser/utility_sandbox_delegate.cc
@@ -52,6 +52,7 @@
 #endif
       sandbox_type_ == sandbox::mojom::Sandbox::kUtility ||
       sandbox_type_ == sandbox::mojom::Sandbox::kService ||
+      sandbox_type_ == sandbox::mojom::Sandbox::kServiceWithJit ||
       sandbox_type_ == sandbox::mojom::Sandbox::kNetwork ||
       sandbox_type_ == sandbox::mojom::Sandbox::kCdm ||
 #if BUILDFLAG(ENABLE_OOP_PRINTING)
diff --git a/content/browser/utility_sandbox_delegate_win.cc b/content/browser/utility_sandbox_delegate_win.cc
index a4b34a2a..37867ef6 100644
--- a/content/browser/utility_sandbox_delegate_win.cc
+++ b/content/browser/utility_sandbox_delegate_win.cc
@@ -11,7 +11,6 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/sandboxed_process_launcher_delegate.h"
-#include "media/mojo/mojom/cdm_service.mojom.h"
 #include "printing/buildflags/buildflags.h"
 #include "sandbox/policy/features.h"
 #include "sandbox/policy/mojom/sandbox.mojom.h"
@@ -230,14 +229,17 @@
     policy->SetTokenLevel(sandbox::USER_UNPROTECTED, sandbox::USER_UNPROTECTED);
   }
 
-  if (sandbox_type_ == sandbox::mojom::Sandbox::kService) {
+  if (sandbox_type_ == sandbox::mojom::Sandbox::kService ||
+      sandbox_type_ == sandbox::mojom::Sandbox::kServiceWithJit) {
     auto result = sandbox::policy::SandboxWin::AddWin32kLockdownPolicy(policy);
     if (result != sandbox::SBOX_ALL_OK)
       return false;
+  }
 
+  if (sandbox_type_ == sandbox::mojom::Sandbox::kService) {
     auto delayed_flags = policy->GetDelayedProcessMitigations();
     delayed_flags |= sandbox::MITIGATION_DYNAMIC_CODE_DISABLE;
-    result = policy->SetDelayedProcessMitigations(delayed_flags);
+    auto result = policy->SetDelayedProcessMitigations(delayed_flags);
     if (result != sandbox::SBOX_ALL_OK)
       return false;
   }
@@ -262,6 +264,9 @@
 }
 
 bool UtilitySandboxedProcessLauncherDelegate::CetCompatible() {
+  // TODO(1268074) can remove once v8 is cet-compatible.
+  if (sandbox_type_ == sandbox::mojom::Sandbox::kServiceWithJit)
+    return false;
   auto utility_sub_type =
       cmd_line_.GetSwitchValueASCII(switches::kUtilitySubType);
   return GetContentClient()->browser()->IsUtilityCetCompatible(
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 7da4d45..9f26fb3c 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -6757,7 +6757,7 @@
     if (!render_frame_host->GetLastCommittedOrigin().opaque()) {
       bool is_different_origin_subframe =
           render_frame_host->GetLastCommittedOrigin() !=
-          url::Origin::Create(render_frame_host->GetMainFrame()
+          url::Origin::Create(render_frame_host->GetOutermostMainFrame()
                                   ->GetLastCommittedURL()
                                   .DeprecatedGetOriginAsURL());
       suppress_this_message |= is_different_origin_subframe;
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index b2de95ab..93e14ed 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -511,7 +511,7 @@
   return pending_default_factory;
 }
 
-// The implementation of the following algorithm:
+// [spec]
 // https://html.spec.whatwg.org/C/#check-a-global-object's-embedder-policy
 bool DedicatedWorkerHost::CheckCrossOriginEmbedderPolicy(
     network::CrossOriginEmbedderPolicy creator_cross_origin_embedder_policy,
@@ -522,7 +522,7 @@
   if (!creator_coep_reporter_)
     return false;
 
-  // > 4. If ownerPolicy's report-only value is "require-corp" or
+  // [spec]: 4. If ownerPolicy's report-only value is "require-corp" or
   // "credentialless" and policy's value is "unsafe-none", then queue a
   // cross-origin embedder policy inheritance violation with response, "worker
   // initialization", owner's policy's report only reporting endpoint,
@@ -536,7 +536,7 @@
         /*report_only=*/true);
   }
 
-  // > 5. If ownerPolicy's value is "unsafe-none" or policy's value is
+  // [spec]: 5. If ownerPolicy's value is "unsafe-none" or policy's value is
   // "require-corp" or "credentialless", then return true.
   if (!network::CompatibleWithCrossOriginIsolated(
           creator_cross_origin_embedder_policy) ||
@@ -545,14 +545,14 @@
     return true;
   }
 
-  // > 6. Queue a cross-origin embedder policy inheritance violation with
+  // [spec]: 6. Queue a cross-origin embedder policy inheritance violation with
   // response, "worker initialization", owner's policy's reporting endpoint,
   // "enforce", and owner.
   creator_coep_reporter_->QueueWorkerInitializationReport(
       final_response_url_.value(),
       /*report_only=*/false);
 
-  // > 7. Return false.
+  // [spec]: 7. Return false.
   return false;
 }
 
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index b5f1801..357f00e 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -530,19 +530,29 @@
     info.client->OnScriptLoadFailed(error_message);
 }
 
-// The implementation of the following algorithm:
+// [spec]:
 // https://html.spec.whatwg.org/C/#check-a-global-object's-embedder-policy
 bool SharedWorkerHost::CheckCrossOriginEmbedderPolicy(
     network::CrossOriginEmbedderPolicy creator_cross_origin_embedder_policy,
     network::CrossOriginEmbedderPolicy worker_cross_origin_embedder_policy) {
   DCHECK(base::FeatureList::IsEnabled(blink::features::kCOEPForSharedWorker));
+  // [spec]: 4. If ownerPolicy's report-only value is "require-corp" or
+  // "credentialless" and policy's value is "unsafe-none", then queue a
+  // cross-origin embedder policy inheritance violation with response, "worker
+  // initialization", owner's policy's report only reporting endpoint,
+  // "reporting", and owner.
+  // TODO(https://crbug.com/1060832): Add reporters.
+
+  // [spec]: 5. If ownerPolicy's value is "unsafe-none" or policy's value is
+  // "require-corp" or "credentialless", then return true.
   if (!network::CompatibleWithCrossOriginIsolated(
           creator_cross_origin_embedder_policy) ||
       network::CompatibleWithCrossOriginIsolated(
           worker_cross_origin_embedder_policy)) {
     return true;
   }
-  // TODO(https://crbug.com/1060832): Add reporters.
+
+  // [spec]: 7. Return false.
   return false;
 }
 
diff --git a/content/browser/xr/README.md b/content/browser/xr/README.md
new file mode 100644
index 0000000..0a7eb31
--- /dev/null
+++ b/content/browser/xr/README.md
@@ -0,0 +1,37 @@
+# XR Browser implementation
+_For a more high level overview of the entire WebXR stack, please refer to
+[components/webxr](https://source.chromium.org/chromium/chromium/src/+/main:components/webxr/README.md)._
+
+This folder contains trusted XR code. It is largely responsible for either
+communicating with other trusted code, or to broker connections and
+communication between either other trusted code or the renderer process and the
+various XR device runtimes (which are hosted in a separate process).
+
+The primary entrypoint is the [`VRServiceImpl`][vr service impl], which
+implements the [`VRService`][vr service] mojom interface. This service is
+responsible for brokering connections between the renderer process and the
+device process, both for the sake of starting up an XR Session, as well as
+simply querying support for a session. Each browsing context ends up creating
+its own [`VRService`][vr service]. A [`VRServiceImpl`][vr service impl] instance,
+when tracking a session, also leans on code from the ./metrics directory to log
+various metrics about that session, both at creation and termination.
+
+The [`XRRuntimeManager`][runtime manager] is a singleton component which
+maintains references to [`BrowserXrRuntime`][xr runtime] objects representing
+the various devices and/or sensor groups that could back an XR Session. The
+[`XRRuntimeManager`][runtime manager] is responsible for tracking the state of
+the hardware and aiding the multiple [`VRServices`][vr service impl] with
+communicating/tracking this state.
+
+When necessary, [`XRFrameSinkClient`][xr frame sink] instances aid communication
+with viz for runtimes which utilize that component to manage their own
+compositing.
+
+Some chrome-specific customizations based on this content implementation can be
+found in [chrome/browser/vr](https://chromium.googlesource.com/chromium/src/+/HEAD/chrome/browser/vr)
+
+[vr service impl]: https://chromium.googlesource.com/chromium/src/+/HEAD/content/browser/xr/service/vr_service_impl.h
+[vr service]: https://chromium.googlesource.com/chromium/src/+/HEAD/device/vr/public/mojom/vr_service.mojom
+[runtime manager]: https://chromium.googlesource.com/chromium/src/+/HEAD/content/browser/xr/service/xr_runtime_manager_impl.h
+[xr runtime]: https://chromium.googlesource.com/chromium/src/+/HEAD/content/browser/xr/service/browser_xr_runtime.h
+[xr frame sink]: https://chromium.googlesource.com/chromium/src/+/HEAD/content/browser/xr/service/xr_frame_sink_client_impl.h
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 4a0726f7..e3fd09e 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -571,6 +571,10 @@
     sources += [ "zygote_host/zygote_host_linux.h" ]
   }
 
+  if (is_chromeos) {
+    sources += [ "lock_screen_storage.h" ]
+  }
+
   if (!is_android) {
     sources += [
       # Some Web Authentication related targets only apply to desktop because
diff --git a/content/public/browser/ax_inspect_factory.cc b/content/public/browser/ax_inspect_factory.cc
index 31e109b..a46e1f6 100644
--- a/content/public/browser/ax_inspect_factory.cc
+++ b/content/public/browser/ax_inspect_factory.cc
@@ -5,7 +5,6 @@
 #include "content/public/browser/ax_inspect_factory.h"
 
 #include "base/notreached.h"
-#include "content/browser/accessibility/accessibility_event_recorder.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_blink.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "ui/base/buildflags.h"
@@ -29,7 +28,7 @@
 std::unique_ptr<ui::AXEventRecorder> AXInspectFactory::CreatePlatformRecorder(
     BrowserAccessibilityManager* manager,
     base::ProcessId pid,
-    const AXTreeSelector& selector) {
+    const ui::AXTreeSelector& selector) {
   return AXInspectFactory::CreateRecorder(ui::AXApiType::kBlink);
 }
 
@@ -55,7 +54,7 @@
     ui::AXApiType::Type type,
     BrowserAccessibilityManager* manager,
     base::ProcessId pid,
-    const AXTreeSelector& selector) {
+    const ui::AXTreeSelector& selector) {
   // Developer mode: crash immediately on any accessibility fatal error.
   // This only runs during integration tests, or if a developer is
   // using an inspection tool, e.g. chrome://accessibility.
diff --git a/content/public/browser/ax_inspect_factory_android.cc b/content/public/browser/ax_inspect_factory_android.cc
index 8a938c2..0fc516b 100644
--- a/content/public/browser/ax_inspect_factory_android.cc
+++ b/content/public/browser/ax_inspect_factory_android.cc
@@ -5,7 +5,6 @@
 #include "content/public/browser/ax_inspect_factory.h"
 
 #include "base/notreached.h"
-#include "content/browser/accessibility/accessibility_event_recorder.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_android.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_android_external.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_blink.h"
diff --git a/content/public/browser/ax_inspect_factory_fuchsia.cc b/content/public/browser/ax_inspect_factory_fuchsia.cc
index 234fa53..073ca57 100644
--- a/content/public/browser/ax_inspect_factory_fuchsia.cc
+++ b/content/public/browser/ax_inspect_factory_fuchsia.cc
@@ -48,7 +48,7 @@
 // static
 std::unique_ptr<ui::AXEventRecorder> AXInspectFactory::CreateRecorder(
     ui::AXApiType::Type type,
-    BrowserAccessibilityManager* manager,
+    BrowserAccessibilityManager*,
     base::ProcessId pid,
     const ui::AXTreeSelector& selector) {
   // Developer mode: crash immediately on any accessibility fatal error.
@@ -58,8 +58,7 @@
 
   switch (type) {
     case ui::AXApiType::kFuchsia:
-      return std::make_unique<AccessibilityEventRecorderFuchsia>(manager, pid,
-                                                                 selector);
+      return std::make_unique<AccessibilityEventRecorderFuchsia>(pid, selector);
     default:
       NOTREACHED() << "Unsupported API type " << static_cast<std::string>(type);
   }
diff --git a/content/public/browser/ax_inspect_factory_mac.mm b/content/public/browser/ax_inspect_factory_mac.mm
index 83d9653..9b2056c 100644
--- a/content/public/browser/ax_inspect_factory_mac.mm
+++ b/content/public/browser/ax_inspect_factory_mac.mm
@@ -48,7 +48,7 @@
 // static
 std::unique_ptr<ui::AXEventRecorder> AXInspectFactory::CreateRecorder(
     ui::AXApiType::Type type,
-    BrowserAccessibilityManager* manager,
+    BrowserAccessibilityManager*,
     base::ProcessId pid,
     const ui::AXTreeSelector& selector) {
   // Developer mode: crash immediately on any accessibility fatal error.
@@ -58,8 +58,7 @@
 
   switch (type) {
     case ui::AXApiType::kMac:
-      return std::make_unique<AccessibilityEventRecorderMac>(manager, pid,
-                                                             selector);
+      return std::make_unique<AccessibilityEventRecorderMac>(pid, selector);
     default:
       NOTREACHED() << "Unsupported API type " << type;
   }
diff --git a/content/public/browser/lock_screen_storage.h b/content/public/browser/lock_screen_storage.h
new file mode 100644
index 0000000..51dc74b
--- /dev/null
+++ b/content/public/browser/lock_screen_storage.h
@@ -0,0 +1,37 @@
+// 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 CONTENT_PUBLIC_BROWSER_LOCK_SCREEN_STORAGE_H_
+#define CONTENT_PUBLIC_BROWSER_LOCK_SCREEN_STORAGE_H_
+
+#include "content/common/content_export.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace content {
+
+class BrowserContext;
+
+// Global storage for lock screen data stored by websites. This isn't
+// BrowserContext keyed because there can only ever be one lock screen for the
+// entire browser (the primary user's BrowserContext).
+class CONTENT_EXPORT LockScreenStorage {
+ public:
+  static LockScreenStorage* GetInstance();
+
+  // LockScreenStorage must be initialized before any data is written to it or
+  // read from it by the Lock Screen API. The BrowserContext associated with the
+  // lock screen and the base path where data will be stored should be passed
+  // in. There can be up to one lock screen for the entire browser, so
+  // this can be called only once with the BrowserContext associated with the
+  // lock screen.
+  virtual void Init(BrowserContext* browser_context,
+                    const base::FilePath& base_path) = 0;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_LOCK_SCREEN_STORAGE_H_
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 5046fce..7ea6d17c 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -291,7 +291,7 @@
 // Enables new canvas 2d api features. Enabled either with either
 // enable-experimental-canvas-features or new-canvas-2d-api runtime flags
 const base::Feature kEnableNewCanvas2DAPI{"EnableNewCanvas2DAPI",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
+                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
 // If this feature is enabled and device permission is not granted by the user,
 // media-device enumeration will provide at most one device per type and the
@@ -799,6 +799,13 @@
 const base::Feature kWebOTPAssertionFeaturePolicy{
     "WebOTPAssertionFeaturePolicy", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable the web lockscreen API implementation
+// (https://github.com/WICG/lock-screen) in Chrome.
+#if defined(OS_CHROMEOS)
+const base::Feature kWebLockScreenApi{"WebLockScreenApi",
+                                      base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 // Experiment allowing control over what requests are intercepted by Service
 // Worker fetch events. By setting a Service-Worker-Subresource-Filter HTTP
 // header on the document to some string, only requests which contain a fragment
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index db74c78..f480dfe 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -194,6 +194,9 @@
 CONTENT_EXPORT extern const base::Feature
     kSkipEarlyCommitPendingForCrashedFrame;
 CONTENT_EXPORT extern const base::Feature kUserMediaCaptureOnFocus;
+#if defined(OS_CHROMEOS)
+CONTENT_EXPORT extern const base::Feature kWebLockScreenApi;
+#endif  // defined(OS_CHROMEOS)
 CONTENT_EXPORT extern const base::Feature kWebOTP;
 CONTENT_EXPORT extern const base::Feature kWebOTPAssertionFeaturePolicy;
 CONTENT_EXPORT extern const base::Feature kServiceWorkerSubresourceFilter;
diff --git a/content/public/common/content_switch_dependent_feature_overrides.cc b/content/public/common/content_switch_dependent_feature_overrides.cc
index 8e33e6b..c9122563 100644
--- a/content/public/common/content_switch_dependent_feature_overrides.cc
+++ b/content/public/common/content_switch_dependent_feature_overrides.cc
@@ -31,9 +31,6 @@
      std::cref(net::features::kCookieSameSiteConsidersRedirectChain),
      base::FeatureList::OVERRIDE_ENABLE_FEATURE},
     {switches::kEnableExperimentalWebPlatformFeatures,
-     std::cref(network::features::kCrossOriginEmbedderPolicyCredentialless),
-     base::FeatureList::OVERRIDE_ENABLE_FEATURE},
-    {switches::kEnableExperimentalWebPlatformFeatures,
      std::cref(features::kDocumentPolicyNegotiation),
      base::FeatureList::OVERRIDE_ENABLE_FEATURE},
 #if BUILDFLAG(ENABLE_REPORTING)
diff --git a/content/services/auction_worklet/public/mojom/auction_worklet_service.mojom b/content/services/auction_worklet/public/mojom/auction_worklet_service.mojom
index 74537c23..9f94e380 100644
--- a/content/services/auction_worklet/public/mojom/auction_worklet_service.mojom
+++ b/content/services/auction_worklet/public/mojom/auction_worklet_service.mojom
@@ -21,7 +21,7 @@
 // Used by the browser to load and run FLEDGE worklets in a sandboxed utility
 // process.
 // See https://github.com/WICG/turtledove/blob/main/FLEDGE.md
-[ServiceSandbox=sandbox.mojom.Sandbox.kService]
+[ServiceSandbox=sandbox.mojom.Sandbox.kServiceWithJit]
 interface AuctionWorkletService {
   // Attempts to load Javascript at the specified URL and create a BidderWorklet
   // from the response body. There is no return value - any error loading the
diff --git a/content/services/isolated_xr_device/README.md b/content/services/isolated_xr_device/README.md
new file mode 100644
index 0000000..f7532aa
--- /dev/null
+++ b/content/services/isolated_xr_device/README.md
@@ -0,0 +1,19 @@
+# Isolated XR Device Service
+_For a more thorough/high level overview of the entire WebXR stack, please refer to
+[components/webxr](https://source.chromium.org/chromium/chromium/src/+/main:components/webxr/README.md)_
+
+Chromium's WebXR implementation makes use of the multiprocess architecture for
+added security. Thus all code which directly interfaces with and talks to the
+XR hardware ("runtimes") ends up loaded/hosted in a separate XR Utility process,
+except on Android where this is not possible. The `XrDeviceService` serves as
+the entry point for the VRServiceImpl in `content/browser/xr` to talk to this
+process, while the `IsolatedXRRuntimeProvider` is the main in-process entry
+point. The runtime provider continually polls for supported runtimes and when a
+change is detected creates and returns the appropriate runtime over mojom. The
+mojom interfaces used by this process are defined in `device/vr`.
+
+## Testing
+
+This folder also defines test hooks. Tests can set this to modify the behavior
+of the `IsolatedXRRuntimeProvider` and force it to create a fake XR Device which
+the tests can fully control.
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 4dda0b6..8e077aa 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1578,6 +1578,11 @@
         [ "../browser/media/media_keys_listener_manager_impl_browsertest.cc" ]
   }
 
+  if (is_chromeos) {
+    sources +=
+        [ "../browser/lock_screen/lock_screen_service_impl_browsertest.cc" ]
+  }
+
   if (is_win) {
     sources += [
       "../browser/accessibility/accessibility_browsertest.cc",
diff --git a/content/test/data/lock_screen/simple.html b/content/test/data/lock_screen/simple.html
new file mode 100644
index 0000000..70db0f2
--- /dev/null
+++ b/content/test/data/lock_screen/simple.html
@@ -0,0 +1,4 @@
+<html>
+<head><title>OK</title></head>
+<body></body>
+</html>
diff --git a/content/test/data/media/webrtc_test_utilities.js b/content/test/data/media/webrtc_test_utilities.js
index 6e8df6f..10a79d0 100644
--- a/content/test/data/media/webrtc_test_utilities.js
+++ b/content/test/data/media/webrtc_test_utilities.js
@@ -111,7 +111,7 @@
             ' to appear');
         return;
       }
-      var context = canvas.getContext('2d');
+      var context = canvas.getContext('2d', {willReadFrequently: true});
       context.drawImage(videoElement, 0, 0);
       var pixels = context.getImageData(0, 0, width, height / 3).data;
 
diff --git a/content/test/data/prerender/viewport.html b/content/test/data/prerender/viewport.html
new file mode 100644
index 0000000..b6252547
--- /dev/null
+++ b/content/test/data/prerender/viewport.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width initial-scale=1.0" />
+</head>
+<body>
+<script>
+
+const viewportTag = document.getElementsByTagName('meta')[0];
+const defaultViewportContent = viewportTag.getAttribute('content');
+
+function setViewportFit(value) {
+  viewportTag.setAttribute('content', defaultViewportContent + ', viewport-fit=' + value);
+}
+
+</script>
+</body>
+</html>
diff --git a/device/bluetooth/bluetooth_strings.grd b/device/bluetooth/bluetooth_strings.grd
index 0e6be00..8b19b37e 100644
--- a/device/bluetooth/bluetooth_strings.grd
+++ b/device/bluetooth/bluetooth_strings.grd
@@ -40,7 +40,7 @@
       <output filename="bluetooth_strings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="bluetooth_strings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="bluetooth_strings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="bluetooth_strings_am.pak" type="data_package" lang="am" />
diff --git a/device/fido/fido_strings.grd b/device/fido/fido_strings.grd
index 13a9507b..ea20486 100644
--- a/device/fido/fido_strings.grd
+++ b/device/fido/fido_strings.grd
@@ -40,7 +40,7 @@
       <output filename="fido_strings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="fido_strings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="fido_strings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="fido_strings_am.pak" type="data_package" lang="am" />
diff --git a/device/vr/README.md b/device/vr/README.md
index e0c27eb..15c3426 100644
--- a/device/vr/README.md
+++ b/device/vr/README.md
@@ -1,5 +1,8 @@
 # VR
 
+_For a more thorough/high level overview of the entire WebXR stack, please refer to
+[components/webxr](https://source.chromium.org/chromium/chromium/src/+/main:components/webxr/README.md)_
+
 `device/vr` abstracts [WebXR](https://immersive-web.github.io/webxr/) features
 across multiple platforms.
 
diff --git a/docs/accessibility/overview.md b/docs/accessibility/overview.md
index 2619800..be0f55b9 100644
--- a/docs/accessibility/overview.md
+++ b/docs/accessibility/overview.md
@@ -106,15 +106,18 @@
 and if you call IAccessible::get_accName, it returns "How old are you?".
 Other methods let you walk the tree.
 
+The Linux accessibility API, (ATK)[https://gnome.pages.gitlab.gnome.org/atk/],
+is similar to (IAccessible2)[https://wiki.linuxfoundation.org/accessibility/iaccessible2/start],
+aka IA2. Historical note: IA2 was developed to extend MSAA/IAccessible to add
+richer document support, in a way that was harmonious with ATK, in order to
+simplify implementing them both within the same product. Both APIs are
+maintained by the Linux Foundation.
+
 On macOS, the root node implements the NSAccessibility protocol and
 if you call [NSAccessibility accessibilityRole], it returns @"AXWebArea",
 and if you call [NSAccessibility accessibilityLabel], it returns
 "How old are you?".
 
-The Linux accessibility API, ATK, is more similar to the Windows APIs;
-they were developed together. (Chrome's support for desktop Linux
-accessibility is unfinished.)
-
 The Android accessibility API is of course based on Java. The main
 data structure is AccessibilityNodeInfo. It doesn't have a role, but
 if you call AccessibilityNodeInfo.getClassName() on the root node
@@ -209,6 +212,9 @@
 Automation API).
 * Installing the [https://github.com/google/automation-inspector](
 Automation Inspector Chrome extension).
+* Building and using [ax_dump_tree or ax_dump_events](tools/accessibility/inspect/README.md).
+These can be used to view accessibility trees and events from any application on
+Windows, Mac or Linux.
 * Or by using native tools:
 
   - Android: UIAutomatorViewer
diff --git a/docs/security/rust-toolchain.md b/docs/security/rust-toolchain.md
index 91ea892..65b09925 100644
--- a/docs/security/rust-toolchain.md
+++ b/docs/security/rust-toolchain.md
@@ -95,7 +95,12 @@
 
 Rust supports unit tests within the primary source code files (e.g. see
 [an example here](https://doc.rust-lang.org/rust-by-example/testing/unit_testing.html)).
-With most of the templates above you'll get a bonus `gn` target called
+This section describes how to build and run such unit tests.
+
+## Automatically generated targets
+
+GN templates that work with Rust sources will automatically generate a bonus
+`gn` target called
 `<your target name>_unittests` (for pure-Rust targets like `cargo_crate`,
 `executable`, or `rust_source_set`) or `<your target name>_rs_unittests` (for
 mixed C++/Rust targets like (`mixed_component`, `mixed_executable`, or
@@ -105,13 +110,44 @@
 - An `out/Default/bin/run_<bonus target name>` script that enables running
   the tests on Chromium bots.
 
+## Explicitly defined groups of tests
+
+To group multiple Rust unit test executables into a single test step,
+please use the `rust_unit_tests_group("my_test_group")` template:
+
+```
+rust_unit_tests_group("my_group_of_rust_unit_tests") {
+  deps = [
+    "my_rust_source_set1",
+    "my_rust_source_set2",
+    "my_rust_executable",
+    # ...
+  ]
+}
+```
+
+The example above will build all the `deps`.  This will also generate a wrapper
+script that wraps all the Rust unit test executables from `deps` and their
+transitive dependencies.  In the example above, the script will be generated at
+`out/Default/bin/run_my_group_of_rust_unit_tests`.
+
+The generated script can be used for integration with Chromium bots, but can
+also be used as a convenience to manually/locally run all tests from the group.
+Run the script with the `--help` argument to see more details (e.g. how to
+filter which tests to run).
+
+## Configuring running Rust unit tests on bots
+
 To manually configure running Rust unit tests on bots, please follow the pattern
 from https://crrev.com/c/3322199:
 - Define a new isolate in `//testing/buildbot/gn_isolate_map.pyl`:
-    - Set `label` to the bonus target's name
+    - Set `label` to the fully qualified name of either
+      the implicit bonus target (e.g. `..._rs_unittests`)
+      or the explicit `rust_unit_tests_group` target.
     - Set `type` to `generated_script`
-    - There are no restrictions on the name of the new isolate,
-      but typically it will have the same name as the bonus target.
+    - There are no requirements on the name of the new isolate,
+      but typically it will have the same name as the target mentioned
+      in the `label`.
 - Define a new test step, or extend an existing test step in
   `//testing/buildbot/test_suites.pyl` (adding an entry referring to
   the new isolate above).  Note that the tests grouped under the test
@@ -122,9 +158,6 @@
 - Run `//testing/buildbot/generate_buildbot_json.py`.
 
 Future work:
-- At present, there is no way to group multiple Rust test executables
-  (e.g. all Rust unittests from a single top-level directory
-  into a single test suite.  This is something we're working on.
 - At present, there is no automatic integration of such unit tests into our
   existing test infrastructure, but this is something we're working on.
 - At present, the bot integration only supports reporting whether the tests
diff --git a/docs/vscode.md b/docs/vscode.md
index d780f7b..2aa2e05 100644
--- a/docs/vscode.md
+++ b/docs/vscode.md
@@ -338,6 +338,7 @@
 VSCode should work remotely after following this step.
 
 ### Snippets
+
 There are some useful snippets provided in
 [//tools/vscode/cpp.json5](/tools/vscode/cpp.json5).
 
@@ -347,11 +348,9 @@
 $ cp tools/vscode/cpp.json5 ~/.config/Code/User/snippets/cpp.json
 ```
 
-Or install them as project snippets after installing the [Project
-Snippets](https://marketplace.visualstudio.com/items?itemName=rebornix.project-snippets)
-extension:
+Or install them as project snippets:
 ```
-$ cp tools/vscode/cpp.json5 .vscode/snippets/cpp.json
+$ cp tools/vscode/cpp.json5 .vscode/cpp.code-snippets
 ```
 
 ### Tips
diff --git a/extensions/browser/api/audio/audio_device_id_calculator.cc b/extensions/browser/api/audio/audio_device_id_calculator.cc
index 7a238e2..052f42e 100644
--- a/extensions/browser/api/audio/audio_device_id_calculator.cc
+++ b/extensions/browser/api/audio/audio_device_id_calculator.cc
@@ -35,7 +35,7 @@
 
   PrefService* pref_service =
       ExtensionsBrowserClient::Get()->GetPrefServiceForContext(context_);
-  const base::ListValue* audio_service_stable_ids =
+  const base::Value* audio_service_stable_ids =
       pref_service->GetList(kAudioApiStableDeviceIds);
   base::Value::ConstListView ids_list = audio_service_stable_ids->GetList();
   for (size_t i = 0; i < ids_list.size(); ++i) {
diff --git a/extensions/browser/api/lock_screen_data/lock_screen_item_storage.cc b/extensions/browser/api/lock_screen_data/lock_screen_item_storage.cc
index 6625030..16d6707 100644
--- a/extensions/browser/api/lock_screen_data/lock_screen_item_storage.cc
+++ b/extensions/browser/api/lock_screen_data/lock_screen_item_storage.cc
@@ -597,7 +597,7 @@
     bool include_empty) {
   std::set<std::string> result;
 
-  const base::DictionaryValue* items =
+  const base::Value* items =
       local_state_->GetDictionary(kLockScreenDataPrefKey);
   if (!items)
     return result;
@@ -622,7 +622,7 @@
 std::set<ExtensionId> LockScreenItemStorage::GetExtensionsToMigrate() {
   std::set<ExtensionId> result;
 
-  const base::DictionaryValue* items =
+  const base::Value* items =
       local_state_->GetDictionary(kLockScreenDataPrefKey);
 
   if (!items)
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index ed5211c..48e99a5 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -427,8 +427,8 @@
 }
 
 void ExtensionPrefs::MakePathsRelative() {
-  const base::DictionaryValue* dict =
-      prefs_->GetDictionary(pref_names::kExtensions);
+  const base::DictionaryValue* dict = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(pref_names::kExtensions));
   if (!dict || dict->DictEmpty())
     return;
 
@@ -474,7 +474,7 @@
 
 const base::DictionaryValue* ExtensionPrefs::GetExtensionPref(
     const std::string& extension_id) const {
-  const base::DictionaryValue* extensions =
+  const base::Value* extensions =
       prefs_->GetDictionary(pref_names::kExtensions);
   if (!extensions)
     return nullptr;
@@ -1140,7 +1140,8 @@
 }
 
 base::Time ExtensionPrefs::BlocklistLastPingDay() const {
-  return ReadTime(prefs_->GetDictionary(kExtensionsBlocklistUpdate),
+  return ReadTime(&base::Value::AsDictionaryValue(
+                      *prefs_->GetDictionary(kExtensionsBlocklistUpdate)),
                   kLastPingDay);
 }
 
@@ -1513,7 +1514,7 @@
 std::unique_ptr<ExtensionInfo> ExtensionPrefs::GetInstalledExtensionInfo(
     const std::string& extension_id,
     bool include_component_extensions) const {
-  const base::DictionaryValue* extensions =
+  const base::Value* extensions =
       prefs_->GetDictionary(pref_names::kExtensions);
   if (!extensions)
     return nullptr;
@@ -1536,15 +1537,14 @@
     bool include_component_extensions) const {
   std::unique_ptr<ExtensionsInfo> extensions_info(new ExtensionsInfo);
 
-  const base::DictionaryValue* extensions =
+  const base::Value* extensions =
       prefs_->GetDictionary(pref_names::kExtensions);
-  for (base::DictionaryValue::Iterator extension_id(*extensions);
-       !extension_id.IsAtEnd(); extension_id.Advance()) {
-    if (!crx_file::id_util::IdIsValid(extension_id.key()))
+  for (const auto extension_id : extensions->DictItems()) {
+    if (!crx_file::id_util::IdIsValid(extension_id.first))
       continue;
 
     std::unique_ptr<ExtensionInfo> info = GetInstalledExtensionInfo(
-        extension_id.key(), include_component_extensions);
+        extension_id.first, include_component_extensions);
     if (info)
       extensions_info->push_back(std::move(info));
   }
@@ -1665,15 +1665,14 @@
 ExtensionPrefs::GetAllDelayedInstallInfo() const {
   std::unique_ptr<ExtensionsInfo> extensions_info(new ExtensionsInfo);
 
-  const base::DictionaryValue* extensions =
+  const base::Value* extensions =
       prefs_->GetDictionary(pref_names::kExtensions);
-  for (base::DictionaryValue::Iterator extension_id(*extensions);
-       !extension_id.IsAtEnd(); extension_id.Advance()) {
-    if (!crx_file::id_util::IdIsValid(extension_id.key()))
+  for (const auto extension_id : extensions->DictItems()) {
+    if (!crx_file::id_util::IdIsValid(extension_id.first))
       continue;
 
     std::unique_ptr<ExtensionInfo> info =
-        GetDelayedInstallInfo(extension_id.key());
+        GetDelayedInstallInfo(extension_id.first);
     if (info)
       extensions_info->push_back(std::move(info));
   }
@@ -1783,8 +1782,7 @@
 }
 
 void ExtensionPrefs::ClearLastLaunchTimes() {
-  const base::DictionaryValue* dict =
-      prefs_->GetDictionary(pref_names::kExtensions);
+  const base::Value* dict = prefs_->GetDictionary(pref_names::kExtensions);
   if (!dict || dict->DictEmpty())
     return;
 
@@ -1895,7 +1893,7 @@
     const PrefMap& pref) const {
   DCHECK_EQ(PrefScope::kProfile, pref.scope);
   DCHECK_EQ(PrefType::kDictionary, pref.type);
-  return prefs_->GetDictionary(pref.name);
+  return &base::Value::AsDictionaryValue(*prefs_->GetDictionary(pref.name));
 }
 
 void ExtensionPrefs::IncrementPref(const PrefMap& pref) {
@@ -2003,7 +2001,8 @@
 }
 
 const base::DictionaryValue* ExtensionPrefs::GetInstallSignature() const {
-  return prefs_->GetDictionary(kInstallSignature);
+  return &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(kInstallSignature));
 }
 
 void ExtensionPrefs::SetInstallSignature(
@@ -2438,7 +2437,7 @@
     return;
   std::string key = extension_id + "." + scope_string;
 
-  const base::DictionaryValue* source_dict =
+  const base::Value* source_dict =
       pref_service()->GetDictionary(pref_names::kExtensions);
 
   const base::Value* preferences = source_dict->FindDictPath(key);
@@ -2533,7 +2532,7 @@
 }
 
 void ExtensionPrefs::MigrateYoutubeOffBookmarkApps() {
-  const base::DictionaryValue* extensions_dictionary =
+  const base::Value* extensions_dictionary =
       prefs_->GetDictionary(pref_names::kExtensions);
   DCHECK(extensions_dictionary->is_dict());
   const base::Value* youtube_dictionary =
diff --git a/extensions/browser/json_file_sanitizer.cc b/extensions/browser/json_file_sanitizer.cc
index 84d0a29b..8d0ae03 100644
--- a/extensions/browser/json_file_sanitizer.cc
+++ b/extensions/browser/json_file_sanitizer.cc
@@ -90,6 +90,7 @@
     return;
   }
   json_parser_->Parse(std::get<0>(read_and_delete_result),
+                      base::JSON_PARSE_CHROMIUM_EXTENSIONS,
                       base::BindOnce(&JsonFileSanitizer::JsonParsingDone,
                                      weak_factory_.GetWeakPtr(), file_path));
 }
diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc
index e80c62c7..a295e7d 100644
--- a/extensions/browser/sandboxed_unpacker.cc
+++ b/extensions/browser/sandboxed_unpacker.cc
@@ -1061,7 +1061,8 @@
     return;
   }
 
-  GetJsonParserPtr()->Parse(contents, std::move(callback));
+  GetJsonParserPtr()->Parse(contents, base::JSON_PARSE_CHROMIUM_EXTENSIONS,
+                            std::move(callback));
 }
 
 }  // namespace extensions
diff --git a/extensions/browser/zipfile_installer.cc b/extensions/browser/zipfile_installer.cc
index bce2013..93a8af596 100644
--- a/extensions/browser/zipfile_installer.cc
+++ b/extensions/browser/zipfile_installer.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/containers/contains.h"
 #include "base/files/file_util.h"
+#include "base/json/json_reader.h"
 #include "base/path_service.h"
 #include "base/strings/string_util.h"
 #include "base/task/post_task.h"
@@ -18,6 +19,8 @@
 #include "extensions/common/constants.h"
 #include "extensions/common/manifest.h"
 #include "extensions/strings/grit/extensions_strings.h"
+#include "services/data_decoder/public/cpp/data_decoder.h"
+#include "services/data_decoder/public/mojom/json_parser.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace extensions {
@@ -140,22 +143,43 @@
     return;
   }
 
-  data_decoder::DataDecoder::ParseJsonIsolated(
-      *manifest_content,
-      base::BindOnce(&ZipFileInstaller::ManifestParsed, this, unzip_dir));
+  // Create a DataDecoder to specify custom parse options to the JSON
+  // parser. The ownership of the |data_decoder| and |json_parser|
+  // transfer to the response callback and are deleted after it runs.
+  auto data_decoder = std::make_unique<data_decoder::DataDecoder>();
+  mojo::Remote<data_decoder::mojom::JsonParser> json_parser;
+  data_decoder->GetService()->BindJsonParser(
+      json_parser.BindNewPipeAndPassReceiver());
+  json_parser.set_disconnect_handler(
+      base::BindOnce(&ZipFileInstaller::ManifestParsed, this, unzip_dir,
+                     absl::nullopt, "Data Decoder terminated unexpectedly"));
+  auto* json_parser_ptr = json_parser.get();
+  json_parser_ptr->Parse(
+      *manifest_content, base::JSON_PARSE_CHROMIUM_EXTENSIONS,
+      base::BindOnce(
+          [](std::unique_ptr<data_decoder::DataDecoder>,
+             mojo::Remote<data_decoder::mojom::JsonParser>,
+             scoped_refptr<ZipFileInstaller> installer,
+             const base::FilePath& unzip_dir, absl::optional<base::Value> value,
+             const absl::optional<std::string>& error) {
+            installer->ManifestParsed(unzip_dir, std::move(value), error);
+          },
+          std::move(data_decoder), std::move(json_parser),
+          base::WrapRefCounted(this), unzip_dir));
 }
 
 void ZipFileInstaller::ManifestParsed(
     const base::FilePath& unzip_dir,
-    data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
+    absl::optional<base::Value> result,
+    const absl::optional<std::string>& error) {
+  if (!result) {
     ReportFailure(std::string(kExtensionHandlerFileUnzipError));
     return;
   }
 
   std::unique_ptr<base::DictionaryValue> manifest_dictionary =
       base::DictionaryValue::From(
-          base::Value::ToUniquePtrValue(std::move(*result.value)));
+          base::Value::ToUniquePtrValue(std::move(*result)));
   if (!manifest_dictionary) {
     ReportFailure(std::string(kExtensionHandlerFileUnzipError));
     return;
diff --git a/extensions/browser/zipfile_installer.h b/extensions/browser/zipfile_installer.h
index 2c54a13..3f3b4ba 100644
--- a/extensions/browser/zipfile_installer.h
+++ b/extensions/browser/zipfile_installer.h
@@ -14,7 +14,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
-#include "services/data_decoder/public/cpp/data_decoder.h"
+#include "base/values.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace extensions {
@@ -67,7 +67,8 @@
   void ManifestRead(const base::FilePath& unzip_dir,
                     absl::optional<std::string> manifest_content);
   void ManifestParsed(const base::FilePath& unzip_dir,
-                      data_decoder::DataDecoder::ValueOrError result);
+                      absl::optional<base::Value> result,
+                      const absl::optional<std::string>& error);
   void UnzipDone(const base::FilePath& unzip_dir, bool success);
 
   // On failure, report the |error| reason.
diff --git a/extensions/strings/extensions_strings.grd b/extensions/strings/extensions_strings.grd
index c4c1139..2c17fec1 100644
--- a/extensions/strings/extensions_strings.grd
+++ b/extensions/strings/extensions_strings.grd
@@ -42,7 +42,7 @@
     <output filename="extensions_strings_hr.pak" type="data_package" lang="hr" />
     <output filename="extensions_strings_hu.pak" type="data_package" lang="hu" />
     <output filename="extensions_strings_id.pak" type="data_package" lang="id" />
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="extensions_strings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="extensions_strings_it.pak" type="data_package" lang="it" />
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index 87ed0c3..614ca00 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -73,7 +73,7 @@
 void PostTaskOnUIThread(base::OnceClosure closure) {
   base::PostTask(FROM_HERE, {web::WebThread::UI}, std::move(closure));
 }
-NSString* const kStartupAttemptReset = @"StartupAttempReset";
+NSString* const kStartupAttemptReset = @"StartupAttemptReset";
 
 // Time interval used for startRecordingMemoryFootprintWithInterval:
 const NSTimeInterval kMemoryFootprintRecordingTimeInterval = 5;
@@ -111,7 +111,7 @@
 @property(nonatomic, strong) AppStateObserverList* observers;
 
 // This method is the first to be called when user launches the application.
-// This performs the minimal amount of browser initalization that is needed by
+// This performs the minimal amount of browser initialization that is needed by
 // safe mode.
 // Depending on the background tasks history, the state of the application is
 // INITIALIZATION_STAGE_BACKGROUND so this
@@ -253,7 +253,7 @@
   if (self.initStage < InitStageBrowserObjectsForUI) {
     // The clean-up done in |-applicationDidEnterBackground:| is only valid for
     // the case when the application is started in foreground, so there is
-    // nothing to clean up as the application was not initialized for foregound.
+    // nothing to clean up as the application was not initialized for foreground.
     //
     // From the stack trace of the crash bug http://crbug.com/437307 , it
     // seems that |-applicationDidEnterBackground:| may be called when the app
@@ -424,7 +424,7 @@
   // Usually Chrome uses -[SceneState sceneSessionID] as identifier to properly
   // support devices that do not support multi-window (and which use a constant
   // identifier). For devices that do not support multi-window the session is
-  // saved at a constant path, so it is harmnless to delete files at a path
+  // saved at a constant path, so it is harmless to delete files at a path
   // derived from -persistentIdentifier (since there won't be files deleted).
   // For devices that do support multi-window, there is data to delete once the
   // session is garbage collected.
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index b521b120..f1299bb 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -137,9 +137,9 @@
 
 namespace {
 
-// Constants for deferring reseting the startup attempt count (to give the app
+// Constants for deferring resetting the startup attempt count (to give the app
 // a little while to make sure it says alive).
-NSString* const kStartupAttemptReset = @"StartupAttempReset";
+NSString* const kStartupAttemptReset = @"StartupAttemptReset";
 
 // Constants for deferring memory debugging tools startup.
 NSString* const kMemoryDebuggingToolsStartup = @"MemoryDebuggingToolsStartup";
@@ -295,7 +295,7 @@
 
   WindowConfigurationRecorder* _windowConfigurationRecorder;
 
-  // Hander for the startup tasks, deferred or not.
+  // Handler for the startup tasks, deferred or not.
   StartupTasks* _startupTasks;
 
   // List of closure to run as part of shutdown. The closure will be called
@@ -687,7 +687,7 @@
   [appState addAgent:[[AppMetricsAppStateAgent alloc] init]];
   [appState addAgent:[[SafeModeAppAgent alloc] init]];
 
-  // Create the window accessibility agent only when multuple windows are
+  // Create the window accessibility agent only when multiple windows are
   // possible.
   if (base::ios::IsMultipleScenesSupported()) {
     [appState addAgent:[[WindowAccessibityChangeNotifierAppAgent alloc] init]];
@@ -777,7 +777,7 @@
 
   // Under the UIScene API, the scene delegate does not receive
   // sceneDidDisconnect: notifications on app termination. We mark remaining
-  // connected scene states as diconnected in order to allow services to
+  // connected scene states as disconnected in order to allow services to
   // properly unregister their observers and tear down remaining UI.
   for (SceneState* sceneState in self.appState.connectedScenes) {
     sceneState.activationLevel = SceneActivationLevelUnattached;
@@ -805,7 +805,7 @@
 
 - (void)registerForOrientationChangeNotifications {
   // Register device orientation. UI orientation will be registered by
-  // each window BVC. These two events may be triggered independantely.
+  // each window BVC. These two events may be triggered independently.
   [[NSNotificationCenter defaultCenter]
       addObserver:self
          selector:@selector(orientationDidChange:)
diff --git a/ios/chrome/app/strings/ios_chromium_strings.grd b/ios/chrome/app/strings/ios_chromium_strings.grd
index 4ec5fe1..1db2de84 100644
--- a/ios/chrome/app/strings/ios_chromium_strings.grd
+++ b/ios/chrome/app/strings/ios_chromium_strings.grd
@@ -364,6 +364,9 @@
       <message name="IDS_IOS_SAVE_IMAGE_PRIVACY_ALERT_MESSAGE_GO_TO_SETTINGS" desc="Body of the alert shown when the user tries to save an image but we lack permission to add photos. This body tells the user he'll be redirected to the Settings app. [Length: 100em] [iOS only].">
         To save images, tap on Settings to let Chromium add to your photos
       </message>
+      <message name="IDS_IOS_SAVE_PASSWORD_FOOTER_NOT_SYNCING" desc="Helper text above the save credentials modal's primary action button. [Length: unlimited] [iOS only]">
+        Passwords are saved to Password Manager on this device only.
+      </message>
       <message name="IDS_IOS_SCANNER_CAMERA_IN_USE_ALERT_DETAIL" desc="Body of a modal dialog shown when the user tries to open the QR or credit card scanner but the camera is in use by another application. [Length: 140em] [iOS only]">
         Chromium can't use your camera because it's in use by another application
       </message>
@@ -382,8 +385,11 @@
       <message name="IDS_IOS_SCANNER_MULTIPLE_FOREGROUND_APPS_ALERT_DETAIL" desc="Body of a modal dialog shown when the user tries to open the QR or credit card scanner in a Split View mode, when the camera is unavailable. [Length: 140em] [iOS only]">
         Chromium can't use your camera in Split View mode
       </message>
-      <message name="IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SECOND_SUBTITLE" desc="The additional subtitle of the view that teaches users they can autofill passwords with Google password manager [iOS only]">
-        To autofill your passwords with Password Manager, turn on Chromium in your iOS AutoFill settings:
+      <message name="IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPAD" desc="The subtitle of the view that teaches users to turn on iOS auto-fill from Chrome password manager for iPad [iPadOS only]">
+        You can use the passwords you saved to Password Manager in other apps on your iPad.
+      </message>
+      <message name="IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPHONE" desc="The subtitle of the view that teaches users to turn on iOS auto-fill from Chrome password manager for iPhone [iOS only]">
+        You can use the passwords you saved to Password Manager in other apps on your iPhone.
       </message>
       <message name="IDS_IOS_SHORT_PRODUCT_NAME" desc="The Chrome application short name.">
         Chromium
@@ -457,6 +463,9 @@
       <message name="IDS_IOS_SETTINGS_SIGNIN_DISABLED_POPOVER_TEXT" desc="This text is shown when sign-in is disabled by policy and the user taps on the info pop-over of the 'Sign in to Chromium' button in the settings. [iOS only]">
         Your organization requires you to stay signed out of Chromium.
       </message>
+      <message name="IDS_IOS_SUGGESTED_STRONG_PASSWORD_HINT_DISPLAYING_EMAIL" desc="Text shown alongside a suggested password to tell user that generated passwords are recorded by chrome. [Length: unlimited] [iOS only]">
+            You won't need to remember this password. It will be saved to Password Manager for <ph name="EMAIL">$1<ex>johndoe@gmail.com</ex></ph>
+      </message>
       <message name="IDS_IOS_PASSWORD_CHECK_ERROR" desc="Description of the password check cell, explaining that Password Check failed. [iOS only]" meaning="Password Check failed due to some error.">
         Chromium can't check your passwords
       </message>
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SAVE_PASSWORD_FOOTER_NOT_SYNCING.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SAVE_PASSWORD_FOOTER_NOT_SYNCING.png.sha1
new file mode 100644
index 0000000..d4661c20
--- /dev/null
+++ b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SAVE_PASSWORD_FOOTER_NOT_SYNCING.png.sha1
@@ -0,0 +1 @@
+bb352378379999450f8ca6c1c1d82ddafc8e66b9
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SECOND_SUBTITLE.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SECOND_SUBTITLE.png.sha1
deleted file mode 100644
index aa688f3..0000000
--- a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SECOND_SUBTITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-33d3f4ce672ec4613a7ddbf6c62dd1c2bbebb6b2
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPAD.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPAD.png.sha1
new file mode 100644
index 0000000..77fcb9d
--- /dev/null
+++ b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPAD.png.sha1
@@ -0,0 +1 @@
+258a1dc63291c15686502f6f5f8bd041ce844a28
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPHONE.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPHONE.png.sha1
new file mode 100644
index 0000000..288a6e1
--- /dev/null
+++ b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPHONE.png.sha1
@@ -0,0 +1 @@
+1adfe98deb2e007e29b8b1f71fd8c99e90ef1a69
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SUGGESTED_STRONG_PASSWORD_HINT_DISPLAYING_EMAIL.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SUGGESTED_STRONG_PASSWORD_HINT_DISPLAYING_EMAIL.png.sha1
new file mode 100644
index 0000000..931a8be6
--- /dev/null
+++ b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SUGGESTED_STRONG_PASSWORD_HINT_DISPLAYING_EMAIL.png.sha1
@@ -0,0 +1 @@
+0e71d6d17dc728891b54f5b8918239d2839b7d4e
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings.grd b/ios/chrome/app/strings/ios_google_chrome_strings.grd
index e7581b5..7dee437f 100644
--- a/ios/chrome/app/strings/ios_google_chrome_strings.grd
+++ b/ios/chrome/app/strings/ios_google_chrome_strings.grd
@@ -364,6 +364,9 @@
       <message name="IDS_IOS_SAVE_IMAGE_PRIVACY_ALERT_MESSAGE_GO_TO_SETTINGS" desc="Body of the alert shown when the user tries to save an image but we lack permission to add to photos. This body tells the user he'll be redirected to the Settings app. [Length: 100em] [iOS only].">
         To save images, tap on Settings to let Chrome add to your photos
       </message>
+      <message name="IDS_IOS_SAVE_PASSWORD_FOOTER_NOT_SYNCING" desc="Helper text above the save credentials modal's primary action button. [Length: unlimited] [iOS only]">
+        Passwords are saved to Google Password Manager on this device only.
+      </message>
       <message name="IDS_IOS_SCANNER_CAMERA_IN_USE_ALERT_DETAIL" desc="Body of a modal dialog shown when the user tries to open the QR scanner but the camera is in use by another application. [Length: 140em] [iOS only]">
         Google Chrome can't use your camera because it's in use by another application
       </message>
@@ -382,8 +385,11 @@
       <message name="IDS_IOS_SCANNER_MULTIPLE_FOREGROUND_APPS_ALERT_DETAIL" desc="Body of a modal dialog shown when the user tries to open the QR scanner in a Split View mode, when the camera is unavailable. [Length: 140em] [iOS only]">
         Google Chrome can't use your camera in Split View mode
       </message>
-      <message name="IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SECOND_SUBTITLE" desc="The additional subtitle of the view that teaches users they can autofill passwords with Google password manager [iOS only]">
-        To autofill your passwords with Google Password Manager, turn on Chrome in your iOS AutoFill settings:
+      <message name="IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPAD" desc="The subtitle of the view that teaches users to turn on iOS auto-fill from Chrome password manager for iPad [iPadOS only]">
+        You can use the passwords you saved to Google Password Manager in other apps on your iPad.
+      </message>
+      <message name="IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPHONE" desc="The subtitle of the view that teaches users to turn on iOS auto-fill from Chrome password manager for iPhone [iOS only]">
+        You can use the passwords you saved to Google Password Manager in other apps on your iPhone.
       </message>
       <message name="IDS_IOS_SHORT_PRODUCT_NAME" desc="The Chrome application short name">
         Chrome
@@ -457,6 +463,9 @@
       <message name="IDS_IOS_SETTINGS_SIGNIN_DISABLED_POPOVER_TEXT" desc="This text is shown when sign-in is disabled by policy and the user taps on the info pop-over of the 'Sign in to Chrome' button in the settings. [iOS only]">
         Your organization requires you to stay signed out of Chrome.
       </message>
+      <message name="IDS_IOS_SUGGESTED_STRONG_PASSWORD_HINT_DISPLAYING_EMAIL" desc="Text shown alongside a suggested password to tell user that generated passwords are recorded by chrome. [Length: unlimited] [iOS only]">
+            You won't need to remember this password. It will be saved to Google Password Manager for <ph name="EMAIL">$1<ex>johndoe@gmail.com</ex></ph>
+      </message>
       <message name="IDS_IOS_PASSWORD_CHECK_ERROR" desc="Description of the password check cell, explaining that Password Check failed. [iOS only]" meaning="Password Check failed due to some error.">
         Chrome can't check your passwords
       </message>
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SAVE_PASSWORD_FOOTER_NOT_SYNCING.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SAVE_PASSWORD_FOOTER_NOT_SYNCING.png.sha1
new file mode 100644
index 0000000..d4661c20
--- /dev/null
+++ b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SAVE_PASSWORD_FOOTER_NOT_SYNCING.png.sha1
@@ -0,0 +1 @@
+bb352378379999450f8ca6c1c1d82ddafc8e66b9
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SECOND_SUBTITLE.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SECOND_SUBTITLE.png.sha1
deleted file mode 100644
index aa688f3..0000000
--- a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SECOND_SUBTITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-33d3f4ce672ec4613a7ddbf6c62dd1c2bbebb6b2
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPAD.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPAD.png.sha1
new file mode 100644
index 0000000..77fcb9d
--- /dev/null
+++ b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPAD.png.sha1
@@ -0,0 +1 @@
+258a1dc63291c15686502f6f5f8bd041ce844a28
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPHONE.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPHONE.png.sha1
new file mode 100644
index 0000000..288a6e1
--- /dev/null
+++ b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPHONE.png.sha1
@@ -0,0 +1 @@
+1adfe98deb2e007e29b8b1f71fd8c99e90ef1a69
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SUGGESTED_STRONG_PASSWORD_HINT_DISPLAYING_EMAIL.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SUGGESTED_STRONG_PASSWORD_HINT_DISPLAYING_EMAIL.png.sha1
new file mode 100644
index 0000000..931a8be6
--- /dev/null
+++ b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SUGGESTED_STRONG_PASSWORD_HINT_DISPLAYING_EMAIL.png.sha1
@@ -0,0 +1 @@
+0e71d6d17dc728891b54f5b8918239d2839b7d4e
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index be1dbca..ac8828a 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -240,9 +240,15 @@
       <message name="IDS_IOS_SUGGESTED_PASSWORD_HINT" desc="Text shown alongside a suggested password to tell user that generated passwords are recorded by chrome. [Length: unlimited] [iOS only]">
         Chrome will remember this password for you. You don't have to remember it.
       </message>
+      <message name="IDS_IOS_SUGGESTED_STRONG_PASSWORD" desc="Text that shows a suggested generated password at user's request [Length: unlimited] [iOS only]">
+        Use Strong Password:
+      </message>
       <message name="IDS_IOS_USE_SUGGESTED_PASSWORD" desc="Button title in a suggested password popup asking user if they want to use the suggested password. [Length: 20em] [iOS only]">
         Use Suggested Password
       </message>
+      <message name="IDS_IOS_USE_SUGGESTED_STRONG_PASSWORD" desc="Button title in a suggested password popup asking user if they want to use the suggested password. [Length: 20em] [iOS only]">
+        Use Password
+      </message>
       <message name="IDS_IOS_APPLICATION_SHORTCUT_INCOGNITOSEARCH_TITLE" desc="Message when opening a New Incognito Search from springboard force touch static shortcuts. [Length: unlimited] [iOS only]." meaning="3D Touch entry to create a new Incognito tab.">
        Incognito Search
       </message>
@@ -1598,6 +1604,9 @@
       <message name="IDS_SAVE_PASSWORD_FOOTER" desc="The footer text of the infobar that offers user to save/update a password to Chrome.">
         Passwords are saved in your Google Account so you can use them on any device.
       </message>
+      <message name="IDS_SAVE_PASSWORD_FOOTER_DISPLAYING_USER_EMAIL" desc="The footer text of the infobar that offers user to save/update a password to Chrome; also display's current user's email.">
+        You can use saved passwords on any device. They're saved to Google Password Manager for <ph name="EMAIL">$1<ex>johndoe@gmail.com</ex></ph>
+      </message>
       <message name="IDS_IOS_PHOTO_LIBRARY_ADD_USAGE_DESCRIPTION" desc="Specifies the reason for writing to the user's photo library while the app is in use [Length: unlimited] [iOS only].">
         This lets you save photos to your photo library.
       </message>
@@ -2122,12 +2131,6 @@
       <message name="IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE" desc="The subtitle of the view that teaches users to turn on iOS auto-fill from Chrome password manager">
         Use the passwords you saved to Chrome in other apps on your device
       </message>
-      <message name="IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPAD" desc="The subtitle of the view that teaches users to turn on iOS auto-fill from Chrome password manager for iPad [iPadOS only]">
-        You can autofill saved passwords in other apps on your iPad.
-      </message>
-      <message name="IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPHONE" desc="The subtitle of the view that teaches users to turn on iOS auto-fill from Chrome password manager for iPhone [iOS only]">
-        You can autofill saved passwords in other apps on your iPhone.
-      </message>
       <message name="IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_SYNCING_ENABLED" desc="The subtitle of the view that teaches password-syncing users they can access passwords from their other devices">
         You can also use saved passwords on your other devices.
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPAD.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPAD.png.sha1
deleted file mode 100644
index 7aacf81..0000000
--- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPAD.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c64594d4ad1beae10f205e38d17f1430b10b8f86
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPHONE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPHONE.png.sha1
deleted file mode 100644
index 7aacf81..0000000
--- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPHONE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c64594d4ad1beae10f205e38d17f1430b10b8f86
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SUGGESTED_STRONG_PASSWORD.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SUGGESTED_STRONG_PASSWORD.png.sha1
new file mode 100644
index 0000000..f560464
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SUGGESTED_STRONG_PASSWORD.png.sha1
@@ -0,0 +1 @@
+42d0c1763c31f68587343793e43b73121c63963d
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_USE_SUGGESTED_STRONG_PASSWORD.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_USE_SUGGESTED_STRONG_PASSWORD.png.sha1
new file mode 100644
index 0000000..e95f6dc
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_USE_SUGGESTED_STRONG_PASSWORD.png.sha1
@@ -0,0 +1 @@
+c628c33391c928a2f5fefb351ad00003fa15dbf0
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_SAVE_PASSWORD_FOOTER_DISPLAYING_USER_EMAIL.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_SAVE_PASSWORD_FOOTER_DISPLAYING_USER_EMAIL.png.sha1
new file mode 100644
index 0000000..bfb92ac
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_SAVE_PASSWORD_FOOTER_DISPLAYING_USER_EMAIL.png.sha1
@@ -0,0 +1 @@
+4d1fcabf1d28f26730edb4babaa3e5d0848de7a5
\ No newline at end of file
diff --git a/ios/chrome/app/strings/resources/ios_chromium_strings_en-GB.xtb b/ios/chrome/app/strings/resources/ios_chromium_strings_en-GB.xtb
index 5d02894..02eabd0 100644
--- a/ios/chrome/app/strings/resources/ios_chromium_strings_en-GB.xtb
+++ b/ios/chrome/app/strings/resources/ios_chromium_strings_en-GB.xtb
@@ -61,6 +61,7 @@
 <translation id="5573014823074921752">Chromium tip. For more tab options, touch and hold the Show Tabs button in the toolbar, which is at the bottom or top of your screen.</translation>
 <translation id="5700709190537129682">Chromium can't check your passwords</translation>
 <translation id="5777187867430702742">Chromium page</translation>
+<translation id="5828714153471158680">To auto-fill your passwords with Password Manager, turn on Chromium in your iOS auto-fill settings:</translation>
 <translation id="5862307444128926510">Welcome to Chromium</translation>
 <translation id="5945387852661427312">You are signing in with an account managed by <ph name="DOMAIN" /> and giving its administrator control over your Chromium data. Your data will become permanently tied to this account. Signing out of Chromium will delete your data from this device, but it will remain stored in your Google Account.</translation>
 <translation id="602807004951640891">By using Chromium, you agree to the <ph name="BEGIN_LINK_TOS" />Terms of Service<ph name="END_LINK_TOS" />.</translation>
diff --git a/ios/chrome/app/strings/resources/ios_chromium_strings_lo.xtb b/ios/chrome/app/strings/resources/ios_chromium_strings_lo.xtb
index 8584716..5ca3b60 100644
--- a/ios/chrome/app/strings/resources/ios_chromium_strings_lo.xtb
+++ b/ios/chrome/app/strings/resources/ios_chromium_strings_lo.xtb
@@ -61,6 +61,7 @@
 <translation id="5573014823074921752">ເຄັດລັບກ່ຽວກັບ Chromium. ສຳລັບຕົວເລືອກແຖບເພີ່ມເຕີມ, ກະລຸນາແຕະປຸ່ມສະແດງແຖບໃນແຖບເຄື່ອງມືຄ້າງໄວ້ ເຊິ່ງຢູ່ລຸ່ມສຸດ ຫຼື ເທິງສຸດຂອງໜ້າຈໍຂອງທ່ານ.</translation>
 <translation id="5700709190537129682">Chromium ບໍ່ສາມາດກວດລະຫັດຜ່ານຂອງທ່ານໄດ້</translation>
 <translation id="5777187867430702742">ໜ້າ Chromium</translation>
+<translation id="5828714153471158680">ເພື່ອຕື່ມຂໍ້ມູນລະຫັດຜ່ານຂອງທ່ານອັດຕະໂນມັດດ້ວຍຕົວຈັດການລະຫັດຜ່ານ, ໃຫ້ເປີດໃຊ້ Chromium ໃນການຕັ້ງຄ່າການຕື່ມຂໍ້ມູນອັດຕະໂນມັດ iOS ຂອງທ່ານກ່ອນ:</translation>
 <translation id="5862307444128926510">ຍິນ​ດີ​ຕ້ອນ​ຮັບສູ່ Chromium</translation>
 <translation id="5945387852661427312">ທ່ານກຳລັງເຂົ້າສູ່ລະບົບດ້ວຍບັນຊີທີ່ຖືກຈັດການໂດຍ <ph name="DOMAIN" /> ແລະ ກຳລັງໃຫ້ການຄວບຄຸມຂໍ້ມູນ Chromium ຂອງທ່ານແກ່ຜູ້ເບິ່ງແຍງລະບົບຂອງມັນ. ຂໍ້ມູນຂອງທ່ານຈະຖືກເຊື່ອມໂຍງຢ່າງຖາວອນກັບບັນຊີນີ້. ການອອກຈາກລະບົບ Chromium ຈະລຶບຂໍ້ມູນຂອງທ່ານອອກຈາກອຸປະກອນນີ້, ແຕ່ຂໍ້ມູນຈະຍັງຄົງຖືກຮັກສາໄວ້ຢູ່ໃນບັນຊີ Google ຂອງທ່ານ.</translation>
 <translation id="602807004951640891">ໂດຍການໃຊ້ Chromium, ແມ່ນຖືວ່າທ່ານເຫັນນຳ <ph name="BEGIN_LINK_TOS" />ຂໍ້ກຳນົດການບໍລິການ<ph name="END_LINK_TOS" />.</translation>
diff --git a/ios/chrome/app/strings/resources/ios_chromium_strings_uz.xtb b/ios/chrome/app/strings/resources/ios_chromium_strings_uz.xtb
index 9cdc5bfc..a767b4e 100644
--- a/ios/chrome/app/strings/resources/ios_chromium_strings_uz.xtb
+++ b/ios/chrome/app/strings/resources/ios_chromium_strings_uz.xtb
@@ -60,6 +60,7 @@
 <translation id="5573014823074921752">Chromium maslahati. Ko‘proq varaqlarni tanlash uchun ekranning quyi yoki yuqorisida joylashgan asboblar panelidan Varaqlarni ko‘rsatish tugmasi ustiga bosib turing.</translation>
 <translation id="5700709190537129682">Chromium parollaringizni tekshira olmadi</translation>
 <translation id="5777187867430702742">Chromium sahifasi</translation>
+<translation id="5828714153471158680">Saqlangan parollaringizni Parollar menejeri avtomatik kiritishi uchun iOS avtomatik kiritish sozlamalarida Chromiumni yoqing:</translation>
 <translation id="5862307444128926510">Chromium brauzeriga xush kelibsiz</translation>
 <translation id="5945387852661427312"><ph name="DOMAIN" /> domenida boshqariladigan hisobga kirish bilan siz administratorga Chromium ma’lumotlaringizni boshqarishiga rozilik bildirasiz. Barcha Chromium ma’lumotlaringiz bu hisobga butunlay bog‘langan. Agar hisobdan chiqadigan bo‘lsangiz, bu qurilmadagi barcha ma’lumotlaringiz o‘chib ketadi, lekin Google hisobingizda saqlanib qoladi.</translation>
 <translation id="602807004951640891">Chromium ishlatish orqali siz <ph name="BEGIN_LINK_TOS" />Xizmat shartlariga<ph name="END_LINK_TOS" /> rozilik bildirgan hisoblanasiz.</translation>
diff --git a/ios/chrome/app/strings/resources/ios_google_chrome_strings_en-GB.xtb b/ios/chrome/app/strings/resources/ios_google_chrome_strings_en-GB.xtb
index 3e7b33b..774245c 100644
--- a/ios/chrome/app/strings/resources/ios_google_chrome_strings_en-GB.xtb
+++ b/ios/chrome/app/strings/resources/ios_google_chrome_strings_en-GB.xtb
@@ -109,6 +109,7 @@
   2. Tap the default browser app
   3. Select Chrome.</translation>
 <translation id="8540666473246803645">Google Chrome</translation>
+<translation id="8699528222318351168">To auto-fill your passwords with Google Password Manager, turn on Chrome in your iOS auto-fill settings:</translation>
 <translation id="8736550665979974340">Stay safe with Google Chrome</translation>
 <translation id="8772179140489533211">Shows prompts to sign in to Chrome.</translation>
 <translation id="9112744793181547300">Set Chrome as default?</translation>
diff --git a/ios/chrome/app/strings/resources/ios_google_chrome_strings_lo.xtb b/ios/chrome/app/strings/resources/ios_google_chrome_strings_lo.xtb
index 4225b7ce..0a99477 100644
--- a/ios/chrome/app/strings/resources/ios_google_chrome_strings_lo.xtb
+++ b/ios/chrome/app/strings/resources/ios_google_chrome_strings_lo.xtb
@@ -109,6 +109,7 @@
   2. ແຕະແອັບໂປຣແກຣມທ່ອງເວັບເລີ່ມຕົ້ນ
   3. ເລືອກ Chrome.</translation>
 <translation id="8540666473246803645">Google Chrome</translation>
+<translation id="8699528222318351168">ເພື່ອຕື່ມຂໍ້ມູນລະຫັດຜ່ານຂອງທ່ານອັດຕະໂນມັດດ້ວຍຕົວຈັດການລະຫັດຜ່ານ, ໃຫ້ເປີດໃຊ້ Chrome ໃນການຕັ້ງຄ່າການຕື່ມຂໍ້ມູນອັດຕະໂນມັດ iOS ຂອງທ່ານກ່ອນ:</translation>
 <translation id="8736550665979974340">ທ່ອງເວັບຢ່າງປອດໄພດ້ວຍ Google Chrome</translation>
 <translation id="8772179140489533211">ສະແດງຂໍ້ຄວາມເພື່ອເຂົ້າສູ່ລະບົບຫາ Chrome.</translation>
 <translation id="9112744793181547300">ຕັ້ງ Chrome ເປັນຄ່າເລີ່ມຕົ້ນບໍ?</translation>
diff --git a/ios/chrome/app/strings/resources/ios_google_chrome_strings_uz.xtb b/ios/chrome/app/strings/resources/ios_google_chrome_strings_uz.xtb
index 2a189a3..f648ddb9 100644
--- a/ios/chrome/app/strings/resources/ios_google_chrome_strings_uz.xtb
+++ b/ios/chrome/app/strings/resources/ios_google_chrome_strings_uz.xtb
@@ -109,6 +109,7 @@
   2. Asosiy brauzer ilovasi ustiga bosing
   3. Chrome brauzerini tanlang.</translation>
 <translation id="8540666473246803645">Google Chrome</translation>
+<translation id="8699528222318351168">Saqlangan parollaringizni Google Parollar menejeri avtomatik kiritishi uchun iOS avtomatik kiritish sozlamalarida Chromeni yoqing:</translation>
 <translation id="8736550665979974340">Google Chrome bilan himoyalaning</translation>
 <translation id="8772179140489533211">Chrome orqali kirish oynasi chiqadi.</translation>
 <translation id="9112744793181547300">Chrome asosiy brauzer sifatida tanlansinmi?</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_ar.xtb b/ios/chrome/app/strings/resources/ios_strings_ar.xtb
index 6fedd738..f9e9e7f 100644
--- a/ios/chrome/app/strings/resources/ios_strings_ar.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_ar.xtb
@@ -357,6 +357,7 @@
 <translation id="4389019817280890563">انقر لتغيير اللغة.</translation>
 <translation id="4442550905108052454">‏فتح <ph name="BEGIN_BOLD" />Settings (الإعدادات)<ph name="END_BOLD" /></translation>
 <translation id="4454246407045105932">إضافة لغة</translation>
+<translation id="4462491365653392320">تتبُّع الأسعار في علامات التبويب</translation>
 <translation id="4469418912670346607">متابعة الإعداد</translation>
 <translation id="4474494258097106883">‏المتصفِّح مصمَّم خصيصًا لهواتف iPhone</translation>
 <translation id="4476574785019001431">الإعدادات</translation>
@@ -420,6 +421,7 @@
 <translation id="4944543191714094452">البحث في الصفحة…</translation>
 <translation id="4945756290001680296">الوصول إلى إعدادات كلمات المرور</translation>
 <translation id="4979397965658815378">‏سجّل الدخول إلى حسابك على Google للحصول على الإشارات المرجعية وكلمات المرور والسجلّ والإعدادات الأخرى واستخدامها على جميع أجهزتك.</translation>
+<translation id="4986678885919050584">‏إزالة حساب Google من هذا الجهاز</translation>
 <translation id="5005498671520578047">نسخ كلمة المرور</translation>
 <translation id="5017828934289857214">تذكيري لاحقًا</translation>
 <translation id="5037676449506322593">اختيار الكل</translation>
@@ -521,6 +523,7 @@
 <translation id="5979837087407522202">بحث عن كلمات المرور</translation>
 <translation id="5982717868370722439">يمكنك إضافة البيانات الحالية إلى <ph name="USER_EMAIL" />.</translation>
 <translation id="5984222099446776634">تم الانتقال إليها مؤخرًا</translation>
+<translation id="5988097621740394599">يمكنك الاطّلاع على الأسعار المخفَّضة في علامات التبويب.</translation>
 <translation id="5988851877894965432">‏فتح عناوين URL في Chrome</translation>
 <translation id="6012140227487808125">جارٍ التشفير...</translation>
 <translation id="6021332621416007159">فتح في...</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_bn.xtb b/ios/chrome/app/strings/resources/ios_strings_bn.xtb
index 667fadc..7f05dae 100644
--- a/ios/chrome/app/strings/resources/ios_strings_bn.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_bn.xtb
@@ -356,6 +356,7 @@
 <translation id="4389019817280890563">ভাষা পরিবর্তন করতে ট্যাপ করুন।</translation>
 <translation id="4442550905108052454"><ph name="BEGIN_BOLD" />সেটিংস<ph name="END_BOLD" /> খুলুন</translation>
 <translation id="4454246407045105932">ভাষা যোগ করার সেটিংস</translation>
+<translation id="4462491365653392320">ট্যাবে দাম ট্র্যাক করুন</translation>
 <translation id="4469418912670346607">সেট-আপ করা চালিয়ে যান</translation>
 <translation id="4474494258097106883">আপনার iPhone-এর জন্য তৈরি করা হয়েছে</translation>
 <translation id="4476574785019001431">সেটিংস</translation>
@@ -419,6 +420,7 @@
 <translation id="4944543191714094452">পৃষ্ঠাতে খুঁজুন…</translation>
 <translation id="4945756290001680296">পাসওয়ার্ড সেটিংস অ্যাক্সেস করুন</translation>
 <translation id="4979397965658815378">আপনার সব ডিভাইসে আপনার বুকমার্ক, পাসওয়ার্ড, ইতিহাস এবং অন্যান্য সেটিংস পেতে আপনার Google অ্যাকাউন্টে সাইন-ইন করুন</translation>
+<translation id="4986678885919050584">এই ডিভাইস থেকে সরান</translation>
 <translation id="5005498671520578047">পাসওয়ার্ড কপি করুন</translation>
 <translation id="5017828934289857214">আমাকে পরে মনে করিয়ে দিও</translation>
 <translation id="5037676449506322593">সকল বেছে নিন</translation>
@@ -521,6 +523,7 @@
 <translation id="5979837087407522202">পাসওয়ার্ড খুঁজুন</translation>
 <translation id="5982717868370722439">বিদ্যমান ডেটা <ph name="USER_EMAIL" /> এ যোগ করুন।</translation>
 <translation id="5984222099446776634">সাম্প্রতিককালে দেখা</translation>
+<translation id="5988097621740394599">ট্যাবে দাম কমার তথ্য দেখুন।</translation>
 <translation id="5988851877894965432">Chrome ব্রাউজার থেকে ইউআরএলগুলি খুলুন</translation>
 <translation id="6012140227487808125">এনক্রিপ্ট হচ্ছে...</translation>
 <translation id="6021332621416007159">এতে খুলুন...</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_cs.xtb b/ios/chrome/app/strings/resources/ios_strings_cs.xtb
index a42e9a7..a5d4c451 100644
--- a/ios/chrome/app/strings/resources/ios_strings_cs.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_cs.xtb
@@ -356,6 +356,7 @@
 <translation id="4389019817280890563">Klepnutím změníte jazyk</translation>
 <translation id="4442550905108052454">Otevřete <ph name="BEGIN_BOLD" />Nastavení<ph name="END_BOLD" /></translation>
 <translation id="4454246407045105932">Přidat jazyk</translation>
+<translation id="4462491365653392320">Sledovat ceny na kartách</translation>
 <translation id="4469418912670346607">Pokračovat v nastavování</translation>
 <translation id="4474494258097106883">Vytvořeno pro iPhone</translation>
 <translation id="4476574785019001431">Nastavení</translation>
@@ -419,6 +420,7 @@
 <translation id="4944543191714094452">Najít na stránce…</translation>
 <translation id="4945756290001680296">Otevřít nastavení hesla</translation>
 <translation id="4979397965658815378">Přihlaste se pomocí účtu Google a získejte záložky, hesla, historii a další nastavení do všech svých zařízení.</translation>
+<translation id="4986678885919050584">Odstranit z tohoto zařízení</translation>
 <translation id="5005498671520578047">Kopírování hesla</translation>
 <translation id="5017828934289857214">Připomenout později</translation>
 <translation id="5037676449506322593">Vybrat vše</translation>
@@ -520,6 +522,7 @@
 <translation id="5979837087407522202">Prohledat hesla</translation>
 <translation id="5982717868370722439">Přidat existující data do účtu <ph name="USER_EMAIL" />.</translation>
 <translation id="5984222099446776634">Nedávno navštívené</translation>
+<translation id="5988097621740394599">Zobrazit pokles ceny na kartách</translation>
 <translation id="5988851877894965432">Otevírat adresy URL v Chromu</translation>
 <translation id="6012140227487808125">Šifrování...</translation>
 <translation id="6021332621416007159">Otevřít v ...</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_en-GB.xtb b/ios/chrome/app/strings/resources/ios_strings_en-GB.xtb
index 495c116..0f5d2a15 100644
--- a/ios/chrome/app/strings/resources/ios_strings_en-GB.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_en-GB.xtb
@@ -235,6 +235,7 @@
 <translation id="3157387275655328056">Add to Reading List</translation>
 <translation id="3157684681743766797">Mark All…</translation>
 <translation id="3169472444629675720">Discover</translation>
+<translation id="3174662312949010067">You can also use saved passwords on your other devices.</translation>
 <translation id="3175081911749765310">Web Services</translation>
 <translation id="3178650076442119961">Active today</translation>
 <translation id="3181825792072797598">Turn on Sync</translation>
@@ -436,6 +437,7 @@
 <translation id="5118764316110575523">Off</translation>
 <translation id="5127805178023152808">Sync is off</translation>
 <translation id="5132942445612118989">Sync your passwords, history and more on all devices</translation>
+<translation id="51339916068413997">You can auto-fill saved passwords in other apps on your iPad.</translation>
 <translation id="5140288047769711648">Chrome will remember this password for you. You don't have to remember it.</translation>
 <translation id="5150492518600715772">Send to your device</translation>
 <translation id="5168414296986405587">Built for iPadOS</translation>
@@ -474,6 +476,7 @@
 Handoff must also be enabled in the General section of Settings, and your devices must use the same iCloud account.</translation>
 <translation id="5551897871312988470">Offer to translate</translation>
 <translation id="5556459405103347317">Reload</translation>
+<translation id="5557664881350188277">You can auto-fill saved passwords in other apps on your iPhone.</translation>
 <translation id="5580834567471114021">Add to reading list for later?</translation>
 <translation id="5592679540098330836">Turn on sync for <ph name="NAME" /></translation>
 <translation id="560322036295180549">Turned off by your organisation</translation>
@@ -768,6 +771,7 @@
 <translation id="8076014560081431679">Saved site settings will not be deleted and may reflect your browsing habits. <ph name="BEGIN_LINK" />Find out more<ph name="END_LINK" /></translation>
 <translation id="8079602123447022758">This setting is managed; double-tap for more information</translation>
 <translation id="8080028325999236607">Close All Tabs</translation>
+<translation id="8101409298456377967">Create, save and manage your passwords so that you can easily sign in to sites and apps. <ph name="BEGIN_LINK" />Learn more<ph name="END_LINK" /></translation>
 <translation id="8105368624971345109">Turn Off</translation>
 <translation id="8114753159095730575">File download is available. Options available near bottom of screen.</translation>
 <translation id="8132598642024322408">Now <ph name="PRICE" />, was <ph name="PREVIOUS_PRICE" />.</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_fa.xtb b/ios/chrome/app/strings/resources/ios_strings_fa.xtb
index d3a0e7c..32e0d98c 100644
--- a/ios/chrome/app/strings/resources/ios_strings_fa.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_fa.xtb
@@ -356,6 +356,7 @@
 <translation id="4389019817280890563">برای تغییر زبان، ضربه بزنید.</translation>
 <translation id="4442550905108052454"><ph name="BEGIN_BOLD" />تنظیمات<ph name="END_BOLD" /> را باز کنید</translation>
 <translation id="4454246407045105932">افزودن زبان</translation>
+<translation id="4462491365653392320">ردیابی قیمت در برگه‌ها</translation>
 <translation id="4469418912670346607">ادامه راه‌اندازی</translation>
 <translation id="4474494258097106883">‏ساخته‌شده برای iPhone شما</translation>
 <translation id="4476574785019001431">تنظیمات</translation>
@@ -419,6 +420,7 @@
 <translation id="4944543191714094452">یافتن در صفحه…</translation>
 <translation id="4945756290001680296">دسترسی به تنظیمات گذرواژه</translation>
 <translation id="4979397965658815378">‏برای دریافت نشانک‌ها، گذرواژه‌ها، سابقه و تنظیمات دیگر در همه دستگاه‌هایتان، به سیستم حساب Google خودتان وارد شوید</translation>
+<translation id="4986678885919050584">حذف از این دستگاه</translation>
 <translation id="5005498671520578047">کپی گذرواژه</translation>
 <translation id="5017828934289857214">بعداً به من یادآوری شود</translation>
 <translation id="5037676449506322593">انتخاب همه</translation>
@@ -520,6 +522,7 @@
 <translation id="5979837087407522202">جستجوی گذرواژه‌ها</translation>
 <translation id="5982717868370722439">افزودن داده‌های موجود به <ph name="USER_EMAIL" />.</translation>
 <translation id="5984222099446776634">به‌تازگی بازدیدشده</translation>
+<translation id="5988097621740394599">کاهش قیمت را در برگه‌ها مشاهده کنید.</translation>
 <translation id="5988851877894965432">‏باز کردن نشانی‌های وب در Chrome</translation>
 <translation id="6012140227487808125">در حال رمزگذاری…</translation>
 <translation id="6021332621416007159">بازکردن در...</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_gu.xtb b/ios/chrome/app/strings/resources/ios_strings_gu.xtb
index 446723e..bded8b8 100644
--- a/ios/chrome/app/strings/resources/ios_strings_gu.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_gu.xtb
@@ -356,6 +356,7 @@
 <translation id="4389019817280890563">ભાષા બદલવા માટે ટૅપ કરો.</translation>
 <translation id="4442550905108052454"><ph name="BEGIN_BOLD" />સેટિંગ <ph name="END_BOLD" />ખોલો</translation>
 <translation id="4454246407045105932">ભાષા ઉમેરો</translation>
+<translation id="4462491365653392320">ટૅબ પર કિંમત ટ્રૅક કરો</translation>
 <translation id="4469418912670346607">સેટઅપ ચાલુ રાખો</translation>
 <translation id="4474494258097106883">તમારા iPhone માટે બનાવાયેલું</translation>
 <translation id="4476574785019001431">સેટિંગ</translation>
@@ -419,6 +420,7 @@
 <translation id="4944543191714094452">પેજમાં શોધો…</translation>
 <translation id="4945756290001680296">પાસવર્ડના સેટિંગને ઍક્સેસ કરો</translation>
 <translation id="4979397965658815378">તમારા બુકમાર્ક, પાસવર્ડ, ઇતિહાસ અને બીજા સેટિંગને તમારા બધા ડિવાઇસ પર મેળવવા માટે તમારા Google એકાઉન્ટ સાથે સાઇન ઇન કરો.</translation>
+<translation id="4986678885919050584">આ ડિવાઇસમાંથી કાઢી નાખો</translation>
 <translation id="5005498671520578047">પાસવર્ડની કૉપિ કરો</translation>
 <translation id="5017828934289857214">મને પછીથી યાદ કરાવો</translation>
 <translation id="5037676449506322593">બધા પસંદ કરો</translation>
@@ -520,6 +522,7 @@
 <translation id="5979837087407522202">પાસવર્ડ શોધો</translation>
 <translation id="5982717868370722439">અસ્તિત્વમાંના ડેટાને <ph name="USER_EMAIL" /> માં ઉમેરો.</translation>
 <translation id="5984222099446776634">હાલમાં મુલાકાત લીધેલા</translation>
+<translation id="5988097621740394599">તમારી ટૅબ પર કિંમતમાં ઘટાડા વિશેની માહિતી જુઓ.</translation>
 <translation id="5988851877894965432">URLsને Chromeમાં ખોલો</translation>
 <translation id="6012140227487808125">એન્ક્રિપ્ટ કરી રહ્યું છે...</translation>
 <translation id="6021332621416007159">આમાં ખોલો...</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_hy.xtb b/ios/chrome/app/strings/resources/ios_strings_hy.xtb
index 0623665..fdd7606 100644
--- a/ios/chrome/app/strings/resources/ios_strings_hy.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_hy.xtb
@@ -356,6 +356,7 @@
 <translation id="4389019817280890563">Հպեք՝ լեզուն փոխելու համար։</translation>
 <translation id="4442550905108052454">Բացեք <ph name="BEGIN_BOLD" />Կարգավորումները<ph name="END_BOLD" />։</translation>
 <translation id="4454246407045105932">Ավելացնել լեզու</translation>
+<translation id="4462491365653392320">Հետագծել գները ներդիրներում</translation>
 <translation id="4469418912670346607">Շարունակել կարգավորումը</translation>
 <translation id="4474494258097106883">Ստեղծված է հատուկ ձեր iPhone-ի համար</translation>
 <translation id="4476574785019001431">Կարգավորումներ</translation>
@@ -419,6 +420,7 @@
 <translation id="4944543191714094452">Գտնել էջում…</translation>
 <translation id="4945756290001680296">Կաղտնաբառերի կարգավորման հասանելիություն</translation>
 <translation id="4979397965658815378">Մուտք գործեք ձեր Google հաշվով՝ ձեր էջանիշները, գաղտնաբառերը, պատմությունը և այլ կարգավորումները ձեր բոլոր սարքերում օգտագործելու համար:</translation>
+<translation id="4986678885919050584">Հեռացնել սարքից</translation>
 <translation id="5005498671520578047">Պատճենել գաղտնաբառը</translation>
 <translation id="5017828934289857214">Հիշեցնել ինձ ավելի ուշ</translation>
 <translation id="5037676449506322593">Select All</translation>
@@ -520,6 +522,7 @@
 <translation id="5979837087407522202">Գաղտնաբառերի որոնում</translation>
 <translation id="5982717868370722439">Առկա տվյալների ավելացում <ph name="USER_EMAIL" /> հաշվում:</translation>
 <translation id="5984222099446776634">Recently Visited</translation>
+<translation id="5988097621740394599">Դիտեք ձեր ներդիրներում գների իջեցման մասին ծանուցումները։</translation>
 <translation id="5988851877894965432">Բացել URL-ները Chrome-ում</translation>
 <translation id="6012140227487808125">Գաղտնագրում…</translation>
 <translation id="6021332621416007159">Բացել այս հավելվածում…</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_lo.xtb b/ios/chrome/app/strings/resources/ios_strings_lo.xtb
index 79f3eea..c1be626 100644
--- a/ios/chrome/app/strings/resources/ios_strings_lo.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_lo.xtb
@@ -235,6 +235,7 @@
 <translation id="3157387275655328056">ເພີ່ມໃສ່ລາຍການທີ່ຈະອ່ານ</translation>
 <translation id="3157684681743766797">ໝາຍທັງໝົດ…</translation>
 <translation id="3169472444629675720">ຄົ້ນຫາ</translation>
+<translation id="3174662312949010067">ທ່ານສາມາດໃຊ້ລະຫັດຜ່ານທີ່ບັນທຶກໄວ້ຢູ່ອຸປະກອນອື່ນຂອງທ່ານໄດ້ນຳ.</translation>
 <translation id="3175081911749765310">ການ​ບໍ​ລິ​ການ​ເວັບ</translation>
 <translation id="3178650076442119961">ນຳໃຊ້ມື້ນີ້</translation>
 <translation id="3181825792072797598">ເປີດ Sync</translation>
@@ -345,6 +346,7 @@
 <translation id="4272631900155121838">ເພື່ອສະແກນລະຫັດ QR, ໃຫ້ເປີດນຳໃຊ້ກ້ອງຖ່າຍຮູບຈາກການຕັ້ງຄ່າ</translation>
 <translation id="4277990410970811858">Safe Browsing</translation>
 <translation id="4281844954008187215">ຂໍ້ກໍານົດການບໍລິການ</translation>
+<translation id="4304713468139749426">ຕົວຈັດການລະຫັດຜ່ານ</translation>
 <translation id="430793432425771671">ຊິງຄ໌ທຸກຢ່າງ</translation>
 <translation id="4309403553630140242">ແຕະສອງເທື່ອສຳລັບຂໍ້ມູນເພີ່ມເຕີມ</translation>
 <translation id="430967081421617822">ຕະຫຼອດເວລາ</translation>
@@ -356,6 +358,7 @@
 <translation id="4389019817280890563">ແຕະເພື່ອປ່ຽນພາສາ.</translation>
 <translation id="4442550905108052454">ເປີດ <ph name="BEGIN_BOLD" />ການຕັ້ງຄ່າ<ph name="END_BOLD" /></translation>
 <translation id="4454246407045105932">ເພີ່ມພາສາ</translation>
+<translation id="4462491365653392320">ຕິດຕາມລາຄາຢູ່ແຖບຕ່າງໆ</translation>
 <translation id="4469418912670346607">ສືບຕໍ່ການຕັ້ງຄ່າ</translation>
 <translation id="4474494258097106883">ສ້າງມາສຳລັບ iPhone ຂອງທ່ານ</translation>
 <translation id="4476574785019001431">ການຕັ້ງຄ່າ</translation>
@@ -419,6 +422,7 @@
 <translation id="4944543191714094452">ຊອກຫາໃນໜ້າ…</translation>
 <translation id="4945756290001680296">ເຂົ້າເຖິງການຕັ້ງຄ່າລະຫັດຜ່ານ</translation>
 <translation id="4979397965658815378">ເຂົ້າສູ່ລະບົບດ້ວຍບັນຊີ Google ຂອງທ່ານເພື່ອໃຊ້ບຸກມາກ, ລະຫັດຜ່ານ, ປະຫວັດ ແລະ ການຕັ້ງຄ່າອື່ນຢູ່ໃນທຸກອຸປະກອນຂອງທ່ານ</translation>
+<translation id="4986678885919050584">ລຶບອອກຈາກອຸປະກອນນີ້</translation>
 <translation id="5005498671520578047">ອັດ​ສຳ​ເນົາ​ລະ​ຫັດ​ຜ່ານ</translation>
 <translation id="5017828934289857214">ແຈ້ງເຕືອນຂ້ອຍພາຍຫຼັງ</translation>
 <translation id="5037676449506322593">ເລືອກ​ທັງ​ຫມົດ</translation>
@@ -433,6 +437,7 @@
 <translation id="5118764316110575523">ປິດ</translation>
 <translation id="5127805178023152808">ຊິງ​ຄ໌​ປິດ</translation>
 <translation id="5132942445612118989">ຊິ້ງຂໍ້ມູນລະຫັດຜ່ານ, ປະຫວັດຂອງທ່ານ ແລະ ອື່ນໆອີກຢູ່ໃນທຸກອຸປະກອນ</translation>
+<translation id="51339916068413997">ທ່ານສາມາດຕື່ມຂໍ້ມູນລະຫັດຜ່ານທີ່ບັນທຶກໄວ້ອັດຕະໂນມັດໃນແອັບອື່ນໆຢູ່ iPad ຂອງທ່ານໄດ້.</translation>
 <translation id="5140288047769711648">Chrome ຈະຈື່ລະຫັດຜ່ານນີ້ສຳລັບທ່ານ. ທ່ານບໍ່ຈໍາເປັນຕ້ອງຈື່ມັນ.</translation>
 <translation id="5150492518600715772">ສົ່ງຫາອຸປະກອນຂອງທ່ານ</translation>
 <translation id="5168414296986405587">ສ້າງມາສຳລັບ iPadOS</translation>
@@ -471,6 +476,7 @@
 Handoff ຍັງ​ຕ້ອງ​ໄດ້​ເປີດ​ໃຊ້​ງານ​ຢູ່​ໃນ​ພາກ​ທົ່ວ​ໄປ​ຂອງ​ການ​ຕັ້ງ​ຄ່າ​ນຳ​ອີກ, ແລະ​ອຸ​ປະ​ກອນ​ຂອງ​ທ່ານ​ຕ້ອງ​ໃຊ້​ບັນ​ຊີ iCloud ດຽວ​ກັນ​ນຳ.</translation>
 <translation id="5551897871312988470">ໃຫ້ການແປພາສາ</translation>
 <translation id="5556459405103347317">ໂຫຼດຄືນໃໝ່</translation>
+<translation id="5557664881350188277">ທ່ານສາມາດຕື່ມຂໍ້ມູນລະຫັດຜ່ານທີ່ບັນທຶກໄວ້ອັດຕະໂນມັດໃນແອັບອື່ນໆຢູ່ iPhone ຂອງທ່ານໄດ້.</translation>
 <translation id="5580834567471114021">ເພີ່ມໃສ່ລາຍຊື່ການອ່ານສຳລັບພາຍຫຼັງບໍ?</translation>
 <translation id="5592679540098330836">ເປີດການຊິ້ງຂໍ້ມູນສຳລັບ <ph name="NAME" /></translation>
 <translation id="560322036295180549">ຖືກປິດໄວ້ໂດຍອົງການຂອງທ່ານ</translation>
@@ -520,6 +526,7 @@
 <translation id="5979837087407522202">ຊອກຫາລະຫັດຜ່ານ</translation>
 <translation id="5982717868370722439">ເພີ່ມຂໍ້ມູນທີ່ມີຢູ່ໃສ່ <ph name="USER_EMAIL" />.</translation>
 <translation id="5984222099446776634">ເຂົ້າເບິ່ງບໍ່​ດົນ​ມາ​ນີ້</translation>
+<translation id="5988097621740394599">ເບິ່ງການຫຼຸດລາຄາຢູ່ແຖບຂອງທ່ານ.</translation>
 <translation id="5988851877894965432">ເປີດ URL ໃນ Chrome</translation>
 <translation id="6012140227487808125">ກຳລັງເຂົ້າລະຫັດ...</translation>
 <translation id="6021332621416007159">ເປີດ​ຢູ່​ໃນ...</translation>
@@ -764,6 +771,7 @@
 <translation id="8076014560081431679">ການຕັ້ງຄ່າເວັບໄຊທີ່ບັນທຶກໄວ້ຈະບໍ່ຖືກລຶບ ແລະ ອາດຈະສະທ້ອນເຖິງນິໄສການທ່ອງເວັບຂອງທ່ານ. <ph name="BEGIN_LINK" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK" /></translation>
 <translation id="8079602123447022758">ການຕັ້ງຄ່ານີ້ໄດ້ຮັບການຈັດການ, ແຕະສອງຄັ້ງສຳລັບຂໍ້ມູນເພີ່ມເຕີມ</translation>
 <translation id="8080028325999236607">ປິດ​ທຸກແຖບ</translation>
+<translation id="8101409298456377967">ສ້າງ, ບັນທຶກ ແລະ ຈັດການລະຫັດຜ່ານຂອງທ່ານເພື່ອໃຫ້ທ່ານສາມາດເຂົ້າສູ່ລະບົບຫາເວັບໄຊ ແລະ ແອັບໄດ້ຢ່າງງ່າຍດາຍ. <ph name="BEGIN_LINK" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK" /></translation>
 <translation id="8105368624971345109">ປິດ</translation>
 <translation id="8114753159095730575">ການດາວໂຫຼດໄຟລ໌ບໍ່ສາມາດໃຊ້ໄດ້. ມີຕົວເລືອກຕ່າງໆຢູ່ໃກ້ສ່ວນລຸ່ມຂອງໜ້າຈໍ.</translation>
 <translation id="8132598642024322408">ຕອນນີ້ <ph name="PRICE" /> ກ່ອນໜ້ານີ້ <ph name="PREVIOUS_PRICE" />.</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_mr.xtb b/ios/chrome/app/strings/resources/ios_strings_mr.xtb
index f79e216..5074257 100644
--- a/ios/chrome/app/strings/resources/ios_strings_mr.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_mr.xtb
@@ -356,6 +356,7 @@
 <translation id="4389019817280890563">भाषा बदलण्यासाठी टॅप करा.</translation>
 <translation id="4442550905108052454"><ph name="BEGIN_BOLD" />सेटिंग्ज<ph name="END_BOLD" /> उघडा</translation>
 <translation id="4454246407045105932">भाषा जोडा</translation>
+<translation id="4462491365653392320">टॅब यावर किमती ट्रॅक करा</translation>
 <translation id="4469418912670346607">सेटअप सुरू ठेवा</translation>
 <translation id="4474494258097106883">तुमच्या iPhone साठी तयार केलेले</translation>
 <translation id="4476574785019001431">सेटिंग्ज</translation>
@@ -420,6 +421,7 @@
 <translation id="4944543191714094452">या पेजमध्ये शोधा…</translation>
 <translation id="4945756290001680296">पासवर्ड सेटिंग्ज अ‍ॅक्सेस करा</translation>
 <translation id="4979397965658815378">आपल्या सर्व डिव्हाइसेसवर तुमचे बुकमार्क, पासवर्ड, इतिहास आणि अन्य सेटिंग्ज प्राप्त करण्यासाठी आपल्या Google खात्यासह साइन इन करा</translation>
+<translation id="4986678885919050584">या डिव्हाइसवरून काढून टाका</translation>
 <translation id="5005498671520578047">पासवर्ड कॉपी करा</translation>
 <translation id="5017828934289857214">मला नंतर आठवण करून द्या</translation>
 <translation id="5037676449506322593">सर्व निवडा</translation>
@@ -521,6 +523,7 @@
 <translation id="5979837087407522202">पासवर्ड शोधा</translation>
 <translation id="5982717868370722439">विद्यमान डेटा <ph name="USER_EMAIL" /> मध्ये जोडा.</translation>
 <translation id="5984222099446776634">अलीकडे भेट दिलेले</translation>
+<translation id="5988097621740394599">तुमच्या टॅबवर कमी झालेली किंमत पहा.</translation>
 <translation id="5988851877894965432">Chrome URL उघडा</translation>
 <translation id="6012140227487808125">कूटबद्ध करीत आहे...</translation>
 <translation id="6021332621416007159">यामध्ये उघडा...</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_ms.xtb b/ios/chrome/app/strings/resources/ios_strings_ms.xtb
index 23dc6fa..0363fa7 100644
--- a/ios/chrome/app/strings/resources/ios_strings_ms.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_ms.xtb
@@ -356,6 +356,7 @@
 <translation id="4389019817280890563">Ketik untuk menukar bahasa.</translation>
 <translation id="4442550905108052454">Buka <ph name="BEGIN_BOLD" />Tetapan<ph name="END_BOLD" /></translation>
 <translation id="4454246407045105932">Tambahkan Bahasa</translation>
+<translation id="4462491365653392320">Jejaki Harga pada Tab</translation>
 <translation id="4469418912670346607">Teruskan Persediaan</translation>
 <translation id="4474494258097106883">Dibina untuk iPhone Anda</translation>
 <translation id="4476574785019001431">Tetapan</translation>
@@ -419,6 +420,7 @@
 <translation id="4944543191714094452">Cari dalam Halaman…</translation>
 <translation id="4945756290001680296">Akses tetapan kata laluan</translation>
 <translation id="4979397965658815378">Log masuk dengan Akaun Google anda untuk mendapatkan penanda halaman, kata laluan, sejarah dan tetapan anda yang lain pada semua peranti anda</translation>
+<translation id="4986678885919050584">Alih keluar daripada Peranti ini</translation>
 <translation id="5005498671520578047">Salin kata laluan</translation>
 <translation id="5017828934289857214">Ingatkan Saya Kemudian</translation>
 <translation id="5037676449506322593">Pilih Semua</translation>
@@ -520,6 +522,7 @@
 <translation id="5979837087407522202">Cari Kata Laluan</translation>
 <translation id="5982717868370722439">Tambahkan data sedia ada pada <ph name="USER_EMAIL" />.</translation>
 <translation id="5984222099446776634">Dilawati Baru-baru Ini</translation>
+<translation id="5988097621740394599">Lihat penurunan harga pada tab anda.</translation>
 <translation id="5988851877894965432">Buka URL dalam Chrome</translation>
 <translation id="6012140227487808125">Menyulitkan...</translation>
 <translation id="6021332621416007159">Buka dalam...</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_ne.xtb b/ios/chrome/app/strings/resources/ios_strings_ne.xtb
index 985cc8c..6359a2a1 100644
--- a/ios/chrome/app/strings/resources/ios_strings_ne.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_ne.xtb
@@ -356,6 +356,7 @@
 <translation id="4389019817280890563">भाषा परिवर्तन गर्न ट्याप गर्नुहोस्।</translation>
 <translation id="4442550905108052454"><ph name="BEGIN_BOLD" />सेटिङ<ph name="END_BOLD" /> खोल्नुहोस्</translation>
 <translation id="4454246407045105932">भाषा थप्नुहोस्</translation>
+<translation id="4462491365653392320">ट्याबहरूमा मूल्य ट्र्याक गर्नुहोस्</translation>
 <translation id="4469418912670346607">सेटअप जारी राख्नुहोस्</translation>
 <translation id="4474494258097106883">तपाईंको iPhone का लागि बनाइएको</translation>
 <translation id="4476574785019001431">सेटिङहरू</translation>
@@ -419,6 +420,7 @@
 <translation id="4944543191714094452">पृष्ठमा फेला पार्नुहोस्…</translation>
 <translation id="4945756290001680296">पासवर्डका सेटिङहरूमाथि पहुँच राख्नुहोस्</translation>
 <translation id="4979397965658815378">आफ्ना सबै यन्त्रहरूमा आफ्ना पुस्तक चिन्ह, इतिहास, पासवर्ड र अन्य सेटिङहरू प्राप्त गर्न आफ्नो Google खाता मार्फत साइन इन गर्नुहोस्।</translation>
+<translation id="4986678885919050584">यो डिभाइसबाट हटाउनुहोस्</translation>
 <translation id="5005498671520578047">पासवर्ड प्रतिलिपि गर्नुहोस्</translation>
 <translation id="5017828934289857214">मलाई पछि स्मरण गराइयोस्</translation>
 <translation id="5037676449506322593">सबै चयन गर्नुहोस्</translation>
@@ -520,6 +522,7 @@
 <translation id="5979837087407522202">पासवर्डहरू खोज्नुहोस्</translation>
 <translation id="5982717868370722439">विद्यमान डेटा <ph name="USER_EMAIL" /> मा थप्नुहोस्।</translation>
 <translation id="5984222099446776634">भर्खरै भ्रमण गरिएको</translation>
+<translation id="5988097621740394599">आफ्ना ट्याबहरूमा मूल्य घटेको कुरासम्बन्धी जानकारी हेर्नुहोस्।</translation>
 <translation id="5988851877894965432">Chrome मा URL हरू खोल्नुहोस्</translation>
 <translation id="6012140227487808125">इन्क्रिप्सन गर्दै ...</translation>
 <translation id="6021332621416007159">यसमा खोल्नुहोस्...</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_pa.xtb b/ios/chrome/app/strings/resources/ios_strings_pa.xtb
index 59096f9..ab6fb9c 100644
--- a/ios/chrome/app/strings/resources/ios_strings_pa.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_pa.xtb
@@ -354,6 +354,7 @@
 <translation id="4389019817280890563">ਭਾਸ਼ਾ ਬਦਲਣ ਲਈ ਟੈਪ ਕਰੋ।</translation>
 <translation id="4442550905108052454"><ph name="BEGIN_BOLD" />ਸੈਟਿੰਗਾਂ<ph name="END_BOLD" /> ਖੋਲ੍ਹੋ</translation>
 <translation id="4454246407045105932">ਭਾਸ਼ਾ ਸ਼ਾਮਲ ਕਰੋ</translation>
+<translation id="4462491365653392320">ਟੈਬਾਂ ਵਿੱਚ ਕੀਮਤਾਂ 'ਤੇ ਨਜ਼ਰ ਰੱਖੋ</translation>
 <translation id="4469418912670346607">ਸੈੱਟਅੱਪ ਜਾਰੀ ਰੱਖੋ</translation>
 <translation id="4474494258097106883">ਤੁਹਾਡੇ iPhone ਲਈ ਬਣਾਇਆ ਗਿਆ</translation>
 <translation id="4476574785019001431">ਸੈਟਿੰਗਾਂ</translation>
@@ -417,6 +418,7 @@
 <translation id="4944543191714094452">ਪੰਨੇ ਵਿੱਚ ਲੱਭੋ…</translation>
 <translation id="4945756290001680296">ਪਾਸਵਰਡ ਸੈਟਿੰਗਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰੋ</translation>
 <translation id="4979397965658815378">ਆਪਣੇ ਸਾਰੇ ਡੀਵਾਈਸਾਂ 'ਤੇ ਆਪਣੇ ਬੁੱਕਮਾਰਕਾਂ, ਪਾਸਵਰਡਾਂ, ਇਤਿਹਾਸ ਅਤੇ ਹੋਰ ਸੈਟਿੰਗਾਂ ਨੂੰ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ ਆਪਣੇ Google ਖਾਤੇ ਨਾਲ ਸਾਈਨ-ਇਨ ਕਰੋ</translation>
+<translation id="4986678885919050584">ਇਸ ਡੀਵਾਈਸ ਤੋਂ ਹਟਾਓ</translation>
 <translation id="5005498671520578047">ਪਾਸਵਰਡ ਕਾਪੀ ਕਰੋ</translation>
 <translation id="5017828934289857214">ਮੈਨੂੰ ਬਾਅਦ ਵਿੱਚ ਯਾਦ ਕਰਵਾਓ</translation>
 <translation id="5037676449506322593">ਸਾਰਿਆਂ ਨੂੰ ਚੁਣੋ</translation>
@@ -518,6 +520,7 @@
 <translation id="5979837087407522202">ਪਾਸਵਰਡ ਖੋਜੋ</translation>
 <translation id="5982717868370722439">ਮੌਜੂਦਾ ਡਾਟੇ ਨੂੰ <ph name="USER_EMAIL" /> ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ।</translation>
 <translation id="5984222099446776634">ਹਾਲੀਆ ਵਿਜਿਟ ਕੀਤੇ</translation>
+<translation id="5988097621740394599">ਆਪਣੀਆਂ ਟੈਬਾਂ 'ਤੇ ਘਟੀਆਂ ਹੋਈਆਂ ਕੀਮਤਾਂ ਦੇਖੋ।</translation>
 <translation id="5988851877894965432">URL ਨੂੰ Chrome ਵਿੱਚ ਖੋਲ੍ਹੋ</translation>
 <translation id="6012140227487808125">ਇਨਕ੍ਰਿਪਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…</translation>
 <translation id="6021332621416007159">ਇਸ ਵਿੱਚ ਖੋਲ੍ਹੋ ...</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_sk.xtb b/ios/chrome/app/strings/resources/ios_strings_sk.xtb
index ed005dd..10c8faff4 100644
--- a/ios/chrome/app/strings/resources/ios_strings_sk.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_sk.xtb
@@ -356,6 +356,7 @@
 <translation id="4389019817280890563">Klepnutím zmeníte jazyk.</translation>
 <translation id="4442550905108052454">Otvorte <ph name="BEGIN_BOLD" />Nastavenia<ph name="END_BOLD" /></translation>
 <translation id="4454246407045105932">Pridanie jazyka</translation>
+<translation id="4462491365653392320">Sledovanie cien na kartách</translation>
 <translation id="4469418912670346607">Pokračovať v nastavovaní</translation>
 <translation id="4474494258097106883">Vytvorené pre váš iPhone</translation>
 <translation id="4476574785019001431">Nastavenia</translation>
@@ -419,6 +420,7 @@
 <translation id="4944543191714094452">Nájsť na stránke…</translation>
 <translation id="4945756290001680296">Prejdite do nastavení hesla</translation>
 <translation id="4979397965658815378">Prihláste sa účtom Google a používajte svoje záložky, heslá, históriu a ďalšie nastavenia vo všetkých svojich zariadeniach</translation>
+<translation id="4986678885919050584">Odstrániť účet z tohto zariadenia</translation>
 <translation id="5005498671520578047">Kopírovanie hesla</translation>
 <translation id="5017828934289857214">Pripomenúť neskôr</translation>
 <translation id="5037676449506322593">Vybrať všetko</translation>
@@ -520,6 +522,7 @@
 <translation id="5979837087407522202">Vyhľadajte heslá</translation>
 <translation id="5982717868370722439">Pridať existujúce dáta do účtu <ph name="USER_EMAIL" />.</translation>
 <translation id="5984222099446776634">Nedávno navštívené</translation>
+<translation id="5988097621740394599">Zobrazte si na kartách poklesy cien.</translation>
 <translation id="5988851877894965432">Otvoriť webové adresy v Chrome</translation>
 <translation id="6012140227487808125">Prebieha šifrovanie…</translation>
 <translation id="6021332621416007159">Otvoriť v...</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_sw.xtb b/ios/chrome/app/strings/resources/ios_strings_sw.xtb
index 2ca7b9f..ffd696c 100644
--- a/ios/chrome/app/strings/resources/ios_strings_sw.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_sw.xtb
@@ -345,6 +345,7 @@
 <translation id="4272631900155121838">Ili kuchanganua msimbo wa QR, washa kamera kwenye mipangilio</translation>
 <translation id="4277990410970811858">Kuvinjari Salama</translation>
 <translation id="4281844954008187215">Sheria na Masharti</translation>
+<translation id="4304713468139749426">Kidhibiti cha Manenosiri</translation>
 <translation id="430793432425771671">Sawazisha Kila kitu</translation>
 <translation id="4309403553630140242">Gusa mara mbili ili upate maelezo zaidi</translation>
 <translation id="430967081421617822">Muda Wote</translation>
@@ -356,6 +357,7 @@
 <translation id="4389019817280890563">Gusa ili ubadilishe lugha.</translation>
 <translation id="4442550905108052454">Fungua <ph name="BEGIN_BOLD" />Mipangilio<ph name="END_BOLD" /></translation>
 <translation id="4454246407045105932">Ongeza Lugha</translation>
+<translation id="4462491365653392320">Fuatilia Bei kwenye Vichupo</translation>
 <translation id="4469418912670346607">Endelea Kuweka Mipangilio</translation>
 <translation id="4474494258097106883">Imeundwa kwa ajili ya iPhone Yako</translation>
 <translation id="4476574785019001431">Mipangilio</translation>
@@ -419,6 +421,7 @@
 <translation id="4944543191714094452">Pata katika Ukurasa…</translation>
 <translation id="4945756290001680296">Fikia mipangilio ya nenosiri</translation>
 <translation id="4979397965658815378">Ingia katika akaunti kwa kutumia Akaunti yako ya Google ili upate alamisho, manenosiri, historia na mipangilio mingine kwenye vifaa vyako vyote.</translation>
+<translation id="4986678885919050584">Ondoa kwenye Kifaa hiki</translation>
 <translation id="5005498671520578047">Nakili nenosiri</translation>
 <translation id="5017828934289857214">Nikumbushe Baadaye</translation>
 <translation id="5037676449506322593">Chagua Zote</translation>
@@ -520,6 +523,7 @@
 <translation id="5979837087407522202">Tafuta Manenosiri</translation>
 <translation id="5982717868370722439">Ongeza data iliyopo kwenye <ph name="USER_EMAIL" />.</translation>
 <translation id="5984222099446776634">Vilivyotembelewa Hivi karibuni</translation>
+<translation id="5988097621740394599">Angalia puzungo la bei kwenye vichupo vyako.</translation>
 <translation id="5988851877894965432">Fungua URL katika Chrome</translation>
 <translation id="6012140227487808125">Inasimba kwa njia fiche...</translation>
 <translation id="6021332621416007159">Fungulia katika...</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_uz.xtb b/ios/chrome/app/strings/resources/ios_strings_uz.xtb
index 97fe8b79..7cb2d0d 100644
--- a/ios/chrome/app/strings/resources/ios_strings_uz.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_uz.xtb
@@ -235,6 +235,7 @@
 <translation id="3157387275655328056">Mutolaa ro‘yxatiga qo‘shish</translation>
 <translation id="3157684681743766797">Hammasini belgilash…</translation>
 <translation id="3169472444629675720">Discover</translation>
+<translation id="3174662312949010067">Boshqa qurilmalarda saqlangan parollardan ham foydalanish mumkin.</translation>
 <translation id="3175081911749765310">Veb-xizmatlar</translation>
 <translation id="3178650076442119961">Bugun onlayn edi</translation>
 <translation id="3181825792072797598">Sinxronizatsiyani yoqish</translation>
@@ -435,6 +436,7 @@
 <translation id="5118764316110575523">Yoqilmagan</translation>
 <translation id="5127805178023152808">Sinxronizatsiya o‘chiq</translation>
 <translation id="5132942445612118989">Parollar, tarix va boshqa sozlamalaringizni barcha qurilmalaringizda sinxronlang</translation>
+<translation id="51339916068413997">iPaddagi boshqa ilovalarda saqlangan parollarni ham avtomatik kiritish mumkin.</translation>
 <translation id="5140288047769711648">Chrome bu parolni eslab qoladi. Yodda tutishingiz shart emas.</translation>
 <translation id="5150492518600715772">Qurilmangizga yuborish</translation>
 <translation id="5168414296986405587">iPadOS uchun ishlab chiqilgan</translation>
@@ -473,6 +475,7 @@
 Handoff funksiyasini faqatgina Chrome sozlamalarida emas, balki qurilma sozlamalarida (“Umumiy” bo‘limida) ham yoqish kerak. Barcha qurilmalaringiz umumiy iCloud hisobida sinxronlanishi zarur.</translation>
 <translation id="5551897871312988470">Tarjima qilishni taklif qilish</translation>
 <translation id="5556459405103347317">Qayta yuklash</translation>
+<translation id="5557664881350188277">iPhonedagi boshqa ilovalarda saqlangan parollarni ham avtomatik kiritish mumkin.</translation>
 <translation id="5580834567471114021">Keyinroq oʻqish uchun mutolaa roʻyxatiga kiritilsinmi?</translation>
 <translation id="5592679540098330836"><ph name="NAME" /> hisobingizni sinxronlang</translation>
 <translation id="560322036295180549">Tashkilotingiz tomonidan faolsizlantirilgan</translation>
@@ -767,6 +770,7 @@
 <translation id="8076014560081431679">Saytlarda saqlangan ma’lumotlar tozalanmaydi va brauzer faoliyatiga ta’sir qilmaydi. <ph name="BEGIN_LINK" />Batafsil<ph name="END_LINK" /></translation>
 <translation id="8079602123447022758">Bu sozlama boshqariladi. Batafsil axborot olish uchun bosing</translation>
 <translation id="8080028325999236607">Barcha tablarni yopish</translation>
+<translation id="8101409298456377967">Sayt va ilovalarga osongina kirish uchun parollaringizni yarating, saqlang va boshqaring. <ph name="BEGIN_LINK" />Batafsil<ph name="END_LINK" /></translation>
 <translation id="8105368624971345109">O‘chirib qo‘yish</translation>
 <translation id="8114753159095730575">Faylni yuklab olish mumkin. Qo‘shimcha amallar ekranning pastki qismida.</translation>
 <translation id="8132598642024322408">Joriy narxi: <ph name="PRICE" />, eski narxi: <ph name="PREVIOUS_PRICE" />.</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_vi.xtb b/ios/chrome/app/strings/resources/ios_strings_vi.xtb
index bda2090..fa1825d 100644
--- a/ios/chrome/app/strings/resources/ios_strings_vi.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_vi.xtb
@@ -356,6 +356,7 @@
 <translation id="4389019817280890563">Nhấn để thay đổi ngôn ngữ.</translation>
 <translation id="4442550905108052454">Mở phần <ph name="BEGIN_BOLD" />Cài đặt<ph name="END_BOLD" /></translation>
 <translation id="4454246407045105932">Thêm ngôn ngữ</translation>
+<translation id="4462491365653392320">Theo dõi giá trên thẻ</translation>
 <translation id="4469418912670346607">Tiếp tục thiết lập</translation>
 <translation id="4474494258097106883">Dành riêng cho iPhone</translation>
 <translation id="4476574785019001431">Cài đặt</translation>
@@ -419,6 +420,7 @@
 <translation id="4944543191714094452">Tìm trong trang…</translation>
 <translation id="4945756290001680296">Truy cập vào phần cài đặt mật khẩu</translation>
 <translation id="4979397965658815378">Đăng nhập bằng Tài khoản Google để nhận dấu trang, mật khẩu, lịch sử và các cài đặt khác trên tất cả thiết bị của bạn</translation>
+<translation id="4986678885919050584">Xoá khỏi thiết bị này</translation>
 <translation id="5005498671520578047">Sao chép mật khẩu</translation>
 <translation id="5017828934289857214">Nhắc tôi sau</translation>
 <translation id="5037676449506322593">Chọn Tất cả</translation>
@@ -520,6 +522,7 @@
 <translation id="5979837087407522202">Tìm kiếm mật khẩu</translation>
 <translation id="5982717868370722439">Thêm dữ liệu hiện có vào <ph name="USER_EMAIL" />.</translation>
 <translation id="5984222099446776634">Truy cập gần đây</translation>
+<translation id="5988097621740394599">Xem các mặt hàng giảm giá trong thẻ của bạn.</translation>
 <translation id="5988851877894965432">Mở URL trong Chrome</translation>
 <translation id="6012140227487808125">Đang mã hóa...</translation>
 <translation id="6021332621416007159">Mở trong...</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_zu.xtb b/ios/chrome/app/strings/resources/ios_strings_zu.xtb
index 008a93bf..39671b6 100644
--- a/ios/chrome/app/strings/resources/ios_strings_zu.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_zu.xtb
@@ -356,6 +356,7 @@
 <translation id="4389019817280890563">Thepha ukuze ushintshe ulimi.</translation>
 <translation id="4442550905108052454">Vula <ph name="BEGIN_BOLD" />Amasethingi<ph name="END_BOLD" /></translation>
 <translation id="4454246407045105932">Engeza ulimi</translation>
+<translation id="4462491365653392320">Landelela Izintengo Kumathebhu</translation>
 <translation id="4469418912670346607">Qhubeka nokusetha</translation>
 <translation id="4474494258097106883">Yakhelwe i-iPhone yakho</translation>
 <translation id="4476574785019001431">Izilungiselelo</translation>
@@ -419,6 +420,7 @@
 <translation id="4944543191714094452">Thola kukhasi…</translation>
 <translation id="4945756290001680296">Finyelela kuzilungiselelo zephasiwedi</translation>
 <translation id="4979397965658815378">Ngena ngemvume nge-akhawunti yakho ye-Google ukuze uthole amabhukhimakhi, amaphasiwedi, umlando nezinye izilungiselelo kuwo wonke amadivayisi akho</translation>
+<translation id="4986678885919050584">Susa kule Divayisi</translation>
 <translation id="5005498671520578047">Kopisha iphasiwedi</translation>
 <translation id="5017828934289857214">Ngikhumbuze ngemuva kwesikhathi</translation>
 <translation id="5037676449506322593">Khetha konke</translation>
@@ -520,6 +522,7 @@
 <translation id="5979837087407522202">Sesha amaphasiwedi</translation>
 <translation id="5982717868370722439">Engeza idatha ekhona ku-<ph name="USER_EMAIL" />.</translation>
 <translation id="5984222099446776634">Ivakashelwe kamuva</translation>
+<translation id="5988097621740394599">Bona ukwehla kwentengo kumathebhu wakho.</translation>
 <translation id="5988851877894965432">Vula ama-URL ku-Chrome</translation>
 <translation id="6012140227487808125">Iyabethela...</translation>
 <translation id="6021332621416007159">Ivula ku...</translation>
diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn
index 9a0669d7..d878096 100644
--- a/ios/chrome/browser/BUILD.gn
+++ b/ios/chrome/browser/BUILD.gn
@@ -208,6 +208,7 @@
     "//components/prefs",
     "//components/safe_browsing/core/common",
     "//components/sessions",
+    "//components/signin/public/identity_manager",
     "//components/translate/core/browser",
     "//components/ukm",
     "//components/update_client",
diff --git a/ios/chrome/browser/autofill/manual_fill/OWNERS b/ios/chrome/browser/autofill/manual_fill/OWNERS
index f29d2615..cb25906 100644
--- a/ios/chrome/browser/autofill/manual_fill/OWNERS
+++ b/ios/chrome/browser/autofill/manual_fill/OWNERS
@@ -1,2 +1 @@
-javierrobles@chromium.org
 djean@chromium.org
diff --git a/ios/chrome/browser/browser_state/browser_state_info_cache.cc b/ios/chrome/browser/browser_state/browser_state_info_cache.cc
index 5e4719da..8c70ee0 100644
--- a/ios/chrome/browser/browser_state/browser_state_info_cache.cc
+++ b/ios/chrome/browser/browser_state/browser_state_info_cache.cc
@@ -178,8 +178,8 @@
 const base::DictionaryValue*
 BrowserStateInfoCache::GetInfoForBrowserStateAtIndex(size_t index) const {
   DCHECK_LT(index, GetNumberOfBrowserStates());
-  const base::DictionaryValue* cache =
-      prefs_->GetDictionary(prefs::kBrowserStateInfoCache);
+  const base::DictionaryValue* cache = &base::Value::AsDictionaryValue(
+      *prefs_->GetDictionary(prefs::kBrowserStateInfoCache));
   const base::DictionaryValue* info = nullptr;
   cache->GetDictionaryWithoutPathExpansion(sorted_keys_[index], &info);
   return info;
diff --git a/ios/chrome/browser/credential_provider/OWNERS b/ios/chrome/browser/credential_provider/OWNERS
index f29d2615..cb25906 100644
--- a/ios/chrome/browser/credential_provider/OWNERS
+++ b/ios/chrome/browser/credential_provider/OWNERS
@@ -1,2 +1 @@
-javierrobles@chromium.org
 djean@chromium.org
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 9ec0a4f..65c48003d 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -765,6 +765,10 @@
     {"default-mode-ua", flag_descriptions::kAddSettingForDefaultPageModeName,
      flag_descriptions::kAddSettingForDefaultPageModeDescription,
      flags_ui::kOsIos, FEATURE_VALUE_TYPE(kAddSettingForDefaultPageMode)},
+    {"ios-media-permissions-control",
+     flag_descriptions::kMediaPermissionsControlName,
+     flag_descriptions::kMediaPermissionsControlDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(web::features::kMediaPermissionsControl)},
 };
 
 bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index eccd505..1efdcfb 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -323,6 +323,12 @@
 const char kLogBreadcrumbsDescription[] =
     "When enabled, breadcrumb events will be logged.";
 
+const char kMediaPermissionsControlName[] =
+    "Camera and Microphone Access Permissions Control";
+const char kMediaPermissionsControlDescription[] =
+    "Enables user control for camera and/or microphone access for a specific "
+    "site through site settings during its lifespan.";
+
 const char kMetrickitCrashReportName[] = "Metrickit crash reports";
 const char kMetrickitCrashReportDescription[] =
     "Enables sending Metrickit crash reports";
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 6f745ed..fbb7af30 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -294,6 +294,11 @@
 extern const char kLogBreadcrumbsName[];
 extern const char kLogBreadcrumbsDescription[];
 
+// Title and description for the flag to control camera and/or microphone access
+// for a specific site through site settings during its lifespan.
+extern const char kMediaPermissionsControlName[];
+extern const char kMediaPermissionsControlDescription[];
+
 // Title and description for the flag that controls sending metrickit crash
 // reports.
 extern const char kMetrickitCrashReportName[];
diff --git a/ios/chrome/browser/ios_chrome_main_parts.mm b/ios/chrome/browser/ios_chrome_main_parts.mm
index fd2690e2..d3dec17 100644
--- a/ios/chrome/browser/ios_chrome_main_parts.mm
+++ b/ios/chrome/browser/ios_chrome_main_parts.mm
@@ -36,6 +36,7 @@
 #include "components/open_from_clipboard/clipboard_recent_content.h"
 #include "components/prefs/json_pref_store.h"
 #include "components/prefs/pref_service.h"
+#import "components/signin/public/identity_manager/tribool.h"
 #include "components/translate/core/browser/translate_download_manager.h"
 #include "components/variations/field_trial_config/field_trial_util.h"
 #include "components/variations/service/variations_service.h"
@@ -184,7 +185,17 @@
 
   // Compute device restore flag before IO is disallowed on UI thread, so the
   // value is available from cache synchronously.
-  IsFirstSessionAfterDeviceRestore();
+  static crash_reporter::CrashKeyString<8> device_restore_key("device-restore");
+  switch (IsFirstSessionAfterDeviceRestore()) {
+    case signin::Tribool::kTrue:
+      device_restore_key.Set("yes");
+      break;
+    case signin::Tribool::kFalse:
+      break;
+    case signin::Tribool::kUnknown:
+      device_restore_key.Set("unknown");
+      break;
+  }
 
   // Convert freeform experimental settings into switches before initializing
   // local state, in case any of the settings affect policy.
diff --git a/ios/chrome/browser/notification_promo.cc b/ios/chrome/browser/notification_promo.cc
index c03a485..2bb1962 100644
--- a/ios/chrome/browser/notification_promo.cc
+++ b/ios/chrome/browser/notification_promo.cc
@@ -167,8 +167,8 @@
   if (promo_id_ == -1)
     return;
 
-  const base::DictionaryValue* promo_dict =
-      local_state_->GetDictionary(kPrefPromoObject);
+  const base::DictionaryValue* promo_dict = &base::Value::AsDictionaryValue(
+      *local_state_->GetDictionary(kPrefPromoObject));
   if (!promo_dict)
     return;
 
diff --git a/ios/chrome/browser/passwords/BUILD.gn b/ios/chrome/browser/passwords/BUILD.gn
index 5cfce48..76db31a 100644
--- a/ios/chrome/browser/passwords/BUILD.gn
+++ b/ios/chrome/browser/passwords/BUILD.gn
@@ -160,9 +160,11 @@
     "//base",
     "//components/infobars/core",
     "//components/password_manager/core/browser",
+    "//components/password_manager/core/common",
     "//components/strings",
     "//ios/chrome/app/strings",
     "//ios/chrome/app/theme",
+    "//ios/chrome/browser/signin",
     "//ui/base",
   ]
 }
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h b/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h
index 24f59a5..d353c74a 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h
+++ b/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h
@@ -9,6 +9,7 @@
 
 #include "components/infobars/core/confirm_infobar_delegate.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "ios/chrome/browser/signin/authentication_service.h"
 
 @protocol ApplicationCommands;
 
@@ -45,6 +46,7 @@
 
  protected:
   IOSChromePasswordManagerInfoBarDelegate(
+      NSString* user_email,
       bool is_sync_user,
       std::unique_ptr<password_manager::PasswordFormManagerForUI> form_manager);
 
@@ -54,6 +56,8 @@
 
   bool is_sync_user() const { return is_sync_user_; }
 
+  NSString* user_email() { return user_email_; }
+
   void set_infobar_response(
       password_manager::metrics_util::UIDismissalReason response) {
     infobar_response_ = response;
@@ -77,6 +81,8 @@
   // Whether to show the additional footer.
   const bool is_sync_user_;
 
+  NSString* user_email_;
+
   // Handler for calling Application commands.
   __weak id<ApplicationCommands> handler_ = nil;
 };
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.mm b/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.mm
index c08081b..a006b57 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.mm
+++ b/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.mm
@@ -10,6 +10,8 @@
 #include "base/strings/sys_string_conversions.h"
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/password_form_manager_for_ui.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+#include "ios/chrome/grit/ios_google_chrome_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/chrome/grit/ios_theme_resources.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -24,16 +26,29 @@
 
 IOSChromePasswordManagerInfoBarDelegate::
     IOSChromePasswordManagerInfoBarDelegate(
+        NSString* user_email,
         bool is_sync_user,
         std::unique_ptr<password_manager::PasswordFormManagerForUI>
             form_to_save)
     : form_to_save_(std::move(form_to_save)),
       infobar_response_(password_manager::metrics_util::NO_DIRECT_INTERACTION),
-      is_sync_user_(is_sync_user) {}
+      is_sync_user_(is_sync_user),
+      user_email_(user_email) {}
 
 NSString* IOSChromePasswordManagerInfoBarDelegate::GetDetailsMessageText()
     const {
-  return is_sync_user_ ? l10n_util::GetNSString(IDS_SAVE_PASSWORD_FOOTER) : @"";
+  if (!base::FeatureList::IsEnabled(
+          password_manager::features::
+              kIOSEnablePasswordManagerBrandingUpdate)) {
+    return is_sync_user_ ? l10n_util::GetNSString(IDS_SAVE_PASSWORD_FOOTER)
+                         : @"";
+  }
+
+  return is_sync_user_
+             ? l10n_util::GetNSStringF(
+                   IDS_SAVE_PASSWORD_FOOTER_DISPLAYING_USER_EMAIL,
+                   base::SysNSStringToUTF16(user_email_))
+             : l10n_util::GetNSString(IDS_IOS_SAVE_PASSWORD_FOOTER_NOT_SYNCING);
 }
 
 NSString* IOSChromePasswordManagerInfoBarDelegate::GetUserNameText() const {
diff --git a/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h b/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h
index b0d178b..fad90e0 100644
--- a/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h
+++ b/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h
@@ -26,6 +26,7 @@
     : public IOSChromePasswordManagerInfoBarDelegate {
  public:
   IOSChromeSavePasswordInfoBarDelegate(
+      NSString* user_email,
       bool is_sync_user,
       bool password_update,
       std::unique_ptr<password_manager::PasswordFormManagerForUI> form_to_save);
diff --git a/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.mm b/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.mm
index 5cda1fb5..692c38d31 100644
--- a/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.mm
+++ b/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.mm
@@ -129,16 +129,17 @@
 }
 
 IOSChromeSavePasswordInfoBarDelegate::IOSChromeSavePasswordInfoBarDelegate(
+    NSString* user_email,
     bool is_sync_user,
     bool password_update,
     std::unique_ptr<PasswordFormManagerForUI> form_manager)
-    : IOSChromePasswordManagerInfoBarDelegate(is_sync_user,
+    : IOSChromePasswordManagerInfoBarDelegate(user_email,
+                                              is_sync_user,
                                               std::move(form_manager)),
       password_update_(password_update),
       infobar_type_(password_update
                         ? PasswordInfobarType::kPasswordInfobarTypeUpdate
-                        : PasswordInfobarType::kPasswordInfobarTypeSave) {
-}
+                        : PasswordInfobarType::kPasswordInfobarTypeSave) {}
 
 IOSChromeSavePasswordInfoBarDelegate::~IOSChromeSavePasswordInfoBarDelegate() {
     // If by any reason this delegate gets dealloc before the Infobar is
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index b3f9392..f5dbb64 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -40,6 +40,7 @@
 #include "components/password_manager/core/browser/password_manager.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
 #include "components/password_manager/core/browser/password_manager_driver.h"
+#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/password_manager/ios/account_select_fill_data.h"
 #import "components/password_manager/ios/password_form_helper.h"
 #import "components/password_manager/ios/password_suggestion_helper.h"
@@ -55,12 +56,15 @@
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h"
 #import "ios/chrome/browser/passwords/notify_auto_signin_view_controller.h"
+#include "ios/chrome/browser/signin/authentication_service.h"
+#include "ios/chrome/browser/signin/authentication_service_factory.h"
 #include "ios/chrome/browser/sync/sync_service_factory.h"
 #import "ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator.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/password_breach_commands.h"
 #import "ios/chrome/browser/ui/commands/password_protection_commands.h"
+#include "ios/chrome/grit/ios_google_chrome_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/web/common/url_scheme_util.h"
 #include "ios/web/public/js_messaging/web_frame.h"
@@ -106,6 +110,11 @@
 
 // Duration for notify user auto-sign in dialog being displayed.
 constexpr int kNotifyAutoSigninDuration = 3;  // seconds
+// Helper to check if password manager rebranding finch flag is enabled.
+BOOL isPasswordManagerBrandingUpdateEnabled() {
+  return base::FeatureList::IsEnabled(
+      password_manager::features::kIOSEnablePasswordManagerBrandingUpdate);
+}
 }  // namespace
 
 @interface PasswordController () <SharedPasswordControllerDelegate>
@@ -355,6 +364,18 @@
 
 #pragma mark - Private methods
 
+// Returns the user email.
+- (NSString*)userEmail {
+  DCHECK(self.browserState);
+
+  AuthenticationService* authService =
+      AuthenticationServiceFactory::GetForBrowserState(self.browserState);
+  ChromeIdentity* authenticatedIdentity =
+      authService->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
+
+  return [authenticatedIdentity userEmail];
+}
+
 // The dispatcher used for ApplicationCommands.
 - (id<ApplicationCommands>)applicationCommandsHandler {
   DCHECK(self.dispatcher);
@@ -426,10 +447,16 @@
   infobars::InfoBarManager* infoBarManager =
       InfoBarManagerImpl::FromWebState(_webState);
 
+  AuthenticationService* authService =
+      AuthenticationServiceFactory::GetForBrowserState(self.browserState);
+  ChromeIdentity* authenticatedIdentity =
+      authService->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
+
   switch (type) {
     case PasswordInfoBarType::SAVE: {
       auto delegate = std::make_unique<IOSChromeSavePasswordInfoBarDelegate>(
-          isSyncUser, /*password_update*/ false, std::move(form));
+          [authenticatedIdentity userEmail], isSyncUser,
+          /*password_update*/ false, std::move(form));
       delegate->set_handler(self.applicationCommandsHandler);
 
         // Count only new infobar showings, not replacements.
@@ -457,7 +484,8 @@
         }
 
         auto delegate = std::make_unique<IOSChromeSavePasswordInfoBarDelegate>(
-            isSyncUser, /*password_update*/ true, std::move(form));
+            [authenticatedIdentity userEmail], isSyncUser,
+            /*password_update*/ true, std::move(form));
         delegate->set_handler(self.applicationCommandsHandler);
         // If manual save, skip showing banner.
         std::unique_ptr<InfoBarIOS> infobar = std::make_unique<InfoBarIOS>(
@@ -485,9 +513,24 @@
 }
 
 - (void)updateGeneratePasswordStrings:(id)sender {
-  NSString* title = [NSString
-      stringWithFormat:@"%@\n%@\n ", GetNSString(IDS_IOS_SUGGESTED_PASSWORD),
-                       self.generatedPotentialPassword];
+  NSString* title;
+  NSString* message;
+
+  if (isPasswordManagerBrandingUpdateEnabled()) {
+    title = [NSString
+        stringWithFormat:@"%@\n%@\n ",
+                         GetNSString(IDS_IOS_SUGGESTED_STRONG_PASSWORD),
+                         self.generatedPotentialPassword];
+    message = l10n_util::GetNSStringF(
+        IDS_IOS_SUGGESTED_STRONG_PASSWORD_HINT_DISPLAYING_EMAIL,
+        base::SysNSStringToUTF16([self userEmail]));
+  } else {
+    title = [NSString stringWithFormat:@"%@\n%@\n ",
+                                       GetNSString(IDS_IOS_SUGGESTED_PASSWORD),
+                                       self.generatedPotentialPassword];
+    message = GetNSString(IDS_IOS_SUGGESTED_PASSWORD_HINT);
+  }
+
   self.actionSheetCoordinator.attributedTitle =
       [[NSMutableAttributedString alloc]
           initWithString:title
@@ -496,7 +539,6 @@
                     [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]
               }];
 
-  NSString* message = GetNSString(IDS_IOS_SUGGESTED_PASSWORD_HINT);
   self.actionSheetCoordinator.attributedMessage =
       [[NSMutableAttributedString alloc]
           initWithString:message
@@ -558,14 +600,20 @@
     [handler closeKeyboardWithoutButtonPress];
   };
 
-  [self.actionSheetCoordinator
-      addItemWithTitle:GetNSString(IDS_IOS_USE_SUGGESTED_PASSWORD)
-                action:^{
-                  decisionHandler(YES);
-                  popupDismissed();
-                  closeKeyboard();
-                }
-                 style:UIAlertActionStyleDefault];
+  NSString* primaryActionString;
+  if (isPasswordManagerBrandingUpdateEnabled()) {
+    primaryActionString = GetNSString(IDS_IOS_USE_SUGGESTED_STRONG_PASSWORD);
+  } else {
+    primaryActionString = GetNSString(IDS_IOS_USE_SUGGESTED_PASSWORD);
+  }
+
+  [self.actionSheetCoordinator addItemWithTitle:primaryActionString
+                                         action:^{
+                                           decisionHandler(YES);
+                                           popupDismissed();
+                                           closeKeyboard();
+                                         }
+                                          style:UIAlertActionStyleDefault];
 
   [self.actionSheetCoordinator addItemWithTitle:GetNSString(IDS_CANCEL)
                                          action:^{
diff --git a/ios/chrome/browser/passwords/password_tab_helper.h b/ios/chrome/browser/passwords/password_tab_helper.h
index f1c7231..0a165c6 100644
--- a/ios/chrome/browser/passwords/password_tab_helper.h
+++ b/ios/chrome/browser/passwords/password_tab_helper.h
@@ -20,6 +20,7 @@
 class PasswordGenerationFrameHelper;
 class PasswordManager;
 class PasswordManagerClient;
+class PasswordManagerDriver;
 }
 
 // Class binding a PasswordController to a WebState.
@@ -56,6 +57,9 @@
   // Returns the PasswordManagerClient owned by the PasswordController.
   password_manager::PasswordManagerClient* GetPasswordManagerClient();
 
+  // Returns the PasswordManagerDriver owned by the PasswordController.
+  password_manager::PasswordManagerDriver* GetPasswordManagerDriver();
+
   // Returns an object that can provide password generation from the
   // PasswordController. May return nil.
   id<PasswordGenerationProvider> GetPasswordGenerationProvider();
diff --git a/ios/chrome/browser/passwords/password_tab_helper.mm b/ios/chrome/browser/passwords/password_tab_helper.mm
index af01b245..964d1c5 100644
--- a/ios/chrome/browser/passwords/password_tab_helper.mm
+++ b/ios/chrome/browser/passwords/password_tab_helper.mm
@@ -55,6 +55,11 @@
   return controller_.passwordManagerClient;
 }
 
+password_manager::PasswordManagerDriver*
+PasswordTabHelper::GetPasswordManagerDriver() {
+  return controller_.passwordManagerDriver;
+}
+
 id<PasswordGenerationProvider>
 PasswordTabHelper::GetPasswordGenerationProvider() {
   return controller_.generationProvider;
diff --git a/ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.mm b/ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.mm
index ea86e35..0838832 100644
--- a/ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.mm
+++ b/ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.mm
@@ -49,6 +49,7 @@
         std::unique_ptr<password_manager::PasswordForm> form,
         std::unique_ptr<GURL> url)
     : IOSChromeSavePasswordInfoBarDelegate(
+          /*user_email=*/@"foobar@gmail.com",
           /*is_sync_user=*/false,
           /*password_update=*/false,
           CreateFormManager(form.get(), url.get())),
diff --git a/ios/chrome/browser/signin/chrome_account_manager_service.mm b/ios/chrome/browser/signin/chrome_account_manager_service.mm
index 9a218787..eb8c75a 100644
--- a/ios/chrome/browser/signin/chrome_account_manager_service.mm
+++ b/ios/chrome/browser/signin/chrome_account_manager_service.mm
@@ -153,8 +153,9 @@
 // Returns the PatternAccountRestriction according to the given PrefService.
 PatternAccountRestriction PatternAccountRestrictionFromPreference(
     PrefService* pref_service) {
-  auto maybe_restriction = PatternAccountRestrictionFromValue(
-      pref_service->GetList(prefs::kRestrictAccountsToPatterns));
+  auto maybe_restriction =
+      PatternAccountRestrictionFromValue(&base::Value::AsListValue(
+          *pref_service->GetList(prefs::kRestrictAccountsToPatterns)));
   return *std::move(maybe_restriction);
 }
 
diff --git a/ios/chrome/browser/ui/authentication/enterprise/enterprise_utils.mm b/ios/chrome/browser/ui/authentication/enterprise/enterprise_utils.mm
index c3b0fa4e..ccbf324 100644
--- a/ios/chrome/browser/ui/authentication/enterprise/enterprise_utils.mm
+++ b/ios/chrome/browser/ui/authentication/enterprise/enterprise_utils.mm
@@ -37,9 +37,8 @@
 }  // namespace
 
 bool IsRestrictAccountsToPatternsEnabled() {
-  const base::ListValue* value =
-      GetApplicationContext()->GetLocalState()->GetList(
-          prefs::kRestrictAccountsToPatterns);
+  const base::Value* value = GetApplicationContext()->GetLocalState()->GetList(
+      prefs::kRestrictAccountsToPatterns);
   return !value->GetList().empty();
 }
 
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
index 0007e230..9ed83a53 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
@@ -383,12 +383,12 @@
 void ChromeAutofillClientIOS::PropagateAutofillPredictions(
     content::RenderFrameHost* rfh,
     const std::vector<FormStructure*>& forms) {
-  password_manager_->ProcessAutofillPredictions(/*driver=*/nullptr, forms);
-  auto* generationHelper =
-      PasswordTabHelper::FromWebState(web_state_)->GetGenerationHelper();
-  if (generationHelper) {
-    generationHelper->ProcessPasswordRequirements(forms);
+  if (!PasswordTabHelper::FromWebState(web_state_)) {
+    return;
   }
+  password_manager_->ProcessAutofillPredictions(
+      PasswordTabHelper::FromWebState(web_state_)->GetPasswordManagerDriver(),
+      forms);
 }
 
 void ChromeAutofillClientIOS::DidFillOrPreviewField(
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory/OWNERS b/ios/chrome/browser/ui/autofill/form_input_accessory/OWNERS
index 632de029..3ebff38d 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory/OWNERS
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory/OWNERS
@@ -1,2 +1 @@
-javierrobles@chromium.org
 olivierrobin@chromium.org
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/OWNERS b/ios/chrome/browser/ui/autofill/manual_fill/OWNERS
index f29d2615..cb25906 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/OWNERS
+++ b/ios/chrome/browser/ui/autofill/manual_fill/OWNERS
@@ -1,2 +1 @@
-javierrobles@chromium.org
 djean@chromium.org
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
index 0a78245..3fb6dec 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -843,13 +843,8 @@
 #pragma mark - New Tab menu tests
 
 // Tests the "new search" menu item from the new tab menu.
-// TODO(crbug.com/1280323): Fails on iOS device.
-#if TARGET_IPHONE_SIMULATOR
-#define MAYBE_testNewSearchFromNewTabMenu testNewSearchFromNewTabMenu
-#else
-#define MAYBE_testNewSearchFromNewTabMenu DISABLED_testNewSearchFromNewTabMenu
-#endif
-- (void)MAYBE_testNewSearchFromNewTabMenu {
+// TODO(crbug.com/1280323): Fails on iOS device and ios15-beta-simulator.
+- (void)DISABLED_testNewSearchFromNewTabMenu {
   if ([ChromeEarlGrey isIPadIdiom]) {
     EARL_GREY_TEST_SKIPPED(@"New Search is only available in phone layout.");
   }
diff --git a/ios/chrome/browser/ui/default_promo/OWNERS b/ios/chrome/browser/ui/default_promo/OWNERS
index c2536df3..04c64383 100644
--- a/ios/chrome/browser/ui/default_promo/OWNERS
+++ b/ios/chrome/browser/ui/default_promo/OWNERS
@@ -1,2 +1 @@
-javierrobles@chromium.org
 rkgibson@google.com
diff --git a/ios/chrome/browser/ui/infobars/test/test_infobar_password_delegate.mm b/ios/chrome/browser/ui/infobars/test/test_infobar_password_delegate.mm
index c633e13..12cad0f 100644
--- a/ios/chrome/browser/ui/infobars/test/test_infobar_password_delegate.mm
+++ b/ios/chrome/browser/ui/infobars/test/test_infobar_password_delegate.mm
@@ -83,7 +83,10 @@
 
 TestInfobarPasswordDelegate::TestInfobarPasswordDelegate(
     NSString* infobar_message)
-    : IOSChromeSavePasswordInfoBarDelegate(false, false, CreateFormManager()),
+    : IOSChromeSavePasswordInfoBarDelegate(@"foobar@gmail.com",
+                                           false,
+                                           false,
+                                           CreateFormManager()),
       infobar_message_(infobar_message) {}
 
 bool TestInfobarPasswordDelegate::Create(
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn
index d6af25d..0a93ce8 100644
--- a/ios/chrome/browser/ui/ntp/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -189,6 +189,7 @@
     "//ios/chrome/common/ui/util",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/branded_images:branded_images_api",
+    "//ios/public/provider/chrome/browser/discover_feed",
     "//ios/third_party/material_components_ios",
     "//ios/web",
     "//net",
diff --git a/ios/chrome/browser/ui/ntp/discover_feed_wrapper_view_controller.mm b/ios/chrome/browser/ui/ntp/discover_feed_wrapper_view_controller.mm
index c533a45..6822df9 100644
--- a/ios/chrome/browser/ui/ntp/discover_feed_wrapper_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/discover_feed_wrapper_view_controller.mm
@@ -8,6 +8,8 @@
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/ntp/discover_feed_wrapper_view_controller.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
+#import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#import "ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -44,6 +46,7 @@
   // |discoverFeed| exists, then the feed must be enabled and visible.
   if (self.discoverFeed && self.contentCollectionView) {
     [self configureDiscoverFeedAsWrapper];
+    ios::GetChromeBrowserProvider().GetDiscoverFeedProvider()->UpdateTheme();
   } else {
     [self configureEmptyCollectionAsWrapper];
   }
diff --git a/ios/chrome/browser/ui/passwords/OWNERS b/ios/chrome/browser/ui/passwords/OWNERS
index b6c914d..8ee4bd2 100644
--- a/ios/chrome/browser/ui/passwords/OWNERS
+++ b/ios/chrome/browser/ui/passwords/OWNERS
@@ -1,2 +1 @@
-javierrobles@chromium.org
 vsemeniuk@google.com
diff --git a/ios/chrome/browser/ui/scanner/OWNERS b/ios/chrome/browser/ui/scanner/OWNERS
index 8e66c847..2d35f0a 100644
--- a/ios/chrome/browser/ui/scanner/OWNERS
+++ b/ios/chrome/browser/ui/scanner/OWNERS
@@ -1,2 +1 @@
 gambard@chromium.org
-javierrobles@chromium.org
diff --git a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/BUILD.gn b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/BUILD.gn
index a962738..c58e934 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/BUILD.gn
@@ -13,8 +13,6 @@
   deps = [
     ":passwords_in_other_apps_ui",
     "//base",
-    "//ios/chrome/browser/main:public",
-    "//ios/chrome/browser/sync",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/settings/utils",
     "//ios/public/provider/chrome/browser/password_auto_fill:password_auto_fill_api",
diff --git a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/constants.h b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/constants.h
index 0340d94..19dd98a 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/constants.h
+++ b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/constants.h
@@ -16,9 +16,6 @@
 // A11y Identifier for subtitle label.
 extern NSString* const kPasswordsInOtherAppsSubtitleAccessibilityIdentifier;
 
-// A11y Identifier for second subtitle label.
-extern NSString* const kPasswordsInOtherAppsSecondSubtitleAccessibilityIdentifier;
-
 // A11y Identifier for banner image.
 extern NSString* const kPasswordsInOtherAppsImageAccessibilityIdentifier;
 
diff --git a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/constants.mm b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/constants.mm
index 8387d348..059792e 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/constants.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/constants.mm
@@ -15,8 +15,6 @@
     @"kPasswordsInOtherAppsTitleAccessibilityIdentifier";
 NSString* const kPasswordsInOtherAppsSubtitleAccessibilityIdentifier =
     @"kPasswordsInOtherAppsSubtitleAccessibilityIdentifier";
-NSString* const kPasswordsInOtherAppsSecondSubtitleAccessibilityIdentifier =
-    @"kPasswordsInOtherAppsSecondSubtitleAccessibilityIdentifier";
 NSString* const kPasswordsInOtherAppsImageAccessibilityIdentifier =
     @"kPasswordsInOtherAppsImageAccessibilityIdentifier";
 NSString* const kPasswordsInOtherAppsActionAccessibilityIdentifier =
diff --git a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_coordinator.mm b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_coordinator.mm
index e22a325..cb657d8 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_coordinator.mm
@@ -5,10 +5,6 @@
 #import "ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_coordinator.h"
 
 #import "base/check.h"
-#import "ios/chrome/browser/main/browser.h"
-#import "ios/chrome/browser/sync/sync_service_factory.h"
-#import "ios/chrome/browser/sync/sync_setup_service.h"
-#import "ios/chrome/browser/sync/sync_setup_service_factory.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_mediator.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.h"
 
@@ -46,12 +42,7 @@
 }
 
 - (void)start {
-  SyncSetupService* syncSetupService =
-      SyncSetupServiceFactory::GetForBrowserState(self.browser->GetBrowserState());
-  syncer::ModelType kSyncPasswordsModelType =
-      syncSetupService->GetModelType(SyncSetupService::kSyncPasswords);
-
-  self.viewController = [[PasswordsInOtherAppsViewController alloc] initWithSyncingPasswords:syncSetupService->IsDataTypeActive(kSyncPasswordsModelType)];
+  self.viewController = [[PasswordsInOtherAppsViewController alloc] init];
   self.viewController.presenter = self;
 
   self.mediator = [[PasswordsInOtherAppsMediator alloc] init];
diff --git a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.h b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.h
index 4caf57f3..e48dc14 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.h
+++ b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.h
@@ -31,13 +31,11 @@
 @property(nonatomic, weak) id<PasswordsInOtherAppsViewControllerDelegate>
     delegate;
 
-- (instancetype)initWithSyncingPasswords:(BOOL)isSyncingPasswords
-    NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
 
-- (instancetype)init NS_UNAVAILABLE;
-- (instancetype)initWithNibName:(NSString*)nibName
-                         bundle:(NSBundle*)nibBundle NS_UNAVAILABLE;
 - (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE;
+- (instancetype)initWithNibName:(NSString*)nibNAme
+                         bundle:(NSBundle*)nibBundle NS_UNAVAILABLE;
 
 @end
 
diff --git a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.mm b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.mm
index eeba11d..108a1bb9 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.mm
@@ -6,8 +6,6 @@
 
 #include "base/ios/ios_util.h"
 #include "components/password_manager/core/common/password_manager_features.h"
-//#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-//#import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/ui/elements/instruction_view.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_in_other_apps/constants.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_view_controller_delegate.h"
@@ -50,7 +48,6 @@
 // Properties set on initialization.
 @property(nonatomic, copy, readonly) NSString* titleText;
 @property(nonatomic, copy, readonly) NSString* subtitleText;
-@property(nonatomic, copy, readonly) NSString* secondSubtitleText;
 @property(nonatomic, strong, readonly) UIImage* bannerImage;
 @property(nonatomic, copy, readonly) NSString* actionString;
 
@@ -58,9 +55,6 @@
 @property(nonatomic, strong) UIImageView* imageView;
 @property(nonatomic, strong) UILabel* titleLabel;
 @property(nonatomic, strong) UILabel* subtitleLabel;
-// NOTE: This label will only be displayed if
-// isPasswordManagerBrandingUpdateEnabled() returns true
-@property(nonatomic, strong) UILabel* secondSubtitleLabel;
 @property(nonatomic, strong) UIView* turnOnInstructionView;
 @property(nonatomic, strong) UIView* turnOffInstructionView;
 @property(nonatomic, strong) UIButton* actionButton;
@@ -90,7 +84,7 @@
 
 @implementation PasswordsInOtherAppsViewController
 
-- (instancetype)initWithSyncingPasswords:(BOOL)isSyncingPasswords {
+- (instancetype)init {
   self = [super initWithNibName:nil bundle:nil];
   if (self) {
     _titleText =
@@ -106,15 +100,7 @@
         _subtitleText = l10n_util::GetNSString(
             IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_IPHONE);
       }
-      if (isSyncingPasswords) {
-        _subtitleText = [NSString
-            stringWithFormat:
-                @"%@ %@", _subtitleText,
-                l10n_util::GetNSString(
-                    IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SUBTITLE_SYNCING_ENABLED)];
-      }
-      _secondSubtitleText = l10n_util::GetNSString(
-          IDS_IOS_SETTINGS_PASSWORDS_IN_OTHER_APPS_SECOND_SUBTITLE);
+
       _bannerImage =
           [UIImage imageNamed:@"settings_passwords_in_other_apps_banner"];
     } else {
@@ -150,9 +136,6 @@
   // Add the labels.
   [self.scrollContentView addSubview:self.titleLabel];
   [self.scrollContentView addSubview:self.subtitleLabel];
-  if (isPasswordManagerBrandingUpdateEnabled()) {
-    [self.scrollContentView addSubview:self.secondSubtitleLabel];
-  }
   [self.view addLayoutGuide:subtitleMarginLayoutGuide];
   [self.scrollContentView addSubview:self.specificContentView];
 
@@ -236,6 +219,11 @@
         constraintEqualToAnchor:self.scrollContentView.centerXAnchor],
     [self.subtitleLabel.widthAnchor
         constraintLessThanOrEqualToAnchor:self.scrollContentView.widthAnchor],
+
+    // Constraints for the screen-specific content view. It should take the
+    // remaining scroll view area, with some margins on the top and sides.
+    [subtitleMarginLayoutGuide.topAnchor
+        constraintEqualToAnchor:self.subtitleLabel.bottomAnchor],
     [subtitleMarginLayoutGuide.heightAnchor
         constraintEqualToConstant:kDefaultMargin],
     [self.specificContentView.topAnchor
@@ -248,24 +236,6 @@
         constraintEqualToAnchor:self.scrollContentView.bottomAnchor],
   ]];
 
-  if (isPasswordManagerBrandingUpdateEnabled()) {
-    [NSLayoutConstraint activateConstraints:@[
-      [self.secondSubtitleLabel.topAnchor
-          constraintEqualToAnchor:self.subtitleLabel.bottomAnchor
-                         constant:kDefaultMargin],
-      [self.secondSubtitleLabel.centerXAnchor
-          constraintEqualToAnchor:self.scrollContentView.centerXAnchor],
-      [self.secondSubtitleLabel.widthAnchor
-          constraintLessThanOrEqualToAnchor:self.scrollContentView.widthAnchor],
-      [subtitleMarginLayoutGuide.topAnchor
-          constraintEqualToAnchor:self.secondSubtitleLabel.bottomAnchor],
-    ]];
-  } else {
-    [subtitleMarginLayoutGuide.topAnchor
-        constraintEqualToAnchor:self.subtitleLabel.bottomAnchor]
-        .active = YES;
-  }
-
   // In iPhone landscape mode, the top image is removed. In that case, we should
   // make sure there is enough distance between the title label and the top edge
   // of the iPhone.
@@ -406,21 +376,20 @@
 }
 
 - (UILabel*)subtitleLabel {
-    if(!_subtitleLabel){
-      _subtitleLabel = [self createSubtitle];
-      _subtitleLabel.text = self.subtitleText;
-      _subtitleLabel.accessibilityIdentifier = kPasswordsInOtherAppsSubtitleAccessibilityIdentifier;
-    }
-    return _subtitleLabel;
-}
-
-- (UILabel*)secondSubtitleLabel {
-    if(!_secondSubtitleLabel){
-      _secondSubtitleLabel = [self createSubtitle];
-      _secondSubtitleLabel.text = self.secondSubtitleText;
-      _secondSubtitleLabel.accessibilityIdentifier = kPasswordsInOtherAppsSecondSubtitleAccessibilityIdentifier;
-    }
-    return _secondSubtitleLabel;
+  if (!_subtitleLabel) {
+    _subtitleLabel = [[UILabel alloc] init];
+    _subtitleLabel.font =
+        [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
+    _subtitleLabel.numberOfLines = 0;
+    _subtitleLabel.textColor = [UIColor colorNamed:kGrey800Color];
+    _subtitleLabel.text = self.subtitleText;
+    _subtitleLabel.textAlignment = NSTextAlignmentCenter;
+    _subtitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
+    _subtitleLabel.adjustsFontForContentSizeCategory = YES;
+    _subtitleLabel.accessibilityIdentifier =
+        kPasswordsInOtherAppsSubtitleAccessibilityIdentifier;
+  }
+  return _subtitleLabel;
 }
 
 - (UIActivityIndicatorView*)spinner {
@@ -685,19 +654,6 @@
 
 #pragma mark - Private
 
-// Creates a label with reasonable defaults
-- (UILabel*)createSubtitle {
-  UILabel* label = [[UILabel alloc] init];
-  label.font =
-      [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
-  label.numberOfLines = 0;
-  label.textColor = [UIColor colorNamed:kGrey800Color];
-  label.textAlignment = NSTextAlignmentCenter;
-  label.translatesAutoresizingMaskIntoConstraints = NO;
-  label.adjustsFontForContentSizeCategory = YES;
-  return label;
-}
-
 // Returns caption text that shows below the subtitle in turnOffInstructions.
 - (UITextView*)drawCaptionTextView {
   NSString* text =
diff --git a/ios/chrome/browser/ui/settings/safety_check/OWNERS b/ios/chrome/browser/ui/settings/safety_check/OWNERS
index ec58c6e..336c4ac5 100644
--- a/ios/chrome/browser/ui/settings/safety_check/OWNERS
+++ b/ios/chrome/browser/ui/settings/safety_check/OWNERS
@@ -1,2 +1 @@
 harrisonsean@chromium.org
-javierrobles@chromium.org
diff --git a/ios/chrome/browser/ui/settings/search_engine_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/search_engine_table_view_controller_unittest.mm
index 978f626c..661d1b4 100644
--- a/ios/chrome/browser/ui/settings/search_engine_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/search_engine_table_view_controller_unittest.mm
@@ -472,8 +472,9 @@
 
   // Check that the selection was written back to the prefs.
   const base::DictionaryValue* searchProviderDict =
-      chrome_browser_state_->GetTestingPrefService()->GetDictionary(
-          DefaultSearchManager::kDefaultSearchProviderDataPrefName);
+      &base::Value::AsDictionaryValue(
+          *chrome_browser_state_->GetTestingPrefService()->GetDictionary(
+              DefaultSearchManager::kDefaultSearchProviderDataPrefName));
   ASSERT_TRUE(searchProviderDict);
   std::u16string short_name;
   EXPECT_TRUE(searchProviderDict->GetString(DefaultSearchManager::kShortName,
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index cb793e7..be0024f 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -1641,7 +1641,7 @@
 
 // Check if the default search engine is managed by policy.
 - (BOOL)isDefaultSearchEngineManagedByPolicy {
-  const base::DictionaryValue* dict = _browserState->GetPrefs()->GetDictionary(
+  const base::Value* dict = _browserState->GetPrefs()->GetDictionary(
       DefaultSearchManager::kDefaultSearchProviderDataPrefName);
 
   if (dict) {
@@ -1654,7 +1654,7 @@
 
 // Returns the text to be displayed by the managed Search Engine item.
 - (NSString*)managedSearchEngineDetailText {
-  const base::DictionaryValue* dict = _browserState->GetPrefs()->GetDictionary(
+  const base::Value* dict = _browserState->GetPrefs()->GetDictionary(
       DefaultSearchManager::kDefaultSearchProviderDataPrefName);
   if (dict->FindBoolPath(DefaultSearchManager::kDisabledByPolicy)) {
     // Default search engine is disabled by policy.
diff --git a/ios/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.mm b/ios/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.mm
index 3c5be31b..5ef796c 100644
--- a/ios/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.mm
+++ b/ios/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.mm
@@ -32,8 +32,6 @@
 
   source->AddResourcePath("autofill_and_password_manager_internals.js",
                           IDR_AUTOFILL_AND_PASSWORD_MANAGER_INTERNALS_JS);
-  source->AddResourcePath("autofill_and_password_manager_internals.css",
-                          IDR_AUTOFILL_AND_PASSWORD_MANAGER_INTERNALS_CSS);
   source->SetDefaultResource(IDR_AUTOFILL_AND_PASSWORD_MANAGER_INTERNALS_HTML);
   // Data strings:
   source->AddString(version_ui::kVersion, version_info::GetVersionNumber());
diff --git a/ios/chrome/browser/widget_kit/OWNERS b/ios/chrome/browser/widget_kit/OWNERS
index c2536df3..04c64383 100644
--- a/ios/chrome/browser/widget_kit/OWNERS
+++ b/ios/chrome/browser/widget_kit/OWNERS
@@ -1,2 +1 @@
-javierrobles@chromium.org
 rkgibson@google.com
diff --git a/ios/chrome/common/OWNERS b/ios/chrome/common/OWNERS
index 632de029..3ebff38d 100644
--- a/ios/chrome/common/OWNERS
+++ b/ios/chrome/common/OWNERS
@@ -1,2 +1 @@
-javierrobles@chromium.org
 olivierrobin@chromium.org
diff --git a/ios/chrome/common/credential_provider/OWNERS b/ios/chrome/common/credential_provider/OWNERS
index f29d2615..cb25906 100644
--- a/ios/chrome/common/credential_provider/OWNERS
+++ b/ios/chrome/common/credential_provider/OWNERS
@@ -1,2 +1 @@
-javierrobles@chromium.org
 djean@chromium.org
diff --git a/ios/chrome/common/ui/colors/OWNERS b/ios/chrome/common/ui/colors/OWNERS
index c2536df3..d75ee0e 100644
--- a/ios/chrome/common/ui/colors/OWNERS
+++ b/ios/chrome/common/ui/colors/OWNERS
@@ -1,2 +1,2 @@
-javierrobles@chromium.org
+djean@chromium.org
 rkgibson@google.com
diff --git a/ios/chrome/common/ui/confirmation_alert/OWNERS b/ios/chrome/common/ui/confirmation_alert/OWNERS
index 7be16e8..c5cd5cb 100644
--- a/ios/chrome/common/ui/confirmation_alert/OWNERS
+++ b/ios/chrome/common/ui/confirmation_alert/OWNERS
@@ -1,2 +1 @@
-javierrobles@chromium.org
 sdefresne@chromium.org
diff --git a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
index a8b6f277..207befa 100644
--- a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
+++ b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
@@ -93,15 +93,14 @@
   UIStackView* stackView =
       [self createStackViewWithArrangedSubviews:stackSubviews];
 
-  // UIView that wraps the scrollable content. Needed in order to pin UI
+  // UILayoutGuide that wraps the scrollable content. Needed in order to pin UI
   // elements in view specific content above actions buttons.
-  UIView* scrollContentView = [[UIView alloc] init];
-  scrollContentView.translatesAutoresizingMaskIntoConstraints = NO;
+  UILayoutGuide* scrollContentLayoutGuide = [[UILayoutGuide alloc] init];
 
   UIScrollView* scrollView = [self createScrollView];
-  [scrollContentView addSubview:stackView];
-  [scrollContentView addSubview:self.specificContentView];
-  [scrollView addSubview:scrollContentView];
+  [scrollView addLayoutGuide:scrollContentLayoutGuide];
+  [scrollView addSubview:stackView];
+  [scrollView addSubview:self.specificContentView];
   [self.view addSubview:scrollView];
 
   self.view.preservesSuperviewLayoutMargins = YES;
@@ -127,13 +126,14 @@
         constraintLessThanOrEqualToAnchor:margins.widthAnchor],
 
     [stackView.topAnchor
-        constraintGreaterThanOrEqualToAnchor:scrollContentView.topAnchor],
+        constraintGreaterThanOrEqualToAnchor:scrollContentLayoutGuide
+                                                 .topAnchor],
     [stackView.bottomAnchor
         constraintLessThanOrEqualToAnchor:self.specificContentView.topAnchor
                                  constant:-kScrollViewBottomInsets],
 
     [self.specificContentView.bottomAnchor
-        constraintEqualToAnchor:scrollContentView.bottomAnchor],
+        constraintEqualToAnchor:scrollContentLayoutGuide.bottomAnchor],
     [self.specificContentView.centerXAnchor
         constraintEqualToAnchor:self.view.centerXAnchor],
     [self.specificContentView.widthAnchor
@@ -141,12 +141,12 @@
 
     // Constrain its height to at least the scroll view height, so that derived
     // VCs can pin UI elements just above the buttons.
-    [scrollContentView.topAnchor constraintEqualToAnchor:scrollView.topAnchor],
-    [scrollContentView.bottomAnchor
+    [scrollContentLayoutGuide.topAnchor
+        constraintEqualToAnchor:scrollView.topAnchor],
+    [scrollContentLayoutGuide.bottomAnchor
         constraintEqualToAnchor:scrollView.bottomAnchor],
-    [scrollContentView.heightAnchor
+    [scrollContentLayoutGuide.heightAnchor
         constraintGreaterThanOrEqualToAnchor:scrollView.heightAnchor],
-
   ]];
 
   // Width Scroll View constraint for regular mode.
@@ -210,11 +210,12 @@
       .active = YES;
 
   if (self.topAlignedLayout) {
-    [stackView.topAnchor constraintEqualToAnchor:scrollContentView.topAnchor]
+    [stackView.topAnchor
+        constraintEqualToAnchor:scrollContentLayoutGuide.topAnchor]
         .active = YES;
   } else {
     [stackView.centerYAnchor
-        constraintEqualToAnchor:scrollContentView.centerYAnchor]
+        constraintEqualToAnchor:scrollContentLayoutGuide.centerYAnchor]
         .active = YES;
   }
 
diff --git a/ios/chrome/credential_provider_extension/OWNERS b/ios/chrome/credential_provider_extension/OWNERS
index 00a908da..602c0445 100644
--- a/ios/chrome/credential_provider_extension/OWNERS
+++ b/ios/chrome/credential_provider_extension/OWNERS
@@ -1,3 +1,2 @@
-javierrobles@chromium.org
-djean@chromium.org
 rkgibson@google.com
+djean@chromium.org
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_ar.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_ar.xtb
index 80fda5b..6c6d9ae3f 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_ar.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_ar.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">‏يتم حفظ كلمات المرور في "مدير كلمات المرور" من Google لتتمكّن من استخدامها على أي جهاز.</translation>
 <translation id="3753678329684433031">‏الملء التلقائي لكلمة المرور في متصفِّح Chrome</translation>
 <translation id="3789385946721385622">اسم المستخدم</translation>
+<translation id="3830647155781949426">‏ستُحفظ كلمة المرور في "مدير كلمات المرور" من Google على عنوان البريد الإلكتروني <ph name="EMAIL" /> عند العودة إلى Chrome.</translation>
 <translation id="4064278913989596727">مساعدة</translation>
 <translation id="4241076354893135477">‏ما مِن كلمات مرور محفوظة في متصفِّح Chrome</translation>
 <translation id="4452240207605337349">يتعذّر حفظ كلمة المرور</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_bn.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_bn.xtb
index 8ce5806..0d0a80a 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_bn.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_bn.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">আপনি যেকোনও ডিভাইসেই যাতে পাসওয়ার্ড ব্যবহার করতে পারেন, সেই জন্য Google পাসওয়ার্ড ম্যানেজার বিকল্পে পাসওয়ার্ড সেভ করা হয়।</translation>
 <translation id="3753678329684433031">Chrome-এ সেভ থাকা পাসওয়ার্ড অটো-ফিল করা</translation>
 <translation id="3789385946721385622">ইউজারনেম</translation>
+<translation id="3830647155781949426">আপনি Chorme-এ ফিরে এলে, <ph name="EMAIL" /> ইমেলের জন্য, Google পাসওয়ার্ড ম্যানেজারে আপনার পাসওয়ার্ড সেভ হয়ে যাবে।</translation>
 <translation id="4064278913989596727">সহায়তা</translation>
 <translation id="4241076354893135477">Chrome-এ কোনও পাসওয়ার্ড সেভ করা নেই</translation>
 <translation id="4452240207605337349">পাসওয়ার্ড সেভ করতে পারছে না</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_cs.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_cs.xtb
index 854689a..d87d4fa 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_cs.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_cs.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">Hesla se ukládají do Správce hesel Google, abyste je mohli používat na všech zařízeních.</translation>
 <translation id="3753678329684433031">Automatické vyplňování hesla Chromu</translation>
 <translation id="3789385946721385622">Uživatelské jméno</translation>
+<translation id="3830647155781949426">Když se vrátíte do Chromu, vaše heslo se uloží do Správce hesel Google pro účet <ph name="EMAIL" />.</translation>
 <translation id="4064278913989596727">Nápověda</translation>
 <translation id="4241076354893135477">V Chromu nemáte žádná hesla</translation>
 <translation id="4452240207605337349">Heslo nelze uložit</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_fa.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_fa.xtb
index ec4309b..ec7eeb7 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_fa.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_fa.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">‏گذرواژه‌ها در «مدیر گذرواژه Google» ذخیره می‌شوند و می‌توانید در هر دستگاهی از آن‌ها استفاده کنید.</translation>
 <translation id="3753678329684433031">‏تکمیل خودکار گذرواژه Chrome</translation>
 <translation id="3789385946721385622">نام کاربری</translation>
+<translation id="3830647155781949426">‏پس‌از برگشتن به Chrome، گذرواژه‌تان در «مدیر گذرواژه Google» برای <ph name="EMAIL" /> حذف خواهد شد.</translation>
 <translation id="4064278913989596727">راهنما</translation>
 <translation id="4241076354893135477">‏گذرواژه‌ای در Chrome نیست</translation>
 <translation id="4452240207605337349">گذرواژه ذخیره نشد</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_gu.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_gu.xtb
index 7ea9aaad..ab7093b 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_gu.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_gu.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">પાસવર્ડ Google પાસવર્ડ મેનેજરમાં સાચવવામાં આવે છે અને તમે કોઈપણ ડિવાઇસમાં તેનો ઉપયોગ કરી શકો છો.</translation>
 <translation id="3753678329684433031">Chromeનો પાસવર્ડ ઑટોમૅટિક રીતે ભરાય</translation>
 <translation id="3789385946721385622">વપરાશકર્તાનું નામ</translation>
+<translation id="3830647155781949426">તમે Chrome પર પાછા આવો, ત્યાં સુધી <ph name="EMAIL" /> માટેનો તમારો પાસવર્ડ તમારા Google પાસવર્ડ મેનેજરમાં સાચવવામાં આવશે.</translation>
 <translation id="4064278913989596727">સહાય</translation>
 <translation id="4241076354893135477">Chromeમાં કોઈ પાસવર્ડ નથી</translation>
 <translation id="4452240207605337349">પાસવર્ડ સાચવી શકાતો નથી</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_hy.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_hy.xtb
index dffb370..ed27b9891 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_hy.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_hy.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">Գաղտնաբառերը պահվում են Google Գաղտնաբառերի կառավարիչում, և դուք կարող եք դրանք օգտագործել ցանկացած սարքում</translation>
 <translation id="3753678329684433031">Chrome-ի գաղտնաբառի ինքնալրացում</translation>
 <translation id="3789385946721385622">Օգտանուն</translation>
+<translation id="3830647155781949426">Երբ դուք նորից բացեք Chrome-ը, ձեր գաղտնաբառը կպահվի <ph name="EMAIL" /> հաշվի Google Գաղտնաբառերի կառավարիչում։</translation>
 <translation id="4064278913989596727">Օգնություն</translation>
 <translation id="4241076354893135477">Chrome-ի գաղտնաբառեր չկան</translation>
 <translation id="4452240207605337349">Չհաջողվեց պահել գաղտնաբառը</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_lo.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_lo.xtb
index 52e0c0205..7852817 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_lo.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_lo.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">ລະຫັດຜ່ານແມ່ນຖືກບັນທຶກໄປໃສ່ຕົວຈັດການລະຫັດຜ່ານ Google ແລະ ທ່ານສາມາດໃຊ້ພວກມັນຢູ່ອຸປະກອນໃດກໍໄດ້.</translation>
 <translation id="3753678329684433031">ຕື່ມລະຫັດຜ່ານໃນ Chrome ໂດຍອັດຕະໂນມັດ</translation>
 <translation id="3789385946721385622">ຊື່ຜູ້ໃຊ້</translation>
+<translation id="3830647155781949426">ລະຫັດຜ່ານຂອງທ່ານຈະຖືກບັນທຶກໄປໃສ່ຕົວຈັດການລະຫັດຜ່ານ Google ສຳລັບ <ph name="EMAIL" /> ເມື່ອທ່ານກັບໄປຫາ Chrome.</translation>
 <translation id="4064278913989596727">ຊ່ວຍເຫຼືອ</translation>
 <translation id="4241076354893135477">ບໍ່ມີລະຫັດຜ່ານໃນ Chrome</translation>
 <translation id="4452240207605337349">ບໍ່ສາມາດບັນທຶກລະຫັດຜ່ານໄດ້</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_mr.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_mr.xtb
index 190922a6e..bf4d0b3 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_mr.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_mr.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">पासवर्ड हे Google पासवर्ड व्यवस्थापक यामध्ये सेव्ह केले जातात आणि तुम्ही ते कोणत्याही डिव्हाइसवर वापरू शकता.</translation>
 <translation id="3753678329684433031">Chrome पासवर्ड ऑटोफिल करा</translation>
 <translation id="3789385946721385622">वापरकर्ता नाव</translation>
+<translation id="3830647155781949426">तुम्ही Chrome वर परत आल्यावर तुमचा पासवर्ड <ph name="EMAIL" /> साठी Google पासवर्ड व्यवस्थापक यामध्ये सेव्ह केला जाईल.</translation>
 <translation id="4064278913989596727">मदत</translation>
 <translation id="4241076354893135477">Chrome पासवर्ड नाहीत</translation>
 <translation id="4452240207605337349">पासवर्ड सेव्ह करू शकत नाही</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_ms.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_ms.xtb
index 69c165c..2e22a9f 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_ms.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_ms.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">Kata laluan disimpan pada Pengurus Kata Laluan Google supaya anda boleh menggunakan kata laluan tersebut pada mana-mana peranti.</translation>
 <translation id="3753678329684433031">Autolengkap Kata Laluan Chrome</translation>
 <translation id="3789385946721385622">Nama pengguna</translation>
+<translation id="3830647155781949426">Kata laluan anda akan disimpan pada Pengurus Kata Laluan Google untuk <ph name="EMAIL" /> apabila anda kembali menggunakan Chrome.</translation>
 <translation id="4064278913989596727">Bantuan</translation>
 <translation id="4241076354893135477">Tiada Kata Laluan Chrome</translation>
 <translation id="4452240207605337349">Tidak Dapat Menyimpan Kata Laluan</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_ne.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_ne.xtb
index c5f7f29..386de8d4 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_ne.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_ne.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">पासवर्डहरू Google पासवर्ड म्यानेजरमा सेभ गरिन्छन् र तपाईं ती पासवर्डहरू जुनसुकै डिभाइसमा प्रयोग गर्न सक्नुहुन्छ।</translation>
 <translation id="3753678329684433031">Chrome मा सुरक्षित गरिएको पासवर्ड स्वतः भरियोस्</translation>
 <translation id="3789385946721385622">युजरनेम</translation>
+<translation id="3830647155781949426">तपाईं Chrome मा फर्केर आउँदा तपाईंको पासवर्ड <ph name="EMAIL" /> खाताको Google पासवर्ड म्यानेजरमा सेभ गरिने छ।</translation>
 <translation id="4064278913989596727">मद्दत</translation>
 <translation id="4241076354893135477">Chrome मा कुनै पनि पासवर्ड सुरक्षित गरिएको छैन</translation>
 <translation id="4452240207605337349">पासवर्ड सेभ गर्न सकिएन</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_pa.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_pa.xtb
index fcd5bb62..4842476 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_pa.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_pa.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">ਪਾਸਵਰਡ Google ਪਾਸਵਰਡ ਪ੍ਰਬੰਧਕ ਵਿੱਚ ਰੱਖਿਅਤ ਕੀਤੇ ਗਏ ਹਨ ਅਤੇ ਤੁਸੀਂ ਉਨ੍ਹਾਂ ਨੂੰ ਕਿਸੇ ਵੀ ਡੀਵਾਈਸ 'ਤੇ ਵਰਤ ਸਕਦੇ ਹੋ।</translation>
 <translation id="3753678329684433031">ਆਟੋਫਿਲ Chrome ਪਾਸਵਰਡ</translation>
 <translation id="3789385946721385622">ਵਰਤੋਂਕਾਰ ਨਾਮ</translation>
+<translation id="3830647155781949426"><ph name="EMAIL" /> ਲਈ ਤੁਹਾਡਾ ਪਾਸਵਰਡ Google ਪਾਸਵਰਡ ਪ੍ਰਬੰਧਕ ਵਿੱਚ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ, ਤਾਂ ਜੋ ਤੁਸੀਂ ਕਦੇ ਵੀ ਵਾਪਸ ਆ ਕੇ ਇਸ ਨੂੰ ਵਰਤ ਸਕੋ।</translation>
 <translation id="4064278913989596727">ਮਦਦ</translation>
 <translation id="4241076354893135477">ਕੋਈ Chrome ਪਾਸਵਰਡ ਨਹੀਂ</translation>
 <translation id="4452240207605337349">ਪਾਸਵਰਡ ਨੂੰ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_sk.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_sk.xtb
index 660a695..6fe1a26f 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_sk.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_sk.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">Heslá sa ukladajú do správcu hesiel Google, takže ich môžete používať vo všetkých zariadeniach.</translation>
 <translation id="3753678329684433031">Automatické dopĺňanie hesiel Chromu</translation>
 <translation id="3789385946721385622">Používateľské meno</translation>
+<translation id="3830647155781949426">Keď sa vrátite do Chromu, vaše heslo účtu <ph name="EMAIL" /> sa uloží do správcu hesiel od Googlu.</translation>
 <translation id="4064278913989596727">Pomocník</translation>
 <translation id="4241076354893135477">Žiadne heslá Chromu</translation>
 <translation id="4452240207605337349">Heslo sa nedá uložiť</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_sw.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_sw.xtb
index bfd94292..2fa727a 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_sw.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_sw.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">Manenosiri huhifadhiwa kwenye Kidhibiti cha Manenosiri cha Google na unaweza kuyatumia kwenye kifaa chochote.</translation>
 <translation id="3753678329684433031">Jaza Kiotomatiki Nenosiri la Chrome</translation>
 <translation id="3789385946721385622">Jina la mtumiaji</translation>
+<translation id="3830647155781949426">Nenosiri lako litahifadhiwa kwenye Kidhibiti cha Manenosiri cha Google kwa ajili ya <ph name="EMAIL" /> ukirejea kwenye Chrome.</translation>
 <translation id="4064278913989596727">Usaidizi</translation>
 <translation id="4241076354893135477">Hamna Manenosiri ya Chrome</translation>
 <translation id="4452240207605337349">Imeshindwa Kuhifadhi Nenosiri</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_vi.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_vi.xtb
index 8f9c01d..2da680a 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_vi.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_vi.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">Mật khẩu được lưu vào Trình quản lý mật khẩu của Google để bạn có thể dùng trên mọi thiết bị.</translation>
 <translation id="3753678329684433031">Tự động điền mật khẩu trên Chrome</translation>
 <translation id="3789385946721385622">Tên người dùng</translation>
+<translation id="3830647155781949426">Mật khẩu của bạn sẽ được lưu vào Trình quản lý mật khẩu của Google cho tài khoản <ph name="EMAIL" /> khi bạn quay lại sử dụng Chrome.</translation>
 <translation id="4064278913989596727">Trợ giúp</translation>
 <translation id="4241076354893135477">Không có mật khẩu trên Chrome</translation>
 <translation id="4452240207605337349">Không thể lưu mật khẩu</translation>
diff --git a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_zu.xtb b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_zu.xtb
index d88cc32..c8e911f 100644
--- a/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_zu.xtb
+++ b/ios/chrome/credential_provider_extension/strings/resources/ios_credential_provider_extension_strings_zu.xtb
@@ -21,6 +21,7 @@
 <translation id="3739920431472254679">Amaphasiwedi alondolozwa ku-Google Password Manager futhi ungawasebenzisa kunoma iyiphi idivayisi.</translation>
 <translation id="3753678329684433031">Gcwalisa Ngokuzenzakalelayo Iphasiwedi ye-Chrome</translation>
 <translation id="3789385946721385622">Igama lomsebenzisi</translation>
+<translation id="3830647155781949426">Iphasiwedi yakho izolondolozwa ku-Google Password Manager ka-<ph name="EMAIL" /> uma ubuyela ku-Chrome.</translation>
 <translation id="4064278913989596727">Usizo</translation>
 <translation id="4241076354893135477">Awekho amaphasiwedi we-Chrome</translation>
 <translation id="4452240207605337349">Ayikwazi Ukulondoloza Iphasiwedi</translation>
diff --git a/ios/chrome/widget_kit_extension/OWNERS b/ios/chrome/widget_kit_extension/OWNERS
index c2536df3..04c64383 100644
--- a/ios/chrome/widget_kit_extension/OWNERS
+++ b/ios/chrome/widget_kit_extension/OWNERS
@@ -1,2 +1 @@
-javierrobles@chromium.org
 rkgibson@google.com
diff --git a/ios/components/webui/sync_internals/sync_internals_message_handler.h b/ios/components/webui/sync_internals/sync_internals_message_handler.h
index da971a6..a84f86f7 100644
--- a/ios/components/webui/sync_internals/sync_internals_message_handler.h
+++ b/ios/components/webui/sync_internals/sync_internals_message_handler.h
@@ -36,32 +36,33 @@
 
   // Fires an event to send updated data to the About page and registers
   // observers to notify the page upon updates.
-  void HandleRequestDataAndRegisterForUpdates(const base::ListValue* args);
+  void HandleRequestDataAndRegisterForUpdates(base::Value::ConstListView args);
 
   // Fires an event to send the list of types back to the page.
-  void HandleRequestListOfTypes(const base::ListValue* args);
+  void HandleRequestListOfTypes(base::Value::ConstListView args);
 
   // Fires an event to send the initial state of the "include specifics" flag.
-  void HandleRequestIncludeSpecificsInitialState(const base::ListValue* args);
+  void HandleRequestIncludeSpecificsInitialState(
+      base::Value::ConstListView args);
 
   // Handler for getAllNodes message.  Needs a |request_id| argument.
-  void HandleGetAllNodes(const base::ListValue* args);
+  void HandleGetAllNodes(base::Value::ConstListView args);
 
   // Handler for setting internal state of if specifics should be included in
   // protocol events when sent to be displayed.
-  void HandleSetIncludeSpecifics(const base::ListValue* args);
+  void HandleSetIncludeSpecifics(base::Value::ConstListView args);
 
   // Handler for requestStart message.
-  void HandleRequestStart(const base::ListValue* args);
+  void HandleRequestStart(base::Value::ConstListView args);
 
   // Handler for requestStopKeepData message.
-  void HandleRequestStopKeepData(const base::ListValue* args);
+  void HandleRequestStopKeepData(base::Value::ConstListView args);
 
   // Handler for requestStopClearData message.
-  void HandleRequestStopClearData(const base::ListValue* args);
+  void HandleRequestStopClearData(base::Value::ConstListView args);
 
   // Handler for triggerRefresh message.
-  void HandleTriggerRefresh(const base::ListValue* args);
+  void HandleTriggerRefresh(base::Value::ConstListView args);
 
   // Callback used in GetAllNodes.
   void OnReceivedAllNodes(const std::string& callback_id,
diff --git a/ios/components/webui/sync_internals/sync_internals_message_handler.mm b/ios/components/webui/sync_internals/sync_internals_message_handler.mm
index 225b737..2009f554 100644
--- a/ios/components/webui/sync_internals/sync_internals_message_handler.mm
+++ b/ios/components/webui/sync_internals/sync_internals_message_handler.mm
@@ -52,61 +52,61 @@
 void SyncInternalsMessageHandler::RegisterMessages() {
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kRequestDataAndRegisterForUpdates,
       base::BindRepeating(
           &SyncInternalsMessageHandler::HandleRequestDataAndRegisterForUpdates,
           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kRequestListOfTypes,
       base::BindRepeating(
           &SyncInternalsMessageHandler::HandleRequestListOfTypes,
           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kRequestIncludeSpecificsInitialState,
       base::BindRepeating(&SyncInternalsMessageHandler::
                               HandleRequestIncludeSpecificsInitialState,
                           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kSetIncludeSpecifics,
       base::BindRepeating(
           &SyncInternalsMessageHandler::HandleSetIncludeSpecifics,
           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kRequestStart,
       base::BindRepeating(&SyncInternalsMessageHandler::HandleRequestStart,
                           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kRequestStopKeepData,
       base::BindRepeating(
           &SyncInternalsMessageHandler::HandleRequestStopKeepData,
           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kRequestStopClearData,
       base::BindRepeating(
           &SyncInternalsMessageHandler::HandleRequestStopClearData,
           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kTriggerRefresh,
       base::BindRepeating(&SyncInternalsMessageHandler::HandleTriggerRefresh,
                           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       syncer::sync_ui_util::kGetAllNodes,
       base::BindRepeating(&SyncInternalsMessageHandler::HandleGetAllNodes,
                           base::Unretained(this)));
 }
 
 void SyncInternalsMessageHandler::HandleRequestDataAndRegisterForUpdates(
-    const base::ListValue* args) {
-  DCHECK(args->GetList().empty());
+    base::Value::ConstListView args) {
+  DCHECK(args.empty());
 
   // is_registered_ flag protects us from double-registering.  This could
   // happen on a page refresh, where the JavaScript gets re-run but the
@@ -122,8 +122,8 @@
 }
 
 void SyncInternalsMessageHandler::HandleRequestListOfTypes(
-    const base::ListValue* args) {
-  DCHECK(args->GetList().empty());
+    base::Value::ConstListView args) {
+  DCHECK(args.empty());
   base::DictionaryValue event_details;
   auto type_list = std::make_unique<base::ListValue>();
   syncer::ModelTypeSet protocol_types = syncer::ProtocolTypes();
@@ -135,8 +135,8 @@
 }
 
 void SyncInternalsMessageHandler::HandleRequestIncludeSpecificsInitialState(
-    const base::ListValue* args) {
-  DCHECK(args->GetList().empty());
+    base::Value::ConstListView args) {
+  DCHECK(args.empty());
 
   base::DictionaryValue value;
   value.SetBoolean(syncer::sync_ui_util::kIncludeSpecifics,
@@ -147,14 +147,10 @@
 }
 
 void SyncInternalsMessageHandler::HandleGetAllNodes(
-    const base::ListValue* args) {
-  base::Value::ConstListView args_list = args->GetList();
-  DCHECK_EQ(1U, args_list.size());
-  std::string callback_id;
-  if (args_list[0].is_string())
-    callback_id = args_list[0].GetString();
-  else
-    DCHECK(false);
+    base::Value::ConstListView args) {
+  DCHECK_EQ(1U, args.size());
+  DCHECK(args[0].is_string());
+  std::string callback_id = args[0].GetString();
 
   syncer::SyncService* service = GetSyncService();
   if (service) {
@@ -165,14 +161,14 @@
 }
 
 void SyncInternalsMessageHandler::HandleSetIncludeSpecifics(
-    const base::ListValue* args) {
-  DCHECK_EQ(1U, args->GetList().size());
-  include_specifics_ = args->GetList()[0].GetBool();
+    base::Value::ConstListView args) {
+  DCHECK_EQ(1U, args.size());
+  include_specifics_ = args[0].GetBool();
 }
 
 void SyncInternalsMessageHandler::HandleRequestStart(
-    const base::ListValue* args) {
-  DCHECK_EQ(0U, args->GetList().size());
+    base::Value::ConstListView args) {
+  DCHECK_EQ(0U, args.size());
 
   syncer::SyncService* service = GetSyncService();
   if (!service) {
@@ -188,8 +184,8 @@
 }
 
 void SyncInternalsMessageHandler::HandleRequestStopKeepData(
-    const base::ListValue* args) {
-  DCHECK_EQ(0U, args->GetList().size());
+    base::Value::ConstListView args) {
+  DCHECK_EQ(0U, args.size());
 
   syncer::SyncService* service = GetSyncService();
   if (!service) {
@@ -200,8 +196,8 @@
 }
 
 void SyncInternalsMessageHandler::HandleRequestStopClearData(
-    const base::ListValue* args) {
-  DCHECK_EQ(0U, args->GetList().size());
+    base::Value::ConstListView args) {
+  DCHECK_EQ(0U, args.size());
 
   syncer::SyncService* service = GetSyncService();
   if (!service) {
@@ -212,7 +208,7 @@
 }
 
 void SyncInternalsMessageHandler::HandleTriggerRefresh(
-    const base::ListValue* args) {
+    base::Value::ConstListView args) {
   syncer::SyncService* service = GetSyncService();
   if (!service) {
     return;
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 c7b9c78..6148e12 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 @@
-8d6242a223d9dc5e6e4a9242f311432aabfeb79e
\ No newline at end of file
+b3ae3646e5e5d0e3b5dc2a692b07e15abe305bf0
\ 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 29080e9..a12b299 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 @@
-9c22f8323e9a88b001cd017aa1c70c88f822bc19
\ No newline at end of file
+722f63d52f679d4b56d064f58c1af44a082b01d8
\ 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 a6fc218..098e1143 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 @@
-a12d52efb278fc620d2d8928facbb723aeaec78e
\ No newline at end of file
+62a29335b3f94a2727a97452ebd2b4c0d5b50b1a
\ 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 359cc0d..7a5aaff 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 @@
-5fbcc5810969313ac55576f02047d8bca75037c9
\ No newline at end of file
+2c4e3dda8e9daf1645a8d726d2433b5ca57f4da2
\ 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 522c61e..3f75d41 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 @@
-9bc094dd58b28fd9728e936760525794d3b7b819
\ No newline at end of file
+98d3d2a9e8da83fab0a838eb362b088fcef977f1
\ 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 192d0a11..a54c7f4 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 @@
-fec7eb8ed58781f60dafca1476e58d7b54dc14b8
\ No newline at end of file
+21b2c5a0ccd0581954df1e3b5b4d3c55cc1154e6
\ 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 3bf299d5..64bae475a 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 @@
-1e03901ad6b2e1c80a2197afe333f1a91f64bdde
\ No newline at end of file
+ac31433d93f2bb07cd4948d619291b4772faee4f
\ 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 163a895d..12ba2c7 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 @@
-fcc2a7f18ac9e7eb147a7fecc5238ac555bb4f40
\ No newline at end of file
+71fd2669c64af166734d535e6367b1b8443cac98
\ 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 3333b73..5b6a44c 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 @@
-3885e4e6eaf29504c5df3cbfc0cc2ae115070f29
\ No newline at end of file
+4f9e1a94f1892c02eb4f1963bbe0c78d0ba1aaf7
\ 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 8f0cb03..102beed 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 @@
-eb99667b5c138142ff6cdc7b87e01815f2b81309
\ No newline at end of file
+6ae84a951227033ffcbeceaf64b647d24b1cccc9
\ No newline at end of file
diff --git a/ios/showcase/credential_provider/OWNERS b/ios/showcase/credential_provider/OWNERS
index 93ecef3..602c0445 100644
--- a/ios/showcase/credential_provider/OWNERS
+++ b/ios/showcase/credential_provider/OWNERS
@@ -1 +1,2 @@
-javierrobles@chromium.org
+rkgibson@google.com
+djean@chromium.org
diff --git a/ios/web/common/features.h b/ios/web/common/features.h
index 5ab444f..23449f4 100644
--- a/ios/web/common/features.h
+++ b/ios/web/common/features.h
@@ -75,6 +75,12 @@
 // See //docs/ios/unrealized_web_state.md for more information.
 extern const base::Feature kEnableUnrealizedWebStates;
 
+// Enables user control for camera and/or microphone access for a specific site
+// through site settings during its lifespan. When enabled, each web state will
+// keep track of whether camera and/or microphone access is granted by the user
+// for its current site.
+extern const base::Feature kMediaPermissionsControl;
+
 // When true, the native context menu for the web content are used.
 bool UseWebViewNativeContextMenuWeb();
 
diff --git a/ios/web/common/features.mm b/ios/web/common/features.mm
index f4edc81b..0f773ac 100644
--- a/ios/web/common/features.mm
+++ b/ios/web/common/features.mm
@@ -65,6 +65,9 @@
 const base::Feature kEnableUnrealizedWebStates{
     "EnableUnrealizedWebStates", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kMediaPermissionsControl{"MediaPermissionsControl",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+
 bool UseWebViewNativeContextMenuWeb() {
   return base::FeatureList::IsEnabled(kDefaultWebViewContextMenu);
 }
diff --git a/ios/web/js_messaging/BUILD.gn b/ios/web/js_messaging/BUILD.gn
index c0160371..7dce5999 100644
--- a/ios/web/js_messaging/BUILD.gn
+++ b/ios/web/js_messaging/BUILD.gn
@@ -156,8 +156,10 @@
     "//ios/web/public/js_messaging",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
+    "//ios/web/test:js_test_util_internal",
     "//ios/web/test/fakes",
     "//ios/web/web_state:web_state_impl_header",
+    "//ios/web/web_state/ui:web_controller_header",
     "//ios/web/web_state/ui:wk_web_view_configuration_provider",
     "//testing/gtest",
     "//third_party/ocmock",
@@ -196,8 +198,10 @@
     "//ios/web/public/test",
     "//ios/web/public/test:util",
     "//ios/web/public/test/fakes",
+    "//ios/web/test:js_test_util_internal",
     "//ios/web/test:test_support",
     "//ios/web/test/fakes",
+    "//ios/web/web_state/ui:web_controller_header",
     "//ios/web/web_state/ui:wk_web_view_configuration_provider_header",
     "//net:test_support",
     "//testing/gmock",
diff --git a/ios/web/js_messaging/page_script_util_unittest.mm b/ios/web/js_messaging/page_script_util_unittest.mm
index 1a8137a..356b2261 100644
--- a/ios/web/js_messaging/page_script_util_unittest.mm
+++ b/ios/web/js_messaging/page_script_util_unittest.mm
@@ -14,6 +14,7 @@
 #import "ios/web/public/test/fakes/fake_web_client.h"
 #import "ios/web/public/test/js_test_util.h"
 #include "ios/web/public/test/web_test.h"
+#import "ios/web/test/js_test_util_internal.h"
 #import "testing/gtest_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/web/js_messaging/scoped_wk_script_message_handler_unittest.mm b/ios/web/js_messaging/scoped_wk_script_message_handler_unittest.mm
index 4e959a32..1c96a6f9 100644
--- a/ios/web/js_messaging/scoped_wk_script_message_handler_unittest.mm
+++ b/ios/web/js_messaging/scoped_wk_script_message_handler_unittest.mm
@@ -7,7 +7,10 @@
 #import "base/bind.h"
 #import "base/ios/ios_util.h"
 #import "base/test/ios/wait_util.h"
+#import "ios/web/public/test/web_state_test_util.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
+#import "ios/web/test/js_test_util_internal.h"
+#import "ios/web/web_state/ui/crw_web_controller.h"
 #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
 #import "testing/gtest_mac.h"
 
@@ -135,8 +138,10 @@
 
     ASSERT_TRUE(LoadHtml("<p>"));
 
-    id result = ExecuteJavaScript(WKContentWorld.defaultClientWorld,
-                                  GetPostMessageScript());
+    WKWebView* web_view =
+        [web::test::GetWebController(web_state()) ensureWebViewCreated];
+    id result = web::test::ExecuteJavaScript(
+        web_view, WKContentWorld.defaultClientWorld, GetPostMessageScript());
     ASSERT_TRUE([result isKindOfClass:[NSNumber class]]);
     ASSERT_TRUE([result boolValue]);
   }
@@ -170,8 +175,10 @@
 
     ASSERT_TRUE(LoadHtml("<p>"));
 
-    id result = ExecuteJavaScript(WKContentWorld.defaultClientWorld,
-                                  GetPostMessageScript());
+    WKWebView* web_view =
+        [web::test::GetWebController(web_state()) ensureWebViewCreated];
+    id result = web::test::ExecuteJavaScript(
+        web_view, WKContentWorld.defaultClientWorld, GetPostMessageScript());
     // JavaScript exception should be thrown and false value returned if script
     // message handler was not registered.
     ASSERT_FALSE([result boolValue]);
@@ -200,8 +207,10 @@
 
     ASSERT_TRUE(LoadHtml("<p>"));
 
-    id result =
-        ExecuteJavaScript(WKContentWorld.pageWorld, GetPostMessageScript());
+    WKWebView* web_view =
+        [web::test::GetWebController(web_state()) ensureWebViewCreated];
+    id result = web::test::ExecuteJavaScript(web_view, WKContentWorld.pageWorld,
+                                             GetPostMessageScript());
     // JavaScript exception should be thrown and false value returned if script
     // message handler was not registered.
     ASSERT_FALSE([result boolValue]);
diff --git a/ios/web/js_messaging/web_frame_impl_inttest.mm b/ios/web/js_messaging/web_frame_impl_inttest.mm
index 48be4ec5..a7d16a01 100644
--- a/ios/web/js_messaging/web_frame_impl_inttest.mm
+++ b/ios/web/js_messaging/web_frame_impl_inttest.mm
@@ -13,8 +13,11 @@
 #import "ios/web/js_messaging/java_script_content_world.h"
 #include "ios/web/js_messaging/page_script_util.h"
 #import "ios/web/public/js_messaging/web_frames_manager.h"
+#import "ios/web/public/test/web_state_test_util.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
 #import "ios/web/public/web_state.h"
+#import "ios/web/test/js_test_util_internal.h"
+#import "ios/web/web_state/ui/crw_web_controller.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
 
@@ -311,11 +314,13 @@
   ASSERT_TRUE(LoadHtml("<p>"));
 
   if (@available(ios 14, *)) {
-    ExecuteJavaScript(WKContentWorld.defaultClientWorld,
-                      @"__gCrWeb = {};"
-                      @"__gCrWeb['fakeFunction'] = function() {"
-                      @"  return '10';"
-                      @"}");
+    WKWebView* web_view =
+        [web::test::GetWebController(web_state()) ensureWebViewCreated];
+    test::ExecuteJavaScript(web_view, WKContentWorld.defaultClientWorld,
+                            @"__gCrWeb = {};"
+                            @"__gCrWeb['fakeFunction'] = function() {"
+                            @"  return '10';"
+                            @"}");
   }
 
   web::WebFrameImpl* main_frame_impl = static_cast<web::WebFrameImpl*>(
diff --git a/ios/web/public/test/BUILD.gn b/ios/web/public/test/BUILD.gn
index c886b3b..d40da66 100644
--- a/ios/web/public/test/BUILD.gn
+++ b/ios/web/public/test/BUILD.gn
@@ -22,6 +22,7 @@
     "//ios/web/navigation",
     "//ios/web/navigation:wk_navigation_util",
     "//ios/web/public/deprecated",
+    "//ios/web/test:js_test_util_internal",
     "//ios/web/test:test_support",
     "//ui/base",
   ]
diff --git a/ios/web/public/test/js_test_util.h b/ios/web/public/test/js_test_util.h
index 382714de..afd2e14f 100644
--- a/ios/web/public/test/js_test_util.h
+++ b/ios/web/public/test/js_test_util.h
@@ -29,22 +29,6 @@
 // Fails if there was an error during script execution.
 id ExecuteJavaScript(WKWebView* web_view, NSString* script);
 
-#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
-// Executes JavaScript in |frame| within |content_world| and returns the result
-// as an id. |error| can be null and will be updated only if script execution
-// fails.
-id ExecuteJavaScript(WKWebView* web_view,
-                     WKContentWorld* content_world,
-                     NSString* script,
-                     NSError* __autoreleasing* error) API_AVAILABLE(ios(14.0));
-
-// Executes JavaScript in |frame| and returns the result as an id.
-// Fails if there was an error during script execution.
-id ExecuteJavaScript(WKWebView* web_view,
-                     WKContentWorld* content_world,
-                     NSString* script) API_AVAILABLE(ios(14.0));
-#endif  // defined(__IPHONE14_0)
-
 // Synchronously loads |html| into |web_view|. Returns true is successful or
 // false if the |web_view| never finishes loading.
 bool LoadHtml(WKWebView* web_view,
diff --git a/ios/web/public/test/js_test_util.mm b/ios/web/public/test/js_test_util.mm
index 2f3b66b..3780c06 100644
--- a/ios/web/public/test/js_test_util.mm
+++ b/ios/web/public/test/js_test_util.mm
@@ -55,45 +55,6 @@
   return result;
 }
 
-#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
-id ExecuteJavaScript(WKWebView* web_view,
-                     WKContentWorld* content_world,
-                     NSString* script) {
-  return ExecuteJavaScript(web_view, content_world, script, /*error=*/nil);
-}
-
-id ExecuteJavaScript(WKWebView* web_view,
-                     WKContentWorld* content_world,
-                     NSString* script,
-                     NSError* __autoreleasing* error) {
-  __block id result;
-  __block bool completed = false;
-  __block NSError* block_error = nil;
-  SCOPED_TRACE(base::SysNSStringToUTF8(script));
-  [web_view evaluateJavaScript:script
-                       inFrame:nil
-                inContentWorld:content_world
-             completionHandler:^(id script_result, NSError* script_error) {
-               result = [script_result copy];
-               block_error = [script_error copy];
-               completed = true;
-             }];
-  BOOL success = WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
-    return completed;
-  });
-  // Log stack trace to provide some context.
-  EXPECT_TRUE(success)
-      << base::SysNSStringToUTF8(block_error.description)
-      << "\nWKWebView failed to complete javascript execution.\n"
-      << base::SysNSStringToUTF8(
-             [[NSThread callStackSymbols] componentsJoinedByString:@"\n"]);
-  if (error) {
-    *error = block_error;
-  }
-  return result;
-}
-#endif  // defined(__IPHONE14_0)
-
 bool LoadHtml(WKWebView* web_view, NSString* html, NSURL* base_url) {
   [web_view loadHTMLString:html baseURL:base_url];
 
diff --git a/ios/web/public/test/web_test_with_web_state.h b/ios/web/public/test/web_test_with_web_state.h
index e15bcab1..c5e0a4c7 100644
--- a/ios/web/public/test/web_test_with_web_state.h
+++ b/ios/web/public/test/web_test_with_web_state.h
@@ -14,8 +14,6 @@
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
 
-@class WKContentWorld;
-
 namespace web {
 
 class JavaScriptFeature;
@@ -78,11 +76,6 @@
   // |feature| and returns the result as id.
   id ExecuteJavaScriptForFeature(NSString* script, JavaScriptFeature* feature)
       API_AVAILABLE(ios(14.0));
-#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
-  // Synchronously executes |script| in |content_world| and returns result.
-  id ExecuteJavaScript(WKContentWorld* content_world, NSString* script)
-      API_AVAILABLE(ios(14.0));
-#endif  // defined(__IPHONE14_0)
 
   // Returns the base URL of the loaded page.
   std::string BaseUrl() const;
diff --git a/ios/web/public/test/web_test_with_web_state.mm b/ios/web/public/test/web_test_with_web_state.mm
index e0f8541..8a55760 100644
--- a/ios/web/public/test/web_test_with_web_state.mm
+++ b/ios/web/public/test/web_test_with_web_state.mm
@@ -19,6 +19,7 @@
 #import "ios/web/public/test/web_view_interaction_test_util.h"
 #import "ios/web/public/web_client.h"
 #include "ios/web/public/web_state_observer.h"
+#import "ios/web/test/js_test_util_internal.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
 #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
 #import "ios/web/web_state/web_state_impl.h"
@@ -30,6 +31,7 @@
 
 using base::test::ios::WaitUntilConditionOrTimeout;
 using base::test::ios::kWaitForActionTimeout;
+using base::test::ios::kWaitForJSCompletionTimeout;
 using base::test::ios::kWaitForPageLoadTimeout;
 
 namespace web {
@@ -187,7 +189,17 @@
       JavaScriptFeatureManager::FromBrowserState(GetBrowserState());
   JavaScriptContentWorld* world =
       feature_manager->GetContentWorldForFeature(feature);
-  return ExecuteJavaScript(world->GetWKContentWorld(), script);
+  // JS execution in particular content worlds is only available on iOS 14+.
+  DCHECK(base::ios::IsRunningOnIOS14OrLater());
+
+  if (@available(ios 14, *)) {
+    WKWebView* web_view =
+        [web::test::GetWebController(web_state()) ensureWebViewCreated];
+    return web::test::ExecuteJavaScript(web_view, world->GetWKContentWorld(),
+                                        script);
+  }
+
+  return nil;
 }
 
 id WebTestWithWebState::ExecuteJavaScript(NSString* script) {
@@ -195,23 +207,6 @@
   return web::test::ExecuteJavaScript(script, web_state());
 }
 
-#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
-// Synchronously executes |script| in |content_world| and returns result.
-id WebTestWithWebState::ExecuteJavaScript(WKContentWorld* content_world,
-                                          NSString* script) {
-  DCHECK(base::ios::IsRunningOnIOS14OrLater());
-
-  WKWebView* web_view =
-      [web::test::GetWebController(web_state()) ensureWebViewCreated];
-
-  if (@available(ios 14, *)) {
-    return web::test::ExecuteJavaScript(web_view, content_world, script);
-  }
-
-  return nil;
-}
-#endif  // defined(__IPHONE14_0)
-
 void WebTestWithWebState::DestroyWebState() {
   web_state_.reset();
 }
diff --git a/ios/web/test/BUILD.gn b/ios/web/test/BUILD.gn
index 4ed8b80..a53b82c 100644
--- a/ios/web/test/BUILD.gn
+++ b/ios/web/test/BUILD.gn
@@ -84,6 +84,18 @@
   ]
 }
 
+source_set("js_test_util_internal") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+
+  deps = [ "//base/test:test_support" ]
+
+  sources = [
+    "js_test_util_internal.h",
+    "js_test_util_internal.mm",
+  ]
+}
+
 source_set("test_constants") {
   testonly = true
   sources = [
diff --git a/ios/web/test/js_test_util_internal.h b/ios/web/test/js_test_util_internal.h
new file mode 100644
index 0000000..5ec9b12
--- /dev/null
+++ b/ios/web/test/js_test_util_internal.h
@@ -0,0 +1,27 @@
+// 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 IOS_WEB_TEST_JS_TEST_UTIL_INTERNAL_H_
+#define IOS_WEB_TEST_JS_TEST_UTIL_INTERNAL_H_
+
+#import <Foundation/Foundation.h>
+#import <WebKit/WebKit.h>
+
+namespace web {
+namespace test {
+
+#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
+// Synchronously executes |script| in |content_world| and returns result.
+// NOTE: Generally, tests should not deal with raw WKContentWorlds. Instead,
+// prefer specifying the associated JavaScriptFeature instance using
+// WebTestWithWebState::ExecuteJavaScriptForFeature.
+id ExecuteJavaScript(WKWebView* web_view,
+                     WKContentWorld* content_world,
+                     NSString* script) API_AVAILABLE(ios(14.0));
+#endif  // defined(__IPHONE14_0)
+
+}  // namespace test
+}  // namespace web
+
+#endif  // IOS_WEB_TEST_JS_TEST_UTIL_INTERNAL_H_
diff --git a/ios/web/test/js_test_util_internal.mm b/ios/web/test/js_test_util_internal.mm
new file mode 100644
index 0000000..b688767
--- /dev/null
+++ b/ios/web/test/js_test_util_internal.mm
@@ -0,0 +1,51 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web/test/js_test_util_internal.h"
+
+#import <WebKit/WebKit.h>
+
+#include "base/strings/sys_string_conversions.h"
+#import "base/test/ios/wait_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using base::test::ios::kWaitForJSCompletionTimeout;
+using base::test::ios::WaitUntilConditionOrTimeout;
+
+namespace web {
+namespace test {
+
+id ExecuteJavaScript(WKWebView* web_view,
+                     WKContentWorld* content_world,
+                     NSString* script) {
+  __block id result;
+  __block bool completed = false;
+  __block NSError* block_error = nil;
+  SCOPED_TRACE(base::SysNSStringToUTF8(script));
+  [web_view evaluateJavaScript:script
+                       inFrame:nil
+                inContentWorld:content_world
+             completionHandler:^(id script_result, NSError* script_error) {
+               result = [script_result copy];
+               block_error = [script_error copy];
+               completed = true;
+             }];
+  BOOL success = WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
+    return completed;
+  });
+  // Log stack trace to provide some context.
+  EXPECT_TRUE(success)
+      << base::SysNSStringToUTF8(block_error.description)
+      << "\nWKWebView failed to complete javascript execution.\n"
+      << base::SysNSStringToUTF8(
+             [[NSThread callStackSymbols] componentsJoinedByString:@"\n"]);
+  return result;
+}
+
+}  // namespace test
+}  // namespace web
diff --git a/media/capture/mojom/video_capture.mojom b/media/capture/mojom/video_capture.mojom
index 32675d2..a17142ad 100644
--- a/media/capture/mojom/video_capture.mojom
+++ b/media/capture/mojom/video_capture.mojom
@@ -55,15 +55,20 @@
   PAUSED,
   RESUMED,
   STOPPED,
-  FAILED,
   ENDED,
 };
 
+union VideoCaptureResult {
+  VideoCaptureState state;
+  media.mojom.VideoCaptureError error_code;
+};
+
 // Interface for notifications from Browser/Host back to Renderer/Client. This
 // interface is used between VideoCaptureHost.Start() and Stop().
 interface VideoCaptureObserver {
-  // Gets notified about a VideoCaptureState update.
-  OnStateChanged(VideoCaptureState state);
+  // Gets notified about a VideoCaptureState update or a VideoCaptureError that
+  // might have occurred during the capture.
+  OnStateChanged(VideoCaptureResult result);
 
   // Registers a |buffer_handle| at the Renderer/Client using the given
   // |buffer_id|. The Browser/Host may subsequently use |buffer_id| to share
diff --git a/media/capture/mojom/video_capture_types.mojom b/media/capture/mojom/video_capture_types.mojom
index 5f9c1499..573afbb 100644
--- a/media/capture/mojom/video_capture_types.mojom
+++ b/media/capture/mojom/video_capture_types.mojom
@@ -239,6 +239,7 @@
   kDesktopCaptureDeviceMacFailedStreamStart,
   kCrosHalV3BufferManagerFailedToReserveBuffers,
   [MinVersion=1] kWinMediaFoundationSystemPermissionDenied,
+  [MinVersion=2] kVideoCaptureImplTimedOutOnStart,
 };
 
 [Stable, Extensible]
diff --git a/media/capture/mojom/video_capture_types_mojom_traits.cc b/media/capture/mojom/video_capture_types_mojom_traits.cc
index a75d6fa..bfca3075 100644
--- a/media/capture/mojom/video_capture_types_mojom_traits.cc
+++ b/media/capture/mojom/video_capture_types_mojom_traits.cc
@@ -721,6 +721,8 @@
     case media::VideoCaptureError::kWinMediaFoundationSystemPermissionDenied:
       return media::mojom::VideoCaptureError::
           kWinMediaFoundationSystemPermissionDenied;
+    case media::VideoCaptureError::kVideoCaptureImplTimedOutOnStart:
+      return media::mojom::VideoCaptureError::kVideoCaptureImplTimedOutOnStart;
   }
   NOTREACHED();
   return media::mojom::VideoCaptureError::kNone;
@@ -1288,6 +1290,9 @@
       *output =
           media::VideoCaptureError::kWinMediaFoundationSystemPermissionDenied;
       return true;
+    case media::mojom::VideoCaptureError::kVideoCaptureImplTimedOutOnStart:
+      *output = media::VideoCaptureError::kVideoCaptureImplTimedOutOnStart;
+      return true;
   }
   NOTREACHED();
   return false;
diff --git a/media/capture/video_capture_types.h b/media/capture/video_capture_types.h
index 14ba6d4..c3ca9403 100644
--- a/media/capture/video_capture_types.h
+++ b/media/capture/video_capture_types.h
@@ -193,7 +193,8 @@
   kDesktopCaptureDeviceMacFailedStreamStart = 125,
   kCrosHalV3BufferManagerFailedToReserveBuffers = 126,
   kWinMediaFoundationSystemPermissionDenied = 127,
-  kMaxValue = 127
+  kVideoCaptureImplTimedOutOnStart = 128,
+  kMaxValue = 128
 };
 
 // WARNING: Do not change the values assigned to the entries. They are used for
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.h b/media/gpu/v4l2/v4l2_video_decode_accelerator.h
index 33912eb..bcb9e3e7 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.h
@@ -315,7 +315,7 @@
   void NofityFlushDone();
   // Returns true if VIDIOC_DECODER_CMD is supported.
   bool IsDecoderCmdSupported();
-  // Send V4L2_DEC_CMD_START to the driver. Return true if success.
+  // Send V4L2_DEC_CMD_STOP to the driver. Return true if success.
   bool SendDecoderCmdStop();
 
   // Reset() task.  Drop all input buffers. If V4L2VDA is not doing resolution
diff --git a/mojo/core/node_controller.cc b/mojo/core/node_controller.cc
index 51f6df2..420407c 100644
--- a/mojo/core/node_controller.cc
+++ b/mojo/core/node_controller.cc
@@ -769,7 +769,7 @@
                                   ports::ScopedEvent event) {
   DCHECK(event);
   if (node == name_)
-    node_->AcceptEvent(std::move(event));
+    node_->AcceptEvent(name_, std::move(event));
   else
     SendPeerEvent(node, std::move(event));
 
@@ -1071,7 +1071,7 @@
     return;
   }
 
-  node_->AcceptEvent(std::move(event));
+  node_->AcceptEvent(from_node, std::move(event));
 
   AttemptShutdownIfRequested();
 }
@@ -1207,7 +1207,7 @@
   for (auto& iter : peers_) {
     // Clone and send the event to each known peer. Events which cannot be
     // cloned cannot be broadcast.
-    ports::ScopedEvent clone = event->Clone();
+    ports::ScopedEvent clone = event->CloneForBroadcast();
     if (!clone) {
       DVLOG(1) << "Ignoring request to broadcast invalid event from "
                << from_node << " [type=" << static_cast<uint32_t>(event->type())
diff --git a/mojo/core/ports/event.cc b/mojo/core/ports/event.cc
index bba2e96..d7897bc45 100644
--- a/mojo/core/ports/event.cc
+++ b/mojo/core/ports/event.cc
@@ -150,7 +150,7 @@
   SerializeData(header + 1);
 }
 
-ScopedEvent Event::Clone() const {
+ScopedEvent Event::CloneForBroadcast() const {
   return nullptr;
 }
 
@@ -299,7 +299,12 @@
   data->proxy_target_port_name = proxy_target_port_name_;
 }
 
-ScopedEvent ObserveProxyEvent::Clone() const {
+ScopedEvent ObserveProxyEvent::CloneForBroadcast() const {
+  // Don't broadcast events targeted at specific ports. Otherwise a malicioius
+  // node can use this to bypass sender verification.
+  if (port_name() != kInvalidPortName) {
+    return nullptr;
+  }
   return std::make_unique<ObserveProxyEvent>(
       port_name(), proxy_node_name_, proxy_port_name_, proxy_target_node_name_,
       proxy_target_port_name_);
@@ -333,11 +338,6 @@
   data->last_sequence_num = last_sequence_num_;
 }
 
-ScopedEvent ObserveProxyAckEvent::Clone() const {
-  return std::make_unique<ObserveProxyAckEvent>(port_name(),
-                                                last_sequence_num_);
-}
-
 ObserveClosureEvent::ObserveClosureEvent(const PortName& port_name,
                                          uint64_t last_sequence_num)
     : Event(Type::kObserveClosure, port_name),
@@ -366,10 +366,6 @@
   data->last_sequence_num = last_sequence_num_;
 }
 
-ScopedEvent ObserveClosureEvent::Clone() const {
-  return std::make_unique<ObserveClosureEvent>(port_name(), last_sequence_num_);
-}
-
 MergePortEvent::MergePortEvent(const PortName& port_name,
                                const PortName& new_port_name,
                                const PortDescriptor& new_port_descriptor)
diff --git a/mojo/core/ports/event.h b/mojo/core/ports/event.h
index 4e97b01c..aef207d 100644
--- a/mojo/core/ports/event.h
+++ b/mojo/core/ports/event.h
@@ -98,7 +98,7 @@
 
   size_t GetSerializedSize() const;
   void Serialize(void* buffer) const;
-  virtual ScopedEvent Clone() const;
+  virtual ScopedEvent CloneForBroadcast() const;
 
  protected:
   Event(Type type, const PortName& port_name);
@@ -212,7 +212,7 @@
  private:
   size_t GetSerializedDataSize() const override;
   void SerializeData(void* buffer) const override;
-  ScopedEvent Clone() const override;
+  ScopedEvent CloneForBroadcast() const override;
 
   const NodeName proxy_node_name_;
   const PortName proxy_port_name_;
@@ -238,7 +238,6 @@
  private:
   size_t GetSerializedDataSize() const override;
   void SerializeData(void* buffer) const override;
-  ScopedEvent Clone() const override;
 
   const uint64_t last_sequence_num_;
 };
@@ -264,7 +263,6 @@
  private:
   size_t GetSerializedDataSize() const override;
   void SerializeData(void* buffer) const override;
-  ScopedEvent Clone() const override;
 
   uint64_t last_sequence_num_;
 };
diff --git a/mojo/core/ports/node.cc b/mojo/core/ports/node.cc
index ae4c2a2..e9c0b35 100644
--- a/mojo/core/ports/node.cc
+++ b/mojo/core/ports/node.cc
@@ -431,10 +431,10 @@
   return OK;
 }
 
-int Node::AcceptEvent(ScopedEvent event) {
+int Node::AcceptEvent(const NodeName& from_node, ScopedEvent event) {
   switch (event->type()) {
     case Event::Type::kUserMessage:
-      return OnUserMessage(Event::Cast<UserMessageEvent>(&event));
+      return OnUserMessage(from_node, Event::Cast<UserMessageEvent>(&event));
     case Event::Type::kPortAccepted:
       return OnPortAccepted(Event::Cast<PortAcceptedEvent>(&event));
     case Event::Type::kObserveProxy:
@@ -510,7 +510,8 @@
   return OK;
 }
 
-int Node::OnUserMessage(std::unique_ptr<UserMessageEvent> message) {
+int Node::OnUserMessage(const NodeName& from_node,
+                        std::unique_ptr<UserMessageEvent> message) {
   PortName port_name = message->port_name();
 
 #if DCHECK_IS_ON()
@@ -531,23 +532,12 @@
   // this node. When the message is forwarded, these ports will get transferred
   // following the usual method. If the message cannot be accepted, then the
   // newly bound ports will simply be closed.
-  for (size_t i = 0; i < message->num_ports(); ++i) {
-    Event::PortDescriptor& descriptor = message->port_descriptors()[i];
-    if (descriptor.referring_node_name == kInvalidNodeName) {
-      // If the referring node name is invalid, this descriptor can be ignored
-      // and the port should already exist locally.
-      PortRef port_ref;
-      if (GetPort(message->ports()[i], &port_ref) != OK)
-        return ERROR_PORT_UNKNOWN;
-    } else {
+  if (from_node != name_) {
+    for (size_t i = 0; i < message->num_ports(); ++i) {
+      Event::PortDescriptor& descriptor = message->port_descriptors()[i];
       int rv = AcceptPort(message->ports()[i], descriptor);
       if (rv != OK)
         return rv;
-
-      // Ensure that the referring node is wiped out of this descriptor. This
-      // allows the event to be forwarded across multiple local hops without
-      // attempting to accept the port more than once.
-      descriptor.referring_node_name = kInvalidNodeName;
     }
   }
 
@@ -697,7 +687,11 @@
       // port referring to the proxy.
       event_target_node = port->peer_node_name;
       event->set_port_name(port->peer_port_name);
-      event_to_forward = std::move(event);
+      if (port->state == Port::kBuffering) {
+        port->control_message_queue.push({event_target_node, std::move(event)});
+      } else {
+        event_to_forward = std::move(event);
+      }
     }
   }
 
@@ -765,7 +759,6 @@
 
   bool notify_delegate = false;
   NodeName peer_node_name;
-  PortName peer_port_name;
   bool try_remove_proxy = false;
   {
     SinglePortLocker locker(&port_ref);
@@ -814,15 +807,19 @@
              << port->peer_node_name
              << " (last_sequence_num=" << event->last_sequence_num() << ")";
 
+    event->set_port_name(port->peer_port_name);
     peer_node_name = port->peer_node_name;
-    peer_port_name = port->peer_port_name;
+
+    if (port->state == Port::kBuffering) {
+      port->control_message_queue.push({peer_node_name, std::move(event)});
+    }
   }
 
   if (try_remove_proxy)
     TryRemoveProxy(port_ref);
 
-  event->set_port_name(peer_port_name);
-  delegate_->ForwardEvent(peer_node_name, std::move(event));
+  if (event)
+    delegate_->ForwardEvent(peer_node_name, std::move(event));
 
   if (notify_delegate)
     delegate_->PortStatusChanged(port_ref);
@@ -899,6 +896,11 @@
         event_to_send = std::make_unique<UserMessageReadAckEvent>(
             port->peer_port_name, current_sequence_num);
 
+        if (port->state == Port::kBuffering) {
+          port->control_message_queue.push(
+              {peer_node_name, std::move(event_to_send)});
+        }
+
         // This might be a late or duplicate acknowledge request, that's
         // requesting acknowledge for an already read message. There may already
         // have been a request for future reads, so take care not to back up
@@ -1040,7 +1042,7 @@
     return OK;
   }
 
-  int accept_result = AcceptEvent(std::move(m));
+  int accept_result = AcceptEvent(name_, std::move(m));
   if (accept_result != OK) {
     // See comment above for why we don't return an error in this case.
     DVLOG(2) << "AcceptEvent failed: " << accept_result;
@@ -1372,12 +1374,21 @@
 }
 
 int Node::BeginProxying(const PortRef& port_ref) {
+  base::queue<std::pair<NodeName, ScopedEvent>> control_message_queue;
   {
     SinglePortLocker locker(&port_ref);
     auto* port = locker.port();
     if (port->state != Port::kBuffering)
       return OOPS(ERROR_PORT_STATE_UNEXPECTED);
     port->state = Port::kProxying;
+    std::swap(port->control_message_queue, control_message_queue);
+  }
+
+  while (!control_message_queue.empty()) {
+    auto node_event_pair = std::move(control_message_queue.front());
+    control_message_queue.pop();
+    delegate_->ForwardEvent(node_event_pair.first,
+                            std::move(node_event_pair.second));
   }
 
   int rv = ForwardUserMessagesFromProxy(port_ref);
@@ -1388,8 +1399,6 @@
   MaybeForwardAckRequest(port_ref);
 
   bool try_remove_proxy_immediately;
-  ScopedEvent closure_event;
-  NodeName closure_target_node;
   {
     SinglePortLocker locker(&port_ref);
     auto* port = locker.port();
@@ -1397,17 +1406,10 @@
       return OOPS(ERROR_PORT_STATE_UNEXPECTED);
 
     try_remove_proxy_immediately = port->remove_proxy_on_last_message;
-    if (try_remove_proxy_immediately) {
-      // Make sure we propagate closure to our current peer.
-      closure_target_node = port->peer_node_name;
-      closure_event = std::make_unique<ObserveClosureEvent>(
-          port->peer_port_name, port->last_sequence_num_to_receive);
-    }
   }
 
   if (try_remove_proxy_immediately) {
     TryRemoveProxy(port_ref);
-    delegate_->ForwardEvent(closure_target_node, std::move(closure_event));
   } else {
     InitiateProxyRemoval(port_ref);
   }
diff --git a/mojo/core/ports/node.h b/mojo/core/ports/node.h
index 053c57d..b19a1b6 100644
--- a/mojo/core/ports/node.h
+++ b/mojo/core/ports/node.h
@@ -156,7 +156,7 @@
       uint64_t sequence_number_acknowledge_interval);
 
   // Corresponding to NodeDelegate::ForwardEvent.
-  int AcceptEvent(ScopedEvent event);
+  int AcceptEvent(const NodeName& from_node, ScopedEvent event);
 
   // Called to merge two ports with each other. If you have two independent
   // port pairs A <=> B and C <=> D, the net result of merging B and C is a
@@ -211,7 +211,8 @@
     const raw_ptr<NodeDelegate> delegate_;
   };
 
-  int OnUserMessage(std::unique_ptr<UserMessageEvent> message);
+  int OnUserMessage(const NodeName& from_node,
+                    std::unique_ptr<UserMessageEvent> message);
   int OnPortAccepted(std::unique_ptr<PortAcceptedEvent> event);
   int OnObserveProxy(std::unique_ptr<ObserveProxyEvent> event);
   int OnObserveProxyAck(std::unique_ptr<ObserveProxyAckEvent> event);
diff --git a/mojo/core/ports/port.h b/mojo/core/ports/port.h
index 73493c0..39a9eab 100644
--- a/mojo/core/ports/port.h
+++ b/mojo/core/ports/port.h
@@ -10,6 +10,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/containers/queue.h"
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
 #include "mojo/core/ports/event.h"
@@ -145,6 +146,9 @@
   // in strict sequential order.
   MessageQueue message_queue;
 
+  // Buffer outgoing control messages while this port is in kBuffering state.
+  base::queue<std::pair<NodeName, ScopedEvent>> control_message_queue;
+
   // In some edge cases, a Node may need to remember to route a single special
   // event upon destruction of this (proxying) Port. That event is stashed here
   // in the interim.
diff --git a/mojo/core/ports/ports_unittest.cc b/mojo/core/ports/ports_unittest.cc
index f5bfe40..404c938 100644
--- a/mojo/core/ports/ports_unittest.cc
+++ b/mojo/core/ports/ports_unittest.cc
@@ -197,13 +197,13 @@
     return true;
   }
 
-  void EnqueueEvent(ScopedEvent event) {
+  void EnqueueEvent(const NodeName& from_node, ScopedEvent event) {
     idle_event_.Reset();
 
     // NOTE: This may be called from ForwardMessage and thus must not reenter
     // |node_|.
     base::AutoLock lock(lock_);
-    incoming_events_.emplace(std::move(event));
+    incoming_events_.push({from_node, std::move(event)});
     events_available_event_.Signal();
   }
 
@@ -279,20 +279,21 @@
       dispatching_ = true;
       while (!incoming_events_.empty()) {
         if (block_on_event_ &&
-            incoming_events_.front()->type() == blocked_event_type_) {
+            incoming_events_.front().second->type() == blocked_event_type_) {
           blocked_ = true;
           // Go idle if we hit a blocked event type.
           break;
         } else {
           blocked_ = false;
         }
-        ScopedEvent event = std::move(incoming_events_.front());
+        auto node_event_pair = std::move(incoming_events_.front());
         incoming_events_.pop();
 
         // NOTE: AcceptMessage() can re-enter this object to call any of the
         // NodeDelegate interface methods.
         base::AutoUnlock unlock(lock_);
-        node_.AcceptEvent(std::move(event));
+        node_.AcceptEvent(node_event_pair.first,
+                          std::move(node_event_pair.second));
       }
 
       dispatching_ = false;
@@ -319,7 +320,7 @@
   bool blocked_ = false;
   bool block_on_event_ = false;
   Event::Type blocked_event_type_;
-  base::queue<ScopedEvent> incoming_events_;
+  base::queue<std::pair<NodeName, ScopedEvent>> incoming_events_;
   base::queue<ScopedMessage> saved_messages_;
 };
 
@@ -424,7 +425,7 @@
           message_event->GetMessage<TestMessage>()->payload()));
     }
 
-    it->second->EnqueueEvent(std::move(event));
+    it->second->EnqueueEvent(from_node->name(), std::move(event));
   }
 
   void BroadcastEvent(TestNode* from_node, ScopedEvent event) override {
@@ -440,7 +441,7 @@
       // Broadcast doesn't deliver to the local node.
       if (node == from_node)
         continue;
-      node->EnqueueEvent(event->Clone());
+      node->EnqueueEvent(from_node->name(), event->CloneForBroadcast());
     }
   }
 
@@ -1309,7 +1310,7 @@
   EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D));
 
   // Write a message on D and close it.
-  EXPECT_EQ(OK, node0.SendStringMessage(D, "hey"));
+  EXPECT_EQ(OK, node1.SendStringMessage(D, "hey"));
   EXPECT_EQ(OK, node1.node().ClosePort(D));
 
   // Initiate a merge between B and C.
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
index f1e4173..e76d393 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -5,6 +5,7 @@
 import("//third_party/closure_compiler/closure_args.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 import("//third_party/protobuf/proto_library.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
 import("//ui/webui/webui_features.gni")
 
 # TODO(rockot): Maybe we can factor these dependencies out of //mojo. They're
@@ -1884,6 +1885,24 @@
         group(webui_js_target_name) {
         }
       }
+
+      webui_grdp_target_name = "${target_name}_webui_grdp"
+      out_grd = "$target_gen_dir/${target_name}_webui_resources.grdp"
+      grd_prefix = "${target_name}_webui"
+      generate_grd(webui_grdp_target_name) {
+        grd_prefix = grd_prefix
+        out_grd = out_grd
+
+        deps = [ ":$webui_js_target_name" ]
+
+        input_files = []
+        foreach(base_path, output_file_base_paths) {
+          input_files += [ "${base_path}-webui.js" ]
+        }
+
+        input_files_base_dir =
+            rebase_path("$root_gen_dir/mojom-webui", "$root_build_dir")
+      }
     }
   }
   if ((generate_fuzzing || !defined(invoker.cpp_only) || !invoker.cpp_only) &&
diff --git a/pdf/pdfium/pdfium_range.cc b/pdf/pdfium/pdfium_range.cc
index b10d7c6..d7467eaa 100644
--- a/pdf/pdfium/pdfium_range.cc
+++ b/pdf/pdfium/pdfium_range.cc
@@ -31,6 +31,9 @@
 
 PDFiumRange::PDFiumRange(PDFiumPage* page, int char_index, int char_count)
     : page_(page), char_index_(char_index), char_count_(char_count) {
+  // TODO(crbug.com/1279497): Demote this CHECK to a DCHECK after the violating
+  // caller is caught.
+  CHECK(page_);
 #if DCHECK_IS_ON()
   AdjustForBackwardsRange(char_index, char_count);
   DCHECK_LE(char_count, FPDFText_CountChars(page_->GetTextPage()));
diff --git a/pdf/pdfium/pdfium_range.h b/pdf/pdfium/pdfium_range.h
index 31df7291..359d319 100644
--- a/pdf/pdfium/pdfium_range.h
+++ b/pdf/pdfium/pdfium_range.h
@@ -49,6 +49,7 @@
   std::u16string GetText() const;
 
  private:
+  // The page containing the range. Must outlive `this`.
   raw_ptr<PDFiumPage> page_;
   // Index of first character.
   int char_index_;
diff --git a/remoting/host/chromeos/ash_display_util.cc b/remoting/host/chromeos/ash_display_util.cc
index 4a3d928..5edc38a 100644
--- a/remoting/host/chromeos/ash_display_util.cc
+++ b/remoting/host/chromeos/ash_display_util.cc
@@ -42,20 +42,14 @@
   }
 
   const std::vector<display::Display>& GetActiveDisplays() const override {
-    if (!display_manager())
-      return empty_display_list_;
-
-    return display_manager()->active_display_list();
+    return display_manager().active_display_list();
   }
 
   const display::Display* GetDisplayForId(DisplayId display_id) const override {
-    if (!display_manager())
+    if (!display_manager().IsActiveDisplayId(display_id))
       return nullptr;
 
-    if (!display_manager()->IsActiveDisplayId(display_id))
-      return nullptr;
-
-    return &display_manager()->GetDisplayForId(display_id);
+    return &display_manager().GetDisplayForId(display_id);
   }
 
   void TakeScreenshotOfDisplay(DisplayId display_id,
@@ -77,19 +71,20 @@
 
  private:
   const display::Screen* screen() const { return display::Screen::GetScreen(); }
-  // We can not return a const pointer, as the ash shell has no const getter for
-  // the display manager :/
-  ash::Shell* shell() const { return ash::Shell::Get(); }
-  const display::DisplayManager* display_manager() const {
-    if (!shell())
-      return nullptr;
-    return shell()->display_manager();
+  // We can not return a const reference, as the ash shell has no const getter
+  // for the display manager :/
+  ash::Shell& shell() const {
+    auto* shell = ash::Shell::Get();
+    DCHECK(shell);
+    return *shell;
+  }
+  const display::DisplayManager& display_manager() const {
+    const auto* result = shell().display_manager();
+    DCHECK(result);
+    return *result;
   }
   aura::Window* GetRootWindowForId(DisplayId id) {
-    if (!shell())
-      return nullptr;
-
-    return shell()->GetRootWindowForDisplayId(id);
+    return shell().GetRootWindowForDisplayId(id);
   }
 
   const std::vector<display::Display> empty_display_list_;
@@ -116,6 +111,16 @@
   g_instance_for_testing_ = instance;
 }
 
+// static
+int AshDisplayUtil::ScaleFactorToDpi(float scale_factor) {
+  return static_cast<int>(scale_factor * kDefaultDpi);
+}
+
+// static
+int AshDisplayUtil::GetDpi(const display::Display& display) {
+  return ScaleFactorToDpi(display.device_scale_factor());
+}
+
 AshDisplayUtil::~AshDisplayUtil() = default;
 
 }  // namespace remoting
diff --git a/remoting/host/chromeos/ash_display_util.h b/remoting/host/chromeos/ash_display_util.h
index 1b280641..6c2f397 100644
--- a/remoting/host/chromeos/ash_display_util.h
+++ b/remoting/host/chromeos/ash_display_util.h
@@ -28,6 +28,10 @@
   // To unset call this method again with nullptr.
   static void SetInstanceForTesting(AshDisplayUtil* instance);
 
+  // Convert the scale factor to DPI.
+  static int ScaleFactorToDpi(float scale_factor);
+  static int GetDpi(const display::Display& display);
+
   virtual ~AshDisplayUtil();
 
   virtual DisplayId GetPrimaryDisplayId() const = 0;
diff --git a/remoting/host/chromeos/aura_desktop_capturer.cc b/remoting/host/chromeos/aura_desktop_capturer.cc
index a6bfed0..ff4341a 100644
--- a/remoting/host/chromeos/aura_desktop_capturer.cc
+++ b/remoting/host/chromeos/aura_desktop_capturer.cc
@@ -16,6 +16,7 @@
 namespace {
 
 std::unique_ptr<webrtc::DesktopFrame> ToDesktopFrame(
+    int dpi,
     absl::optional<SkBitmap> bitmap) {
   if (!bitmap)
     return nullptr;
@@ -23,6 +24,8 @@
   std::unique_ptr<webrtc::DesktopFrame> frame(SkiaBitmapDesktopFrame::Create(
       std::make_unique<SkBitmap>(std::move(bitmap.value()))));
 
+  frame->set_dpi(webrtc::DesktopVector(dpi, dpi));
+
   // |VideoFramePump| will not encode the frame if |updated_region| is empty.
   const webrtc::DesktopRect& rect = webrtc::DesktopRect::MakeWH(
       frame->size().width(), frame->size().height());
@@ -50,9 +53,17 @@
 
 void AuraDesktopCapturer::CaptureFrame() {
   DCHECK(callback_) << "Call Start() first";
+
+  const display::Display* source = GetSourceDisplay();
+  if (!source) {
+    callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_TEMPORARY,
+                               nullptr);
+    return;
+  }
+
   util_.TakeScreenshotOfDisplay(
       source_display_id_,
-      base::BindOnce(ToDesktopFrame)
+      base::BindOnce(ToDesktopFrame, util_.GetDpi(*source))
           .Then(base::BindOnce(&AuraDesktopCapturer::OnFrameCaptured,
                                weak_factory_.GetWeakPtr())));
 }
@@ -80,4 +91,8 @@
   return true;
 }
 
+const display::Display* AuraDesktopCapturer::GetSourceDisplay() const {
+  return util_.GetDisplayForId(source_display_id_);
+}
+
 }  // namespace remoting
diff --git a/remoting/host/chromeos/aura_desktop_capturer.h b/remoting/host/chromeos/aura_desktop_capturer.h
index 798df1a..7567edce 100644
--- a/remoting/host/chromeos/aura_desktop_capturer.h
+++ b/remoting/host/chromeos/aura_desktop_capturer.h
@@ -38,6 +38,8 @@
   // Called when a copy of the layer is captured.
   void OnFrameCaptured(std::unique_ptr<webrtc::DesktopFrame> frame);
 
+  const display::Display* GetSourceDisplay() const;
+
   AshDisplayUtil& util_;
 
   // Points to the callback passed to webrtc::DesktopCapturer::Start().
diff --git a/remoting/host/chromeos/aura_desktop_capturer_unittest.cc b/remoting/host/chromeos/aura_desktop_capturer_unittest.cc
index c03da51f..68d20e1 100644
--- a/remoting/host/chromeos/aura_desktop_capturer_unittest.cc
+++ b/remoting/host/chromeos/aura_desktop_capturer_unittest.cc
@@ -182,7 +182,8 @@
   EXPECT_THAT(request.display, Eq(111));
 }
 
-TEST_F(AuraDesktopCapturerTest, ShouldSendScreenshotToCapturer) {
+// Disabled: This is causing ASan failures (https://crbug.com/1281670)
+TEST_F(AuraDesktopCapturerTest, DISABLED_ShouldSendScreenshotToCapturer) {
   capturer_.Start(&desktop_capturer_callback());
   capturer_.CaptureFrame();
 
@@ -207,6 +208,41 @@
   EXPECT_FALSE(result.frame->updated_region().is_empty());
 }
 
+TEST_F(AuraDesktopCapturerTest, ShouldSetDpi) {
+  const float scale_factor = 2.5;
+  // scale_factor = dpi / default_dpi (and default_dpi is 96).
+  const int dpi = static_cast<int>(scale_factor * 96);
+
+  display_util().GetPrimaryDisplay().set_device_scale_factor(scale_factor);
+
+  capturer_.Start(&desktop_capturer_callback());
+  capturer_.CaptureFrame();
+
+  display_util().ReplyWithScreenshot(TestBitmap());
+
+  CaptureResult result = desktop_capturer_callback().WaitForResult();
+  ASSERT_THAT(result.frame, NotNull());
+  EXPECT_THAT(result.frame->dpi().x(), Eq(dpi));
+  EXPECT_THAT(result.frame->dpi().y(), Eq(dpi));
+}
+
+TEST_F(AuraDesktopCapturerTest, ShouldNotCrashIfDisplayIsUnavailable) {
+  capturer_.Start(&desktop_capturer_callback());
+
+  capturer_.SelectSource(display_util().GetPrimaryDisplayId());
+
+  // By changing the primary display id, the selected source now no longer
+  // exists.
+  display_util().SetPrimaryDisplayId(666);
+
+  capturer_.CaptureFrame();
+
+  CaptureResult result = desktop_capturer_callback().WaitForResult();
+  ASSERT_THAT(result.result,
+              Eq(webrtc::DesktopCapturer::Result::ERROR_TEMPORARY));
+  ASSERT_THAT(result.frame, IsNull());
+}
+
 TEST_F(AuraDesktopCapturerTest, ShouldReturnTemporaryErrorIfScreenshotFails) {
   capturer_.Start(&desktop_capturer_callback());
   capturer_.CaptureFrame();
diff --git a/remoting/resources/remoting_strings.grd b/remoting/resources/remoting_strings.grd
index 9514a30f..52e0b5f7 100644
--- a/remoting/resources/remoting_strings.grd
+++ b/remoting/resources/remoting_strings.grd
@@ -17,7 +17,7 @@
     <output filename="remoting/resources/de.pak" lang="de" type="data_package"/>
     <output filename="remoting/resources/el.pak" lang="el" type="data_package"/>
     <output filename="remoting/resources/en-GB.pak" lang="en-GB" type="data_package"/>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="remoting/resources/en-US.pak" lang="en" type="data_package"/>
     </if>
     <output filename="remoting/resources/en.pak" lang="en" type="data_package"/>
@@ -40,7 +40,7 @@
     <output filename="remoting/resources/hr.pak" lang="hr" type="data_package"/>
     <output filename="remoting/resources/hu.pak" lang="hu" type="data_package"/>
     <output filename="remoting/resources/id.pak" lang="id" type="data_package"/>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="remoting/resources/is.pak" lang="is" type="data_package"/>
     </if>
     <output filename="remoting/resources/it.pak" lang="it" type="data_package"/>
@@ -95,7 +95,7 @@
     <output filename="remoting/resources/_locales/fa/messages.json" lang="fa" type="chrome_messages_json"/>
     <output filename="remoting/resources/_locales/el/messages.json" lang="el" type="chrome_messages_json"/>
     <output filename="remoting/resources/_locales/en/messages.json" lang="en" type="chrome_messages_json"/>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <!-- lang="en-US" will create an empty json file. -->
       <output filename="remoting/resources/_locales/en_US/messages.json" lang="en" type="chrome_messages_json"/>
     </if>
@@ -118,7 +118,7 @@
     <output filename="remoting/resources/_locales/hr/messages.json" lang="hr" type="chrome_messages_json"/>
     <output filename="remoting/resources/_locales/hu/messages.json" lang="hu" type="chrome_messages_json"/>
     <output filename="remoting/resources/_locales/id/messages.json" lang="id" type="chrome_messages_json"/>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="remoting/resources/_locales/is/messages.json" lang="is" type="chrome_messages_json"/>
     </if>
     <output filename="remoting/resources/_locales/it/messages.json" lang="it" type="chrome_messages_json"/>
diff --git a/sandbox/linux/services/libc_interceptor.cc b/sandbox/linux/services/libc_interceptor.cc
index cc2a5291..0a6e5fc6 100644
--- a/sandbox/linux/services/libc_interceptor.cc
+++ b/sandbox/linux/services/libc_interceptor.cc
@@ -23,14 +23,12 @@
 #include "base/compiler_specific.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
-#include "base/metrics/histogram_functions.h"
 #include "base/pickle.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/posix/global_descriptors.h"
 #include "base/posix/unix_domain_socket.h"
 #include "base/synchronization/lock.h"
 #include "base/time/time.h"
-#include "base/timer/elapsed_timer.h"
 
 namespace sandbox {
 
@@ -122,8 +120,6 @@
                                  struct tm* output,
                                  char* timezone_out,
                                  size_t timezone_out_len) {
-  base::ElapsedTimer timer;
-
   base::Pickle request;
   request.WriteInt(METHOD_LOCALTIME);
   request.WriteString(
@@ -142,11 +138,6 @@
   if (!ReadTimeStruct(&iter, output, timezone_out, timezone_out_len)) {
     memset(output, 0, sizeof(struct tm));
   }
-
-  base::UmaHistogramCustomMicrosecondsTimes(
-      "Linux.ProxyLocaltimeCallToBrowserUs", timer.Elapsed(),
-      base::Microseconds(1), base::Seconds(1),
-      /*buckets=*/50);
 }
 
 // The other side of this call is ProxyLocaltimeCallToBrowser().
diff --git a/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc b/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc
index b511e3cd..93a45538 100644
--- a/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc
+++ b/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc
@@ -109,6 +109,11 @@
     0,
 };
 
+constexpr SandboxConfig kServiceWithJitConfig = {
+    base::span<const char* const>(),
+    kAmbientMarkVmoAsExecutable,
+};
+
 // No-access-to-anything.
 constexpr SandboxConfig kEmptySandboxConfig = {
     base::span<const char* const>(),
@@ -127,6 +132,8 @@
       return &kRendererConfig;
     case sandbox::mojom::Sandbox::kVideoCapture:
       return &kVideoCaptureConfig;
+    case sandbox::mojom::Sandbox::kServiceWithJit:
+      return &kServiceWithJitConfig;
     // Remaining types receive no-access-to-anything.
     case sandbox::mojom::Sandbox::kAudio:
     case sandbox::mojom::Sandbox::kCdm:
diff --git a/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc b/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc
index 75ffefc..62c4551f 100644
--- a/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc
+++ b/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc
@@ -186,6 +186,8 @@
       return std::make_unique<AudioProcessPolicy>();
     case sandbox::mojom::Sandbox::kService:
       return std::make_unique<ServiceProcessPolicy>();
+    case sandbox::mojom::Sandbox::kServiceWithJit:
+      return std::make_unique<ServiceProcessPolicy>();
     case sandbox::mojom::Sandbox::kSpeechRecognition:
       return std::make_unique<SpeechRecognitionProcessPolicy>();
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -259,6 +261,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
     case sandbox::mojom::Sandbox::kAudio:
     case sandbox::mojom::Sandbox::kService:
+    case sandbox::mojom::Sandbox::kServiceWithJit:
     case sandbox::mojom::Sandbox::kSpeechRecognition:
     case sandbox::mojom::Sandbox::kNetwork:
 #if BUILDFLAG(ENABLE_OOP_PRINTING)
diff --git a/sandbox/policy/mac/sandbox_mac.mm b/sandbox/policy/mac/sandbox_mac.mm
index 248f571..34f8b003 100644
--- a/sandbox/policy/mac/sandbox_mac.mm
+++ b/sandbox/policy/mac/sandbox_mac.mm
@@ -85,6 +85,7 @@
       break;
     // kService and kUtility are the same on OS_MAC, so fallthrough.
     case sandbox::mojom::Sandbox::kService:
+    case sandbox::mojom::Sandbox::kServiceWithJit:
     case sandbox::mojom::Sandbox::kUtility:
       profile += kSeatbeltPolicyString_utility;
       break;
diff --git a/sandbox/policy/mojom/sandbox.mojom b/sandbox/policy/mojom/sandbox.mojom
index 6ee006b..916ead93 100644
--- a/sandbox/policy/mojom/sandbox.mojom
+++ b/sandbox/policy/mojom/sandbox.mojom
@@ -16,6 +16,11 @@
   // if possible.
   kService,
 
+  // |kServiceWithJit| hosts computation only services that make use of
+  // dynamic code (e.g. v8 or wasm) but do not need access to OS resources.
+  // Prefer |kService| if possible.
+  kServiceWithJit,
+
   // Hosts generic utilities with limited access to system services.
   // On some platforms, may be slightly less locked down than |kService|.
   // For instance, it allows dynamic code and wider access to APIs on Windows.
diff --git a/sandbox/policy/sandbox_type.cc b/sandbox/policy/sandbox_type.cc
index 2a5385d..3c517f9 100644
--- a/sandbox/policy/sandbox_type.cc
+++ b/sandbox/policy/sandbox_type.cc
@@ -47,6 +47,7 @@
       return false;
     case Sandbox::kRenderer:
     case Sandbox::kService:
+    case Sandbox::kServiceWithJit:
     case Sandbox::kUtility:
     case Sandbox::kGpu:
 #if BUILDFLAG(ENABLE_PLUGINS)
@@ -112,6 +113,7 @@
       break;
 #endif
     case Sandbox::kService:
+    case Sandbox::kServiceWithJit:
     case Sandbox::kUtility:
     case Sandbox::kNetwork:
     case Sandbox::kCdm:
@@ -254,6 +256,8 @@
 #endif
     case Sandbox::kService:
       return switches::kServiceSandbox;
+    case Sandbox::kServiceWithJit:
+      return switches::kServiceSandboxWithJit;
     case Sandbox::kSpeechRecognition:
       return switches::kSpeechRecognitionSandbox;
 #if defined(OS_WIN)
@@ -308,6 +312,8 @@
     return Sandbox::kUtility;
   if (sandbox_string == switches::kServiceSandbox)
     return Sandbox::kService;
+  if (sandbox_string == switches::kServiceSandboxWithJit)
+    return Sandbox::kServiceWithJit;
 
   if (sandbox_string == switches::kNoneSandbox)
     return Sandbox::kNoSandbox;
diff --git a/sandbox/policy/sandbox_type_unittest.cc b/sandbox/policy/sandbox_type_unittest.cc
index 83da610..8f8a5a3 100644
--- a/sandbox/policy/sandbox_type_unittest.cc
+++ b/sandbox/policy/sandbox_type_unittest.cc
@@ -115,6 +115,11 @@
                                    switches::kNoneSandbox);
   EXPECT_EQ(Sandbox::kNoSandbox, SandboxTypeFromCommandLine(command_line14));
 
+  base::CommandLine command_line15(command_line);
+  SetCommandLineFlagsForSandboxType(&command_line15, Sandbox::kServiceWithJit);
+  EXPECT_EQ(Sandbox::kServiceWithJit,
+            SandboxTypeFromCommandLine(command_line15));
+
   command_line.AppendSwitch(switches::kNoSandbox);
   EXPECT_EQ(Sandbox::kNoSandbox, SandboxTypeFromCommandLine(command_line));
 }
diff --git a/sandbox/policy/switches.cc b/sandbox/policy/switches.cc
index c7fa0112..2064e21a 100644
--- a/sandbox/policy/switches.cc
+++ b/sandbox/policy/switches.cc
@@ -35,6 +35,7 @@
 const char kPrintCompositorSandbox[] = "print_compositor";
 const char kAudioSandbox[] = "audio";
 const char kServiceSandbox[] = "service";
+const char kServiceSandboxWithJit[] = "service_with_jit";
 const char kSpeechRecognitionSandbox[] = "speech_recognition";
 const char kVideoCaptureSandbox[] = "video_capture";
 
diff --git a/sandbox/policy/switches.h b/sandbox/policy/switches.h
index 5a4cb0af..5b346a5b1 100644
--- a/sandbox/policy/switches.h
+++ b/sandbox/policy/switches.h
@@ -36,6 +36,7 @@
 SANDBOX_POLICY_EXPORT extern const char kPrintCompositorSandbox[];
 SANDBOX_POLICY_EXPORT extern const char kAudioSandbox[];
 SANDBOX_POLICY_EXPORT extern const char kServiceSandbox[];
+SANDBOX_POLICY_EXPORT extern const char kServiceSandboxWithJit[];
 SANDBOX_POLICY_EXPORT extern const char kSpeechRecognitionSandbox[];
 SANDBOX_POLICY_EXPORT extern const char kVideoCaptureSandbox[];
 
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index d37d2bd..197e66e 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -464,7 +464,7 @@
 
 DuplicateHandleFunctionPtr g_iat_orig_duplicate_handle;
 
-NtQueryObject g_QueryObject = NULL;
+NtQueryObjectFunction g_QueryObject = NULL;
 
 static const char* kDuplicateHandleWarning =
     "You are attempting to duplicate a privileged handle into a sandboxed"
@@ -1266,6 +1266,8 @@
       return "Media Foundation CDM";
     case Sandbox::kService:
       return "Service";
+    case Sandbox::kServiceWithJit:
+      return "Service With Jit";
     case Sandbox::kIconReader:
       return "Icon Reader";
     case Sandbox::kWindowsSystemProxyResolver:
diff --git a/sandbox/win/src/handle_closer.cc b/sandbox/win/src/handle_closer.cc
index 841a815..56e27d208 100644
--- a/sandbox/win/src/handle_closer.cc
+++ b/sandbox/win/src/handle_closer.cc
@@ -11,10 +11,6 @@
 #include "base/check_op.h"
 #include "base/memory/free_deleter.h"
 #include "base/win/windows_version.h"
-#include "sandbox/win/src/interceptors.h"
-#include "sandbox/win/src/internal_types.h"
-#include "sandbox/win/src/nt_internals.h"
-#include "sandbox/win/src/process_thread_interception.h"
 #include "sandbox/win/src/win_utils.h"
 
 namespace {
@@ -157,29 +153,4 @@
   return output <= end;
 }
 
-bool GetHandleName(HANDLE handle, std::wstring* handle_name) {
-  static NtQueryObject QueryObject = nullptr;
-  if (!QueryObject)
-    ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
-
-  ULONG size = MAX_PATH;
-  std::unique_ptr<UNICODE_STRING, base::FreeDeleter> name;
-  NTSTATUS result;
-
-  do {
-    name.reset(static_cast<UNICODE_STRING*>(malloc(size)));
-    DCHECK(name.get());
-    result =
-        QueryObject(handle, ObjectNameInformation, name.get(), size, &size);
-  } while (result == STATUS_INFO_LENGTH_MISMATCH ||
-           result == STATUS_BUFFER_OVERFLOW);
-
-  if (NT_SUCCESS(result) && name->Buffer && name->Length)
-    handle_name->assign(name->Buffer, name->Length / sizeof(wchar_t));
-  else
-    handle_name->clear();
-
-  return NT_SUCCESS(result);
-}
-
 }  // namespace sandbox
diff --git a/sandbox/win/src/handle_closer.h b/sandbox/win/src/handle_closer.h
index 4bfab944..ef885dc 100644
--- a/sandbox/win/src/handle_closer.h
+++ b/sandbox/win/src/handle_closer.h
@@ -72,9 +72,6 @@
   HandleMap handles_to_close_;
 };
 
-// Returns the object manager's name associated with a handle
-bool GetHandleName(HANDLE handle, std::wstring* handle_name);
-
 }  // namespace sandbox
 
 #endif  // SANDBOX_WIN_SRC_HANDLE_CLOSER_H_
diff --git a/sandbox/win/src/handle_closer_agent.cc b/sandbox/win/src/handle_closer_agent.cc
index 045b17a..0b0de36 100644
--- a/sandbox/win/src/handle_closer_agent.cc
+++ b/sandbox/win/src/handle_closer_agent.cc
@@ -6,35 +6,13 @@
 
 #include <limits.h>
 #include <stddef.h>
+#include <cstdint>
 
 #include "base/check.h"
 #include "base/win/static_constants.h"
-#include "sandbox/win/src/nt_internals.h"
+#include "base/win/win_util.h"
 #include "sandbox/win/src/win_utils.h"
 
-namespace {
-
-// Returns type infomation for an NT object. This routine is expected to be
-// called for invalid handles so it catches STATUS_INVALID_HANDLE exceptions
-// that can be generated when handle tracing is enabled.
-NTSTATUS QueryObjectTypeInformation(HANDLE handle, void* buffer, ULONG* size) {
-  static NtQueryObject QueryObject = nullptr;
-  if (!QueryObject)
-    ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
-
-  NTSTATUS status = STATUS_UNSUCCESSFUL;
-  __try {
-    status = QueryObject(handle, ObjectTypeInformation, buffer, *size, size);
-  } __except (GetExceptionCode() == STATUS_INVALID_HANDLE
-                  ? EXCEPTION_EXECUTE_HANDLER
-                  : EXCEPTION_CONTINUE_SEARCH) {
-    status = STATUS_INVALID_HANDLE;
-  }
-  return status;
-}
-
-}  // namespace
-
 namespace sandbox {
 
 // Memory buffer mapped from the parent, with the list of handles.
@@ -177,60 +155,44 @@
   if (GetModuleHandleA(base::win::kApplicationVerifierDllName))
     return true;
 
-  // Set up buffers for the type info and the name.
-  std::vector<BYTE> type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) +
-                                     32 * sizeof(wchar_t));
-  OBJECT_TYPE_INFORMATION* type_info =
-      reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0]));
-  std::wstring handle_name;
-  HANDLE handle = nullptr;
+  uint32_t handle_value = 0;
   int invalid_count = 0;
 
   // Keep incrementing until we hit the number of handles reported by
   // GetProcessHandleCount(). If we hit a very long sequence of invalid
   // handles we assume that we've run past the end of the table.
   while (handle_count && invalid_count < kInvalidHandleThreshold) {
-    reinterpret_cast<size_t&>(handle) += kHandleOffset;
-    NTSTATUS rc;
-
-    // Get the type name, reusing the buffer.
-    ULONG size = static_cast<ULONG>(type_info_buffer.size());
-    rc = QueryObjectTypeInformation(handle, type_info, &size);
-    while (rc == STATUS_INFO_LENGTH_MISMATCH || rc == STATUS_BUFFER_OVERFLOW) {
-      type_info_buffer.resize(size + sizeof(wchar_t));
-      type_info =
-          reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0]));
-      rc = QueryObjectTypeInformation(handle, type_info, &size);
-      // Leave padding for the nul terminator.
-      if (NT_SUCCESS(rc) && size == type_info_buffer.size())
-        rc = STATUS_INFO_LENGTH_MISMATCH;
-    }
-    if (!NT_SUCCESS(rc) || !type_info->Name.Buffer) {
+    handle_value += kHandleOffset;
+    HANDLE handle = base::win::Uint32ToHandle(handle_value);
+    std::wstring type_name;
+    if (!GetTypeNameFromHandle(handle, &type_name)) {
       ++invalid_count;
       continue;
     }
 
     --handle_count;
-    type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0';
-
     // Check if we're looking for this type of handle.
-    HandleMap::iterator result = handles_to_close_.find(type_info->Name.Buffer);
-    if (result != handles_to_close_.end()) {
-      HandleMap::mapped_type& names = result->second;
-      // Empty set means close all handles of this type; otherwise check name.
-      if (!names.empty()) {
-        // Move on to the next handle if this name doesn't match.
-        if (!GetHandleName(handle, &handle_name) || !names.count(handle_name))
-          continue;
-      }
+    HandleMap::iterator result = handles_to_close_.find(type_name);
+    if (result == handles_to_close_.end())
+      continue;
 
-      if (!::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0))
-        return false;
-      if (!::CloseHandle(handle))
-        return false;
-      // Attempt to stuff this handle with a new dummy Event.
-      AttemptToStuffHandleSlot(handle, result->first);
+    HandleMap::mapped_type& names = result->second;
+    // Empty set means close all handles of this type; otherwise check name.
+    if (!names.empty()) {
+      std::wstring handle_name;
+      // Move on to the next handle if this name doesn't match.
+      if (!GetPathFromHandle(handle, &handle_name) ||
+          !names.count(handle_name)) {
+        continue;
+      }
     }
+
+    if (!::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0))
+      return false;
+    if (!::CloseHandle(handle))
+      return false;
+    // Attempt to stuff this handle with a new dummy Event.
+    AttemptToStuffHandleSlot(handle, result->first);
   }
 
   return true;
diff --git a/sandbox/win/src/handle_closer_test.cc b/sandbox/win/src/handle_closer_test.cc
index 7406f775..abfb21c 100644
--- a/sandbox/win/src/handle_closer_test.cc
+++ b/sandbox/win/src/handle_closer_test.cc
@@ -5,6 +5,7 @@
 #include <limits.h>
 #include <stddef.h>
 
+#include "base/strings/string_util_win.h"
 #include "base/strings/stringprintf.h"
 #include "base/win/scoped_handle.h"
 #include "sandbox/win/src/handle_closer_agent.h"
@@ -12,6 +13,7 @@
 #include "sandbox/win/src/sandbox.h"
 #include "sandbox/win/src/sandbox_factory.h"
 #include "sandbox/win/src/target_services.h"
+#include "sandbox/win/src/win_utils.h"
 #include "sandbox/win/tests/common/controller.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -45,25 +47,6 @@
                     nullptr, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, nullptr);
 }
 
-// Returns type infomation for an NT object. This routine is expected to be
-// called for invalid handles so it catches STATUS_INVALID_HANDLE exceptions
-// that can be generated when handle tracing is enabled.
-NTSTATUS QueryObjectTypeInformation(HANDLE handle, void* buffer, ULONG* size) {
-  static NtQueryObject QueryObject = nullptr;
-  if (!QueryObject)
-    ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
-
-  NTSTATUS status = STATUS_UNSUCCESSFUL;
-  __try {
-    status = QueryObject(handle, ObjectTypeInformation, buffer, *size, size);
-  } __except (GetExceptionCode() == STATUS_INVALID_HANDLE
-                  ? EXCEPTION_EXECUTE_HANDLER
-                  : EXCEPTION_CONTINUE_SEARCH) {
-    status = STATUS_INVALID_HANDLE;
-  }
-  return status;
-}
-
 // Used by the thread pool tests.
 HANDLE finish_event;
 const int kWaitCount = 20;
@@ -105,7 +88,7 @@
 
       while (handle_count && invalid_count < kInvalidHandleThreshold) {
         reinterpret_cast<size_t&>(handle) += kHandleOffset;
-        if (GetHandleName(handle, &handle_name)) {
+        if (GetPathFromHandle(handle, &handle_name)) {
           for (int i = 1; i < argc; ++i) {
             if (handle_name == argv[i])
               return should_find ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED;
@@ -144,36 +127,9 @@
 
     case AFTER_REVERT:
       for (HANDLE handle : to_check) {
-        // Set up buffers for the type info and the name.
-        std::vector<BYTE> type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) +
-                                           32 * sizeof(wchar_t));
-        OBJECT_TYPE_INFORMATION* type_info =
-            reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0]));
-        NTSTATUS rc;
-
-        // Get the type name, reusing the buffer.
-        ULONG size = static_cast<ULONG>(type_info_buffer.size());
-        rc = QueryObjectTypeInformation(handle, type_info, &size);
-        while (rc == STATUS_INFO_LENGTH_MISMATCH ||
-               rc == STATUS_BUFFER_OVERFLOW) {
-          type_info_buffer.resize(size + sizeof(wchar_t));
-          type_info = reinterpret_cast<OBJECT_TYPE_INFORMATION*>(
-              &(type_info_buffer[0]));
-          rc = QueryObjectTypeInformation(handle, type_info, &size);
-          // Leave padding for the nul terminator.
-          if (NT_SUCCESS(rc) && size == type_info_buffer.size())
-            rc = STATUS_INFO_LENGTH_MISMATCH;
-        }
-
-        CHECK(NT_SUCCESS(rc));
-        CHECK(type_info->Name.Buffer);
-
-        type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] =
-            L'\0';
-
-        // Should be an Event now.
-        CHECK_EQ(wcslen(type_info->Name.Buffer), 5U);
-        CHECK_EQ(wcscmp(L"Event", type_info->Name.Buffer), 0);
+        std::wstring type_name;
+        CHECK(GetTypeNameFromHandle(handle, &type_name));
+        CHECK(base::EqualsCaseInsensitiveASCII(type_name, L"Event"));
 
         // Should not be able to wait.
         CHECK_EQ(WaitForSingleObject(handle, INFINITE), WAIT_FAILED);
@@ -200,7 +156,7 @@
     std::wstring handle_name;
     base::win::ScopedHandle marker(GetMarkerFile(kExtension));
     CHECK(marker.IsValid());
-    CHECK(sandbox::GetHandleName(marker.Get(), &handle_name));
+    CHECK(GetPathFromHandle(marker.Get(), &handle_name));
     command += (L" ");
     command += handle_name;
   }
@@ -220,7 +176,7 @@
     std::wstring handle_name;
     base::win::ScopedHandle marker(GetMarkerFile(kExtension));
     CHECK(marker.IsValid());
-    CHECK(sandbox::GetHandleName(marker.Get(), &handle_name));
+    CHECK(GetPathFromHandle(marker.Get(), &handle_name));
     CHECK_EQ(policy->AddKernelObjectToClose(L"File", handle_name.c_str()),
              SBOX_ALL_OK);
     command += (L" ");
@@ -241,7 +197,7 @@
     std::wstring handle_name;
     base::win::ScopedHandle marker(GetMarkerFile(kExtension));
     CHECK(marker.IsValid());
-    CHECK(sandbox::GetHandleName(marker.Get(), &handle_name));
+    CHECK(GetPathFromHandle(marker.Get(), &handle_name));
     CHECK_EQ(policy->AddKernelObjectToClose(L"File", handle_name.c_str()),
              SBOX_ALL_OK);
   }
diff --git a/sandbox/win/src/named_pipe_policy_test.cc b/sandbox/win/src/named_pipe_policy_test.cc
index db532d61..3d8db97 100644
--- a/sandbox/win/src/named_pipe_policy_test.cc
+++ b/sandbox/win/src/named_pipe_policy_test.cc
@@ -3,10 +3,10 @@
 // found in the LICENSE file.
 
 #include "base/win/windows_version.h"
-#include "sandbox/win/src/handle_closer.h"
 #include "sandbox/win/src/sandbox.h"
 #include "sandbox/win/src/sandbox_factory.h"
 #include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/win_utils.h"
 #include "sandbox/win/tests/common/controller.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -26,7 +26,7 @@
   // pipe should be in the object namespace after creation.
   if (argc == 2) {
     std::wstring handle_name;
-    if (GetHandleName(pipe, &handle_name)) {
+    if (GetPathFromHandle(pipe, &handle_name)) {
       if (handle_name.compare(0, wcslen(argv[1]), argv[1]) != 0)
         return SBOX_TEST_FAILED;
     } else {
diff --git a/sandbox/win/src/nt_internals.h b/sandbox/win/src/nt_internals.h
index 46d489f..79784aa 100644
--- a/sandbox/win/src/nt_internals.h
+++ b/sandbox/win/src/nt_internals.h
@@ -723,13 +723,6 @@
     IN ULONG SystemInformationLength,
     OUT PULONG ReturnLength);
 
-typedef NTSTATUS(WINAPI* NtQueryObject)(IN HANDLE Handle,
-                                        IN OBJECT_INFORMATION_CLASS
-                                            ObjectInformationClass,
-                                        OUT PVOID ObjectInformation,
-                                        IN ULONG ObjectInformationLength,
-                                        OUT PULONG ReturnLength);
-
 // -----------------------------------------------------------------------
 // Strings
 
diff --git a/sandbox/win/src/sandbox_nt_util.cc b/sandbox/win/src/sandbox_nt_util.cc
index 8ba59ff7..84b5bb6 100644
--- a/sandbox/win/src/sandbox_nt_util.cc
+++ b/sandbox/win/src/sandbox_nt_util.cc
@@ -230,70 +230,6 @@
   return ret;
 }
 
-NTSTATUS AllocAndGetFullPath(
-    HANDLE root,
-    const wchar_t* path,
-    std::unique_ptr<wchar_t, NtAllocDeleter>* full_path) {
-  if (!InitHeap())
-    return STATUS_NO_MEMORY;
-
-  DCHECK_NT(full_path);
-  DCHECK_NT(path);
-  NTSTATUS ret = STATUS_UNSUCCESSFUL;
-  __try {
-    do {
-      static NtQueryObjectFunction NtQueryObject = nullptr;
-      if (!NtQueryObject)
-        ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
-
-      ULONG size = 0;
-      // Query the name information a first time to get the size of the name.
-      ret = NtQueryObject(root, ObjectNameInformation, nullptr, 0, &size);
-
-      std::unique_ptr<OBJECT_NAME_INFORMATION, NtAllocDeleter> handle_name;
-      if (size) {
-        handle_name.reset(reinterpret_cast<OBJECT_NAME_INFORMATION*>(
-            new (NT_ALLOC) BYTE[size]));
-
-        // Query the name information a second time to get the name of the
-        // object referenced by the handle.
-        ret = NtQueryObject(root, ObjectNameInformation, handle_name.get(),
-                            size, &size);
-      }
-
-      if (STATUS_SUCCESS != ret)
-        break;
-
-      // Space for path + '\' + name + '\0'.
-      size_t name_length =
-          handle_name->ObjectName.Length + (wcslen(path) + 2) * sizeof(wchar_t);
-      full_path->reset(new (NT_ALLOC) wchar_t[name_length / sizeof(wchar_t)]);
-      if (!*full_path)
-        break;
-      wchar_t* off = full_path->get();
-      ret = CopyData(off, handle_name->ObjectName.Buffer,
-                     handle_name->ObjectName.Length);
-      if (!NT_SUCCESS(ret))
-        break;
-      off += handle_name->ObjectName.Length / sizeof(wchar_t);
-      *off = L'\\';
-      off += 1;
-      ret = CopyData(off, path, wcslen(path) * sizeof(wchar_t));
-      if (!NT_SUCCESS(ret))
-        break;
-      off += wcslen(path);
-      *off = L'\0';
-    } while (false);
-  } __except (EXCEPTION_EXECUTE_HANDLER) {
-    ret = GetExceptionCode();
-  }
-
-  if (!NT_SUCCESS(ret) && *full_path)
-    full_path->reset(nullptr);
-
-  return ret;
-}
-
 // Hacky code... replace with AllocAndCopyObjectAttributes.
 NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object,
                           std::unique_ptr<wchar_t, NtAllocDeleter>* out_name,
diff --git a/sandbox/win/src/sandbox_nt_util.h b/sandbox/win/src/sandbox_nt_util.h
index f7a91d8..2eddc3f 100644
--- a/sandbox/win/src/sandbox_nt_util.h
+++ b/sandbox/win/src/sandbox_nt_util.h
@@ -117,12 +117,6 @@
                           uint32_t* attributes,
                           HANDLE* root);
 
-// Determine full path name from object root and path.
-NTSTATUS AllocAndGetFullPath(
-    HANDLE root,
-    const wchar_t* path,
-    std::unique_ptr<wchar_t, NtAllocDeleter>* full_path);
-
 // Initializes our ntdll level heap
 bool InitHeap();
 
diff --git a/sandbox/win/src/win_utils.cc b/sandbox/win/src/win_utils.cc
index c8d0702..364b2c3 100644
--- a/sandbox/win/src/win_utils.cc
+++ b/sandbox/win/src/win_utils.cc
@@ -10,6 +10,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <limits>
 #include <map>
 #include <memory>
 #include <string>
@@ -147,6 +148,24 @@
     *path = path->substr(kNTDotPrefixLen);
 }
 
+bool QueryObjectInformation(HANDLE handle,
+                            OBJECT_INFORMATION_CLASS info_class,
+                            std::vector<char>& buffer) {
+  static NtQueryObjectFunction NtQueryObject = nullptr;
+  if (!NtQueryObject)
+    ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
+
+  ULONG size = static_cast<ULONG>(buffer.size());
+  __try {
+    return NT_SUCCESS(
+        NtQueryObject(handle, info_class, buffer.data(), size, &size));
+  } __except (GetExceptionCode() == STATUS_INVALID_HANDLE
+                  ? EXCEPTION_EXECUTE_HANDLER
+                  : EXCEPTION_CONTINUE_SEARCH) {
+    return false;
+  }
+}
+
 }  // namespace
 
 namespace sandbox {
@@ -411,30 +430,13 @@
 }
 
 bool GetPathFromHandle(HANDLE handle, std::wstring* path) {
-  NtQueryObjectFunction NtQueryObject = nullptr;
-  ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
-
-  OBJECT_NAME_INFORMATION initial_buffer;
-  OBJECT_NAME_INFORMATION* name = &initial_buffer;
-  ULONG size = sizeof(initial_buffer);
-  // Query the name information a first time to get the size of the name.
-  // Windows XP requires that the size of the buffer passed in here be != 0.
-  NTSTATUS status =
-      NtQueryObject(handle, ObjectNameInformation, name, size, &size);
-
-  std::unique_ptr<BYTE[]> name_ptr;
-  if (size) {
-    name_ptr.reset(new BYTE[size]);
-    name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(name_ptr.get());
-
-    // Query the name information a second time to get the name of the
-    // object referenced by the handle.
-    status = NtQueryObject(handle, ObjectNameInformation, name, size, &size);
-  }
-
-  if (STATUS_SUCCESS != status)
+  using LengthType = decltype(OBJECT_NAME_INFORMATION::ObjectName.Length);
+  std::vector<char> buffer(sizeof(OBJECT_NAME_INFORMATION) +
+                           std::numeric_limits<LengthType>::max());
+  if (!QueryObjectInformation(handle, ObjectNameInformation, buffer))
     return false;
-
+  OBJECT_NAME_INFORMATION* name =
+      reinterpret_cast<OBJECT_NAME_INFORMATION*>(buffer.data());
   path->assign(name->ObjectName.Buffer,
                name->ObjectName.Length / sizeof(name->ObjectName.Buffer[0]));
   return true;
@@ -451,6 +453,20 @@
   return rv;
 }
 
+bool GetTypeNameFromHandle(HANDLE handle, std::wstring* type_name) {
+  // No typename is currently longer than 32 characters on Windows 11, so use an
+  // upper bound of 128 characters.
+  std::vector<char> buffer(sizeof(OBJECT_TYPE_INFORMATION) +
+                           128 * sizeof(WCHAR));
+  if (!QueryObjectInformation(handle, ObjectTypeInformation, buffer))
+    return false;
+  OBJECT_TYPE_INFORMATION* name =
+      reinterpret_cast<OBJECT_TYPE_INFORMATION*>(buffer.data());
+  type_name->assign(name->Name.Buffer,
+                    name->Name.Length / sizeof(name->Name.Buffer[0]));
+  return true;
+}
+
 bool WriteProtectedChildMemory(HANDLE child_process,
                                void* address,
                                const void* buffer,
diff --git a/sandbox/win/src/win_utils.h b/sandbox/win/src/win_utils.h
index 6f54d20..ce96c0f 100644
--- a/sandbox/win/src/win_utils.h
+++ b/sandbox/win/src/win_utils.h
@@ -69,9 +69,12 @@
 bool GetPathFromHandle(HANDLE handle, std::wstring* path);
 
 // Resolves a win32 path to an nt path using GetPathFromHandle. The path must
-// exist. Returs true if the translation was succesful.
+// exist. Returns true if the translation was successful.
 bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path);
 
+// Resolves a handle to its type name. Returns true if successful.
+bool GetTypeNameFromHandle(HANDLE handle, std::wstring* type_name);
+
 // Translates a reserved key name to its handle.
 // For example "HKEY_LOCAL_MACHINE" returns HKEY_LOCAL_MACHINE.
 // Returns nullptr if the name does not represent any reserved key name.
diff --git a/sandbox/win/src/win_utils_unittest.cc b/sandbox/win/src/win_utils_unittest.cc
index b81721c2..a84db93 100644
--- a/sandbox/win/src/win_utils_unittest.cc
+++ b/sandbox/win/src/win_utils_unittest.cc
@@ -15,6 +15,9 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/path_service.h"
+#include "base/rand_util.h"
+#include "base/strings/string_util_win.h"
+#include "base/strings/stringprintf.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/scoped_process_information.h"
 #include "sandbox/win/src/nt_internals.h"
@@ -58,6 +61,25 @@
   return false;
 }
 
+std::wstring GetRandomName() {
+  return base::StringPrintf(L"chrome_%08X%08X", base::RandUint64(),
+                            base::RandUint64());
+}
+
+void CompareHandlePath(const base::win::ScopedHandle& handle,
+                       const std::wstring& expected_path) {
+  std::wstring path;
+  ASSERT_TRUE(GetPathFromHandle(handle.Get(), &path));
+  EXPECT_TRUE(base::EqualsCaseInsensitiveASCII(path, expected_path));
+}
+
+void CompareHandleType(const base::win::ScopedHandle& handle,
+                       const std::wstring& expected_type) {
+  std::wstring type_name;
+  ASSERT_TRUE(GetTypeNameFromHandle(handle.Get(), &type_name));
+  EXPECT_TRUE(base::EqualsCaseInsensitiveASCII(type_name, expected_type));
+}
+
 }  // namespace
 
 TEST(WinUtils, IsReparsePoint) {
@@ -251,4 +273,27 @@
   // Expected result: "\Device\HarddiskVolumeX\ProgramData\%TEMP%\test_calc.exe"
 }
 
+TEST(WinUtils, GetPathAndTypeFromHandle) {
+  std::wstring invalid_handle;
+  EXPECT_FALSE(GetPathFromHandle(nullptr, &invalid_handle));
+  EXPECT_TRUE(invalid_handle.empty());
+  EXPECT_FALSE(GetTypeNameFromHandle(nullptr, &invalid_handle));
+  EXPECT_TRUE(invalid_handle.empty());
+  std::wstring random_name = GetRandomName();
+  ASSERT_FALSE(random_name.empty());
+  std::wstring event_name = L"Global\\" + random_name;
+  base::win::ScopedHandle event_handle(
+      ::CreateEvent(nullptr, FALSE, FALSE, event_name.c_str()));
+  ASSERT_TRUE(event_handle.IsValid());
+  CompareHandlePath(event_handle, L"\\BaseNamedObjects\\" + random_name);
+  CompareHandleType(event_handle, L"Event");
+  std::wstring pipe_name = L"\\\\.\\pipe\\" + random_name;
+  base::win::ScopedHandle pipe_handle(::CreateNamedPipe(
+      pipe_name.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE,
+      PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_USE_DEFAULT_WAIT, nullptr));
+  ASSERT_TRUE(pipe_handle.IsValid());
+  CompareHandlePath(pipe_handle, L"\\Device\\NamedPipe\\" + random_name);
+  CompareHandleType(pipe_handle, L"File");
+}
+
 }  // namespace sandbox
diff --git a/services/data_decoder/json_parser_impl.cc b/services/data_decoder/json_parser_impl.cc
index 69c6dc6..6c7c4b6 100644
--- a/services/data_decoder/json_parser_impl.cc
+++ b/services/data_decoder/json_parser_impl.cc
@@ -16,9 +16,11 @@
 
 JsonParserImpl::~JsonParserImpl() = default;
 
-void JsonParserImpl::Parse(const std::string& json, ParseCallback callback) {
+void JsonParserImpl::Parse(const std::string& json,
+                           uint32_t options,
+                           ParseCallback callback) {
   base::JSONReader::ValueWithError ret =
-      base::JSONReader::ReadAndReturnValueWithError(json);
+      base::JSONReader::ReadAndReturnValueWithError(json, options);
   if (ret.value) {
     std::move(callback).Run(std::move(ret.value), absl::nullopt);
   } else {
diff --git a/services/data_decoder/json_parser_impl.h b/services/data_decoder/json_parser_impl.h
index addc46ce..3314207 100644
--- a/services/data_decoder/json_parser_impl.h
+++ b/services/data_decoder/json_parser_impl.h
@@ -22,7 +22,9 @@
 
  private:
   // mojom::JsonParser implementation.
-  void Parse(const std::string& json, ParseCallback callback) override;
+  void Parse(const std::string& json,
+             uint32_t options,
+             ParseCallback callback) override;
 };
 
 }  // namespace data_decoder
diff --git a/services/data_decoder/public/cpp/data_decoder.cc b/services/data_decoder/public/cpp/data_decoder.cc
index 14401021..5408a16f 100644
--- a/services/data_decoder/public/cpp/data_decoder.cc
+++ b/services/data_decoder/public/cpp/data_decoder.cc
@@ -5,6 +5,7 @@
 #include "services/data_decoder/public/cpp/data_decoder.h"
 
 #include "base/callback.h"
+#include "base/json/json_reader.h"
 #include "base/memory/ref_counted.h"
 #include "base/no_destructor.h"
 #include "base/task/thread_pool.h"
@@ -15,7 +16,6 @@
 #include "services/data_decoder/public/mojom/xml_parser.mojom.h"
 
 #if defined(OS_ANDROID)
-#include "base/json/json_reader.h"
 #include "services/data_decoder/public/cpp/json_sanitizer.h"
 #endif
 
@@ -188,7 +188,7 @@
           std::move(callback));
   GetService()->BindJsonParser(request->BindRemote());
   request->remote()->Parse(
-      json,
+      json, base::JSON_PARSE_RFC,
       base::BindOnce(&ValueParseRequest<mojom::JsonParser,
                                         base::Value>::OnServiceValueOrError,
                      request));
diff --git a/services/data_decoder/public/cpp/data_decoder.h b/services/data_decoder/public/cpp/data_decoder.h
index c585f56a..0f9c1e34 100644
--- a/services/data_decoder/public/cpp/data_decoder.h
+++ b/services/data_decoder/public/cpp/data_decoder.h
@@ -95,7 +95,7 @@
 
   // Parses the potentially unsafe JSON string in |json| using this
   // DataDecoder's service instance or some other platform-specific decoding
-  // facility.
+  // facility. The parser conforms to RFC 8259.
   //
   // Note that |callback| will only be called if the parsing operation succeeds
   // or fails before this DataDecoder is destroyed.
diff --git a/services/data_decoder/public/mojom/json_parser.mojom b/services/data_decoder/public/mojom/json_parser.mojom
index b9f4c3f..af524fa 100644
--- a/services/data_decoder/public/mojom/json_parser.mojom
+++ b/services/data_decoder/public/mojom/json_parser.mojom
@@ -6,6 +6,11 @@
 
 import "mojo/public/mojom/base/values.mojom";
 
+// Interface to parse JSON documents into a base::Value structure.
 interface JsonParser {
-  Parse(string json) => (mojo_base.mojom.Value? result, string? error);
+  // Parses the input |json| according to the base::JSONReader |options|
+  // bitmask. Returns the parsed |result| on success or a string
+  // |error| message on failure.
+  Parse(string json, uint32 options) =>
+      (mojo_base.mojom.Value? result, string? error);
 };
diff --git a/services/image_annotation/annotator.cc b/services/image_annotation/annotator.cc
index 82fb0e8..41751f78 100644
--- a/services/image_annotation/annotator.cc
+++ b/services/image_annotation/annotator.cc
@@ -13,6 +13,7 @@
 #include "base/containers/contains.h"
 #include "base/containers/cxx20_erase.h"
 #include "base/feature_list.h"
+#include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -779,8 +780,9 @@
 
   // Send JSON string to a dedicated service for safe parsing.
   GetJsonParser()->Parse(
-      *json_response, base::BindOnce(&Annotator::OnResponseJsonParsed,
-                                     weak_factory_.GetWeakPtr(), request_keys));
+      *json_response, base::JSON_PARSE_RFC,
+      base::BindOnce(&Annotator::OnResponseJsonParsed,
+                     weak_factory_.GetWeakPtr(), request_keys));
 }
 
 void Annotator::OnResponseJsonParsed(
@@ -973,7 +975,7 @@
   }
 
   GetJsonParser()->Parse(
-      *json_response,
+      *json_response, base::JSON_PARSE_RFC,
       base::BindOnce(&Annotator::OnServerLangsResponseJsonParsed,
                      weak_factory_.GetWeakPtr()));
 }
diff --git a/services/network/network_qualities_pref_delegate.cc b/services/network/network_qualities_pref_delegate.cc
index c838c44..127d110 100644
--- a/services/network/network_qualities_pref_delegate.cc
+++ b/services/network/network_qualities_pref_delegate.cc
@@ -46,7 +46,8 @@
   std::unique_ptr<base::DictionaryValue> GetDictionaryValue() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     UMA_HISTOGRAM_EXACT_LINEAR("NQE.Prefs.ReadCount", 1, 2);
-    return pref_service_->GetDictionary(path_)->CreateDeepCopy();
+    return base::DictionaryValue::From(base::Value::ToUniquePtrValue(
+        pref_service_->GetDictionary(path_)->Clone()));
   }
 
  private:
diff --git a/services/network/public/cpp/cross_origin_embedder_policy.cc b/services/network/public/cpp/cross_origin_embedder_policy.cc
index 7a484ae..941e466 100644
--- a/services/network/public/cpp/cross_origin_embedder_policy.cc
+++ b/services/network/public/cpp/cross_origin_embedder_policy.cc
@@ -30,6 +30,10 @@
   return CompatibleWithCrossOriginIsolated(coep.value);
 }
 
+// [spec]:
+// https://html.spec.whatwg.org/C/#compatible-with-cross-origin-isolation An
+// embedder policy value is compatible with cross-origin isolation if it is
+// "credentialless" or "require-corp".
 bool CompatibleWithCrossOriginIsolated(
     mojom::CrossOriginEmbedderPolicyValue value) {
   switch (value) {
diff --git a/services/network/public/cpp/cross_origin_embedder_policy_parser.cc b/services/network/public/cpp/cross_origin_embedder_policy_parser.cc
index 9c41fa25..3b69446 100644
--- a/services/network/public/cpp/cross_origin_embedder_policy_parser.cc
+++ b/services/network/public/cpp/cross_origin_embedder_policy_parser.cc
@@ -20,6 +20,7 @@
 constexpr char kReportOnlyHeaderName[] =
     "cross-origin-embedder-policy-report-only";
 
+// [spec]: https://html.spec.whatwg.org/C/#obtain-an-embedder-policy
 std::pair<mojom::CrossOriginEmbedderPolicyValue, absl::optional<std::string>>
 Parse(base::StringPiece header_value) {
   using Item = net::structured_headers::Item;
diff --git a/services/network/public/cpp/cross_origin_opener_policy.cc b/services/network/public/cpp/cross_origin_opener_policy.cc
index a9211e5..002282f 100644
--- a/services/network/public/cpp/cross_origin_opener_policy.cc
+++ b/services/network/public/cpp/cross_origin_opener_policy.cc
@@ -58,9 +58,13 @@
   }
 }
 
+// [spec]: https://html.spec.whatwg.org/C/#obtain-coop
 void AugmentCoopWithCoep(CrossOriginOpenerPolicy* coop,
                          const CrossOriginEmbedderPolicy& coep) {
   // COOP:
+  //
+  // [spec]: 4.1.2. If coep's value is compatible with cross-origin isolation,
+  //                then set policy's value to "same-origin-plus-COEP".
   if (coop->value == mojom::CrossOriginOpenerPolicyValue::kSameOrigin &&
       CompatibleWithCrossOriginIsolated(coep.value)) {
     coop->value = mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep;
@@ -75,6 +79,10 @@
   }
 
   // COOP-Report-Only:
+  //
+  // [spec]: 6.1.2. If coep's value is compatible with cross-origin isolation or
+  // coep's report-only value is compatible with cross-origin isolation, then
+  // set policy's report-only value to "same-origin-plus-COEP".
   if (coop->report_only_value ==
           mojom::CrossOriginOpenerPolicyValue::kSameOrigin &&
       (CompatibleWithCrossOriginIsolated(coep.value) ||
diff --git a/services/network/public/mojom/cross_origin_embedder_policy.mojom b/services/network/public/mojom/cross_origin_embedder_policy.mojom
index 2ce20f6..eb4ef4fbf 100644
--- a/services/network/public/mojom/cross_origin_embedder_policy.mojom
+++ b/services/network/public/mojom/cross_origin_embedder_policy.mojom
@@ -7,12 +7,26 @@
 import "url/mojom/url.mojom";
 import "services/network/public/mojom/fetch_api.mojom";
 
-// https://html.spec.whatwg.org/multipage/origin.html#embedder-policy-value
-// https://htmlpreview.github.io/?https://github.com/mikewest/credentiallessness/blob/main/index.html
+// [spec]: https://html.spec.whatwg.org/C/#coep
+// An embedder policy value is one of three strings that controls the fetching
+// of cross-origin resources without explicit permission from resource owners.
 enum CrossOriginEmbedderPolicyValue {
+  // [spec]: This is the default value. When this value is used, cross-origin
+  // resources can be fetched without giving explicit permission through the
+  // CORS protocol or the `Cross-Origin-Resource-Policy` header.
   kNone,
-  kCredentialless,
+
+  // [spec]: When this value is used, fetching cross-origin resources requires
+  // the server's explicit permission through the CORS protocol or the
+  // `Cross-Origin-Resource-Policy` header.
   kRequireCorp,
+
+  // [spec]: When this value is used, fetching cross-origin no-CORS resources
+  // omits credentials. In exchange, an explicit `Cross-Origin-Resource-Policy`
+  // header is not required. Other requests sent with credentials require the
+  // server's explicit permission through the CORS protocol or the
+  // `Cross-Origin-Resource-Policy` header.
+  kCredentialless,
 };
 
 // Reports potential COEP violations. This is mainly used by the CORP check
@@ -27,16 +41,19 @@
   Clone(pending_receiver<CrossOriginEmbedderPolicyReporter> receiver);
 };
 
-// Corresponding to the "embedder policy" concept in the spec.
-// https://html.spec.whatwg.org/multipage/origin.html#coep
+// [spec]: https://html.spec.whatwg.org/C/#embedder-policy
+// An embedder policy consists of:
 struct CrossOriginEmbedderPolicy {
-    // The value of the policy.
-    CrossOriginEmbedderPolicyValue value = CrossOriginEmbedderPolicyValue.kNone;
-    // The reporting endpoint for the policy.
-    string? reporting_endpoint;
-    // The value for the "report only" mode.
-    CrossOriginEmbedderPolicyValue report_only_value =
-        CrossOriginEmbedderPolicyValue.kNone;
-    // The reporting endpoint for the "report only" mode.
-    string? report_only_reporting_endpoint;
+  // [spec]: A `value`, which is an embedder policy value, initially
+  // "unsafe-none".
+  CrossOriginEmbedderPolicyValue value = CrossOriginEmbedderPolicyValue.kNone;
+  // [spec]: A `reporting endpoint` string, initially the empty string.
+  string? reporting_endpoint;
+  // [spec]: A `report only value`, which is an embedder policy value, initially
+  // "unsafe-none".
+  CrossOriginEmbedderPolicyValue report_only_value =
+      CrossOriginEmbedderPolicyValue.kNone;
+  // [spec]: A `report only reporting endpoint` string, initially the empty
+  // string.
+  string? report_only_reporting_endpoint;
 };
diff --git a/services/network/public/mojom/url_response_head.mojom b/services/network/public/mojom/url_response_head.mojom
index 4a26ea8..9e4ea015 100644
--- a/services/network/public/mojom/url_response_head.mojom
+++ b/services/network/public/mojom/url_response_head.mojom
@@ -226,6 +226,7 @@
   // TODO(crbug.com/1176753): Remove this once the investigation is done.
   bool has_authorization_covered_by_wildcard_on_preflight = false;
 
+  // [spec] https://fetch.spec.whatwg.org/#response-request-includes-credentials
   // The request's |includeCredentials| value from the "HTTP-network fetch"
   // algorithm.
   // See: https://fetch.spec.whatwg.org/#concept-http-network-fetch
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index e53ff47..2ca649b 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -174,6 +174,9 @@
   response->has_range_requested = request->extra_request_headers().HasHeader(
       net::HttpRequestHeaders::kRange);
   response->dns_aliases = request->response_info().dns_aliases;
+
+  // [spec]: https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
+  // 13. Set response’s request-includes-credentials to includeCredentials.
   response->request_include_credentials = request->allow_credentials();
 }
 
@@ -2496,8 +2499,13 @@
   }
 }
 
-// https://github.com/mikewest/credentiallessness
+// [spec]:
+// https://fetch.spec.whatwg.org/#cross-origin-embedder-policy-allows-credentials
 bool URLLoader::CoepAllowCredentials(const GURL& url) {
+  // [spec]: To check if Cross-Origin-Embedder-Policy allows credentials, given
+  //         a request request, run these steps:
+
+  // [spec]  1. If request’s mode is not "no-cors", then return true.
   switch (request_mode_) {
     case mojom::RequestMode::kCors:
     case mojom::RequestMode::kCorsWithForcedPreflight:
@@ -2509,22 +2517,30 @@
       break;
   }
 
-  mojom::CrossOriginEmbedderPolicyValue coep_policy =
-      factory_params_->client_security_state
-          ? factory_params_->client_security_state->cross_origin_embedder_policy
-                .value
-          : mojom::CrossOriginEmbedderPolicyValue::kNone;
-  if (coep_policy != mojom::CrossOriginEmbedderPolicyValue::kCredentialless)
+  // [spec]: 2. If request’s client is null, then return true.
+  if (!factory_params_->client_security_state)
     return true;
+
+  // [spec]: 3. If request’s client’s policy container’s embedder policy’s value
+  //            is not "credentialless", then return true.
+  if (factory_params_->client_security_state->cross_origin_embedder_policy
+          .value != mojom::CrossOriginEmbedderPolicyValue::kCredentialless) {
+    return true;
+  }
+
   DCHECK(base::FeatureList::IsEnabled(
       features::kCrossOriginEmbedderPolicyCredentialless));
 
+  // [spec]: 4. If request’s origin is same origin with request’s current URL’s
+  //            origin and request does not have a redirect-tainted origin, then
+  //            return true.
   url::Origin request_origin = url::Origin::Create(url);
   url::Origin request_initiator =
       url_request_->initiator().value_or(url::Origin());
   if (request_origin.IsSameOriginWith(request_initiator))
     return true;
 
+  // [spec]: 5. Return false.
   return false;
 }
 
diff --git a/services/services_strings.grd b/services/services_strings.grd
index 77fce4e..756fe8f 100644
--- a/services/services_strings.grd
+++ b/services/services_strings.grd
@@ -40,7 +40,7 @@
       <output filename="services_strings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="services_strings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="services_strings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="services_strings_am.pak" type="data_package" lang="am" />
diff --git a/storage/browser/blob/blob_registry_impl.cc b/storage/browser/blob/blob_registry_impl.cc
index f62b25a..3d565c5 100644
--- a/storage/browser/blob/blob_registry_impl.cc
+++ b/storage/browser/blob/blob_registry_impl.cc
@@ -649,6 +649,7 @@
     mojo::ReportBadMessage(
         "Cannot access data for origin passed to "
         "BlobRegistryImpl::URLStoreForOrigin");
+    return;
   }
   auto self_owned_associated_receiver = mojo::MakeSelfOwnedAssociatedReceiver(
       std::make_unique<BlobURLStoreImpl>(origin, url_registry_),
diff --git a/storage/browser/quota/quota_internals.mojom b/storage/browser/quota/quota_internals.mojom
index 39e8321f..7c0d2c4 100644
--- a/storage/browser/quota/quota_internals.mojom
+++ b/storage/browser/quota/quota_internals.mojom
@@ -10,4 +10,9 @@
     // Returns the total and available disk space in bits for a user,
     // which is then converted to bytes and displayed on the Quota Internals UI.
     GetDiskAvailability() => (uint64 total_space, uint64 available_space);
+
+    // Returns the following statistics:
+    // Errors on Getting Usage and Quota, Evicted Buckets, Evicted Rounds
+    // and Skipped Eviction Rounds.
+    GetStatistics() => (map<string, string> eviction_statistics);
 };
\ No newline at end of file
diff --git a/storage/browser/quota/quota_manager_impl.cc b/storage/browser/quota/quota_manager_impl.cc
index a771255d..9bfffde 100644
--- a/storage/browser/quota/quota_manager_impl.cc
+++ b/storage/browser/quota/quota_manager_impl.cc
@@ -1407,6 +1407,20 @@
       std::move(callback)));
 }
 
+void QuotaManagerImpl::GetStatistics(GetStatisticsCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  base::flat_map<std::string, std::string> statistics;
+  if (temporary_storage_evictor_) {
+    std::map<std::string, int64_t> stats;
+    temporary_storage_evictor_->GetStatistics(&stats);
+    for (const auto& storage_key_usage_pair : stats) {
+      statistics[storage_key_usage_pair.first] =
+          base::NumberToString(storage_key_usage_pair.second);
+    }
+  }
+  std::move(callback).Run(statistics);
+}
+
 void QuotaManagerImpl::GetPersistentHostQuota(const std::string& host,
                                               QuotaCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -1481,20 +1495,6 @@
   GetUsageTracker(type)->GetHostUsageWithBreakdown(host, std::move(callback));
 }
 
-std::map<std::string, std::string> QuotaManagerImpl::GetStatistics() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  std::map<std::string, std::string> statistics;
-  if (temporary_storage_evictor_) {
-    std::map<std::string, int64_t> stats;
-    temporary_storage_evictor_->GetStatistics(&stats);
-    for (const auto& storage_key_usage_pair : stats) {
-      statistics[storage_key_usage_pair.first] =
-          base::NumberToString(storage_key_usage_pair.second);
-    }
-  }
-  return statistics;
-}
-
 bool QuotaManagerImpl::IsStorageUnlimited(const StorageKey& storage_key,
                                           StorageType type) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/storage/browser/quota/quota_manager_impl.h b/storage/browser/quota/quota_manager_impl.h
index 69fd615..91f8ce2 100644
--- a/storage/browser/quota/quota_manager_impl.h
+++ b/storage/browser/quota/quota_manager_impl.h
@@ -346,6 +346,8 @@
 
   // storage::mojom::QuotaInternalsHandler implementation
   void GetDiskAvailability(GetDiskAvailabilityCallback callback) override;
+  void GetStatistics(GetStatisticsCallback callback) override;
+
   // Called by UI and internal modules.
   void GetPersistentHostQuota(const std::string& host, QuotaCallback callback);
   void SetPersistentHostQuota(const std::string& host,
@@ -357,8 +359,6 @@
                                  blink::mojom::StorageType type,
                                  UsageWithBreakdownCallback callback);
 
-  std::map<std::string, std::string> GetStatistics();
-
   bool IsStorageUnlimited(const blink::StorageKey& storage_key,
                           blink::mojom::StorageType type) const;
 
diff --git a/styleguide/swift/OWNERS b/styleguide/swift/OWNERS
index 50b9bfb..3f88f384 100644
--- a/styleguide/swift/OWNERS
+++ b/styleguide/swift/OWNERS
@@ -1,2 +1,2 @@
-javierrobles@chromium.org
 pinkerton@chromium.org
+rkgibson@google.com
diff --git a/testing/scripts/rust/generate_bash_script.py b/testing/scripts/rust/generate_bash_script.py
index 86bd564..06414f5 100755
--- a/testing/scripts/rust/generate_bash_script.py
+++ b/testing/scripts/rust/generate_bash_script.py
@@ -32,19 +32,42 @@
                         metavar='PATH',
                         required=True)
 
-    parser.add_argument('--rust-test-executable',
-                        action='append',
+    parser.add_argument('--rust-test-executables',
                         dest='rust_test_executables',
-                        default=[],
-                        help='One or more executables to wrap. ' \
-                             '(basename - no extension or directory)',
-                        metavar='BASENAME',
+                        help='File listing one or more executables to wrap. ' \
+                             '(basenames - no .exe extension or directory)',
+                        metavar='FILEPATH',
                         required=True)
 
     return parser.parse_args(args=args)
 
 
-def _generate_script(args):
+def _find_test_executables(args):
+    exes = set()
+    input_filepath = args.rust_test_executables
+    with open(input_filepath) as f:
+        for line in f:
+            exe_name = line.strip()
+            # TODO(https://crbug.com/1271215): Append ".exe" extension when
+            # *targeting* Windows.  (The "targeting" part means that we can't
+            # just detect whether the build is *hosted* on Windows.)
+            if exe_name in exes:
+                raise ValueError("Duplicate entry ('{}') in {}".format(
+                    exe_name, input_filepath))
+            exes.add(exe_name)
+    if not exes:
+        raise ValueError("Unexpectedly empty file: {}".format(input_filepath))
+    exes = sorted(exes)  # For stable results in unit tests.
+    return exes
+
+
+def _validate_if_test_executables_exist(exes):
+    for exe in exes:
+        if not os.path.isfile(exe):
+            raise ValueError("File not found: {}".format(exe_path))
+
+
+def _generate_script(args, should_validate_if_exes_exist=True):
     res = '#!/bin/bash\n'
 
     script_dir = os.path.dirname(args.script_path)
@@ -59,8 +82,12 @@
     lib_dir = os.path.normpath(lib_dir)
     res += 'LIB_DIR="$SCRIPT_DIR/{}"\n'.format(lib_dir)
 
+    exes = _find_test_executables(args)
+    if should_validate_if_exes_exist:
+        _validate_if_test_executables_exist(exes)
+
     res += 'env vpython3 "$LIB_DIR/rust_main_program.py" \\\n'
-    for exe in args.rust_test_executables:
+    for exe in exes:
         res += '    "--rust-test-executable=$EXE_DIR/{}" \\\n'.format(exe)
     res += '    "$@"'
 
diff --git a/testing/scripts/rust/generate_bash_script_unittests.py b/testing/scripts/rust/generate_bash_script_unittests.py
index 89b8119..a81a9fc 100755
--- a/testing/scripts/rust/generate_bash_script_unittests.py
+++ b/testing/scripts/rust/generate_bash_script_unittests.py
@@ -5,22 +5,24 @@
 # found in the LICENSE file.
 
 import os
+from pyfakefs import fake_filesystem_unittest
+import tempfile
 import unittest
 
 from generate_bash_script import _parse_args
 from generate_bash_script import _generate_script
 
 
-class Tests(unittest.TestCase):
+class Tests(fake_filesystem_unittest.TestCase):
     def test_parse_args(self):
         raw_args = [
             '--script-path=./bin/run_foobar', '--exe-dir=.',
-            '--rust-test-executable=foo', '--rust-test-executable=bar'
+            '--rust-test-executables=metadata.json'
         ]
         parsed_args = _parse_args(raw_args)
         self.assertEqual('./bin/run_foobar', parsed_args.script_path)
         self.assertEqual('.', parsed_args.exe_dir)
-        self.assertEqual(['foo', 'bar'], parsed_args.rust_test_executables)
+        self.assertEqual('metadata.json', parsed_args.rust_test_executables)
 
     def test_generate_script(self):
         lib_dir = os.path.dirname(__file__)
@@ -28,17 +30,29 @@
         args = type('', (), {})()
         args.script_path = os.path.join(out_dir, 'bin/run_foo_bar')
         args.exe_dir = out_dir
-        args.rust_test_executables = ['foo', 'bar']
 
-        actual = _generate_script(args)
+        # pylint: disable=unexpected-keyword-arg
+        with tempfile.NamedTemporaryFile(delete=False,
+                                         mode='w',
+                                         encoding='utf-8') as f:
+            filepath = f.name
+            f.write("foo\n")
+            f.write("bar\n")
+        try:
+            args.rust_test_executables = filepath
+            actual = _generate_script(args,
+                                      should_validate_if_exes_exist=False)
+        finally:
+            os.remove(filepath)
+
         expected = '''
 #!/bin/bash
 SCRIPT_DIR=`dirname $0`
 EXE_DIR="$SCRIPT_DIR/.."
 LIB_DIR="$SCRIPT_DIR/../../../testing/scripts/rust"
 env vpython3 "$LIB_DIR/rust_main_program.py" \\
-    "--rust-test-executable=$EXE_DIR/foo" \\
     "--rust-test-executable=$EXE_DIR/bar" \\
+    "--rust-test-executable=$EXE_DIR/foo" \\
     "$@"
 '''.strip()
 
diff --git a/testing/scripts/rust/rust_main_program.py b/testing/scripts/rust/rust_main_program.py
index ba57b9d..556d797 100644
--- a/testing/scripts/rust/rust_main_program.py
+++ b/testing/scripts/rust/rust_main_program.py
@@ -124,6 +124,11 @@
         Returns:
             A list of test_results.TestResult objects.
         """
+        list_of_tests_to_run = _get_exe_specific_tests(
+            self._name_of_test_executable, list_of_tests_to_run)
+        if not list_of_tests_to_run:
+            return []
+
         # TODO(lukasza): Avoid passing all test names on the cmdline (might
         # require adding support to Rust test executables for reading cmdline
         # args from a file).
@@ -134,10 +139,14 @@
             self._path_to_test_executable, '--test', '--format=pretty',
             '--exact'
         ]
-        list_of_tests_to_run = _get_exe_specific_tests(
-            self._name_of_test_executable, list_of_tests_to_run)
         args.extend(list_of_tests_to_run)
+
+        print("Running tests from {}...".format(self._name_of_test_executable))
         output = exe_util.run_and_tee_output(args)
+        print("Running tests from {}... DONE.".format(
+            self._name_of_test_executable))
+        print()
+
         return _scrape_test_results(output, self._name_of_test_executable,
                                     list_of_tests_to_run)
 
@@ -152,7 +161,7 @@
                         action='append',
                         dest='rust_test_executables',
                         default=[],
-                        help='Paths to one or more executables to wrap.',
+                        help=argparse.SUPPRESS,
                         metavar='FILEPATH',
                         required=True)
 
diff --git a/testing/scripts/rust/test_filtering.py b/testing/scripts/rust/test_filtering.py
index 2e10c21..a386dd0 100644
--- a/testing/scripts/rust/test_filtering.py
+++ b/testing/scripts/rust/test_filtering.py
@@ -212,7 +212,8 @@
                   'a prefix plus a "*" on the end (to form a glob). If the ' \
                   'string has a "-" at the front, the test (or glob of ' \
                   'tests) will be skipped, not run.'
-    argparse_parser.add_argument('--isolated-script-test-filter',
+    argparse_parser.add_argument('--test-filter',
+                                 '--isolated-script-test-filter',
                                  action='append',
                                  default=[],
                                  dest='test_filters',
@@ -221,7 +222,8 @@
     file_help = 'Path to a file with test filters in Chromium Test List ' \
                 'Format. See also //testing/buildbot/filters/README.md and ' \
                 'bit.ly/chromium-test-list-format'
-    argparse_parser.add_argument('--isolated-script-test-filter-file',
+    argparse_parser.add_argument('--test-filter-file',
+                                 '--isolated-script-test-filter-file',
                                  action='append',
                                  default=[],
                                  dest='test_filter_files',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 274cc41..bc58700 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2180,21 +2180,6 @@
             ]
         }
     ],
-    "ChromeOSWallpaperMigration": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "WallpaperWebUI"
-                    ]
-                }
-            ]
-        }
-    ],
     "ChromeStart": [
         {
             "platforms": [
@@ -3742,6 +3727,21 @@
             ]
         }
     ],
+    "FilesSystemWebApp": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_20220107",
+                    "enable_features": [
+                        "FilesSWA"
+                    ]
+                }
+            ]
+        }
+    ],
     "FillingAcrossAffiliatedWebsites": [
         {
             "platforms": [
@@ -4823,6 +4823,21 @@
             ]
         }
     ],
+    "LightweightReactions": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "LightweightReactions"
+                    ]
+                }
+            ]
+        }
+    ],
     "LiteVideo": [
         {
             "platforms": [
diff --git a/third_party/abseil-cpp/BUILD.gn b/third_party/abseil-cpp/BUILD.gn
index 39d98be..d3234b0 100644
--- a/third_party/abseil-cpp/BUILD.gn
+++ b/third_party/abseil-cpp/BUILD.gn
@@ -77,6 +77,7 @@
     "//third_party/abseil-cpp/absl/meta:type_traits",
     "//third_party/abseil-cpp/absl/numeric:bits",
     "//third_party/abseil-cpp/absl/numeric:int128",
+    "//third_party/abseil-cpp/absl/random",
     "//third_party/abseil-cpp/absl/status",
     "//third_party/abseil-cpp/absl/status:statusor",
     "//third_party/abseil-cpp/absl/strings",
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 65ed9bf7..0fda834 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -190,7 +190,7 @@
 // allows the element to be enabled by the runtime enabled feature, for origin
 // trials.
 const base::Feature kFencedFrames{"FencedFrames",
-                                  base::FEATURE_ENABLED_BY_DEFAULT};
+                                  base::FEATURE_DISABLED_BY_DEFAULT};
 const base::FeatureParam<FencedFramesImplementationType>::Option
     fenced_frame_implementation_types[] = {
         {FencedFramesImplementationType::kShadowDOM, "shadow_dom"},
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 54a62875..b680b064 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -113,6 +113,7 @@
     "loader/same_document_navigation_type.mojom",
     "loader/transferrable_url_loader.mojom",
     "loader/url_loader_factory_bundle.mojom",
+    "lock_screen/lock_screen.mojom",
     "locks/lock_manager.mojom",
     "manifest/capture_links.mojom",
     "manifest/display_mode.mojom",
diff --git a/third_party/blink/public/mojom/lock_screen/OWNERS b/third_party/blink/public/mojom/lock_screen/OWNERS
new file mode 100644
index 0000000..41d1373
--- /dev/null
+++ b/third_party/blink/public/mojom/lock_screen/OWNERS
@@ -0,0 +1,8 @@
+glenrob@chromium.org
+mgiuca@chromium.org
+raymes@chromium.org
+
+# Changes to Mojo interfaces require a security review to avoid
+# introducing new sandbox escapes.
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/mojom/lock_screen/lock_screen.mojom b/third_party/blink/public/mojom/lock_screen/lock_screen.mojom
new file mode 100644
index 0000000..6767c5c
--- /dev/null
+++ b/third_party/blink/public/mojom/lock_screen/lock_screen.mojom
@@ -0,0 +1,23 @@
+// 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.
+
+module blink.mojom;
+
+enum LockScreenServiceStatus {
+    kSuccess,
+    kNotAllowedFromContext,
+    kWriteError,
+  };
+
+// A per-frame service implemented in the browser process which allows a
+// renderer to implement the Lock Screen API:
+// https://github.com/WICG/lock-screen. This cannot be used from opaque origins
+// and can only be used from the profile associated with the lock screen.
+interface LockScreenService {
+  // Return the keys with data associated with them for the current context.
+  GetKeys() => (array<string> keys);
+
+  // Associate arbitrary data with a given key for retrieval later.
+  SetData(string key, string data) => (LockScreenServiceStatus status);
+};
diff --git a/third_party/blink/public/mojom/use_counter/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/css_property_id.mojom
index 6a2786b..166101e4 100644
--- a/third_party/blink/public/mojom/use_counter/css_property_id.mojom
+++ b/third_party/blink/public/mojom/use_counter/css_property_id.mojom
@@ -366,10 +366,10 @@
     kWebkitRubyPosition = 314,
     kWebkitTextCombine = 315,
     kWebkitTextDecorationsInEffect = 316,
-    kWebkitTextEmphasis = 317,
-    kWebkitTextEmphasisColor = 318,
-    kWebkitTextEmphasisPosition = 319,
-    kWebkitTextEmphasisStyle = 320,
+    kAliasWebkitTextEmphasis = 317,
+    kAliasWebkitTextEmphasisColor = 318,
+    kAliasWebkitTextEmphasisPosition = 319,
+    kAliasWebkitTextEmphasisStyle = 320,
     kWebkitTextFillColor = 321,
     kWebkitTextSecurity = 322,
     kWebkitTextStroke = 323,
@@ -755,6 +755,10 @@
     kAppRegion = 702,
     kFontSynthesisSmallCaps = 703,
     kFontSynthesis = 704,
+    kTextEmphasis = 705,
+    kTextEmphasisColor = 706,
+    kTextEmphasisPosition = 707,
+    kTextEmphasisStyle = 708,
     // 1. Add new features above this line (don't change the assigned numbers of
     //    the existing items).
     // 2. Run the src/tools/metrics/histograms/update_use_counter_css.py script
diff --git a/third_party/blink/public/strings/blink_strings.grd b/third_party/blink/public/strings/blink_strings.grd
index 05695342..903092b 100644
--- a/third_party/blink/public/strings/blink_strings.grd
+++ b/third_party/blink/public/strings/blink_strings.grd
@@ -76,7 +76,7 @@
       <output filename="blink_strings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="blink_strings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="blink_strings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="blink_strings_am.pak" type="data_package" lang="am" />
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_metrics.cc b/third_party/blink/renderer/bindings/core/v8/v8_metrics.cc
index 1aabacb..225b47f 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_metrics.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_metrics.cc
@@ -327,13 +327,16 @@
     const v8::metrics::GarbageCollectionYoungCycle& event,
     ContextId context_id) {
   // Check that all used values have been initialized.
+  DCHECK_LE(0, event.reason);
   DCHECK_LE(0, event.total_wall_clock_duration_in_us);
   DCHECK_LE(0, event.main_thread_wall_clock_duration_in_us);
   DCHECK_LE(0, event.collection_rate_in_percent);
   DCHECK_LE(0, event.efficiency_in_bytes_per_us);
   DCHECK_LE(0, event.main_thread_efficiency_in_bytes_per_us);
 
-  // Report throughput metrics:
+  UMA_HISTOGRAM_ENUMERATION("V8.GC.Cycle.Reason.Young", event.reason,
+                            v8::internal::kGarbageCollectionReasonMaxValue);
+
   UMA_HISTOGRAM_TIMES(
       "V8.GC.Cycle.Young",
       base::Microseconds(event.total_wall_clock_duration_in_us));
@@ -341,7 +344,6 @@
       "V8.GC.Cycle.MainThread.Young",
       base::Microseconds(event.main_thread_wall_clock_duration_in_us));
 
-  // Report efficacy metrics:
   static constexpr size_t kMinSize = 1;
   static constexpr size_t kMaxSize = 4 * 1024 * 1024;
   static constexpr size_t kNumBuckets = 50;
diff --git a/third_party/blink/renderer/core/animation/color_property_functions.cc b/third_party/blink/renderer/core/animation/color_property_functions.cc
index b1a4d4a4..432e4e14 100644
--- a/third_party/blink/renderer/core/animation/color_property_functions.cc
+++ b/third_party/blink/renderer/core/animation/color_property_functions.cc
@@ -42,7 +42,7 @@
       return style.OutlineColor();
     case CSSPropertyID::kColumnRuleColor:
       return style.ColumnRuleColor();
-    case CSSPropertyID::kWebkitTextEmphasisColor:
+    case CSSPropertyID::kTextEmphasisColor:
       return style.TextEmphasisColor();
     case CSSPropertyID::kWebkitTextFillColor:
       return style.TextFillColor();
@@ -92,7 +92,7 @@
       return style.InternalVisitedOutlineColor();
     case CSSPropertyID::kColumnRuleColor:
       return style.InternalVisitedColumnRuleColor();
-    case CSSPropertyID::kWebkitTextEmphasisColor:
+    case CSSPropertyID::kTextEmphasisColor:
       return style.InternalVisitedTextEmphasisColor();
     case CSSPropertyID::kWebkitTextFillColor:
       return style.InternalVisitedTextFillColor();
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 9db94960..a5ef8d4 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -4983,7 +4983,7 @@
       name_for_methods: "TextCombine",
     },
     {
-      name: "-webkit-text-emphasis-color",
+      name: "text-emphasis-color",
       property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "ColorIncludingFallback"],
       inherited: true,
       field_group: "*",
@@ -4998,7 +4998,7 @@
       valid_for_highlight: true,
     },
     {
-      name: "-webkit-text-emphasis-position",
+      name: "text-emphasis-position",
       property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"],
       inherited: true,
       field_group: "*",
@@ -5010,7 +5010,7 @@
       valid_for_marker: true,
     },
     {
-      name: "-webkit-text-emphasis-style",
+      name: "text-emphasis-style",
       property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"],
       inherited: true,
       style_builder_custom_functions: ["initial", "inherit", "value"],
@@ -6396,8 +6396,8 @@
       computable: true,
     },
     {
-      name: "-webkit-text-emphasis",
-      longhands: ["-webkit-text-emphasis-style", "-webkit-text-emphasis-color"],
+      name: "text-emphasis",
+      longhands: ["text-emphasis-style", "text-emphasis-color"],
       property_methods: ["ParseShorthand"],
     },
     {
@@ -6658,7 +6658,7 @@
     },
     {
       name: "-internal-visited-text-emphasis-color",
-      visited_property_for: "-webkit-text-emphasis-color",
+      visited_property_for: "text-emphasis-color",
       property_methods: ["ParseSingleValue", "ColorIncludingFallback"],
       inherited: true,
       field_group: "*->inherited_visited",
@@ -6839,15 +6839,15 @@
     },
     {
       name: "-epub-text-emphasis",
-      alias_for: "-webkit-text-emphasis",
+      alias_for: "text-emphasis",
     },
     {
       name: "-epub-text-emphasis-color",
-      alias_for: "-webkit-text-emphasis-color",
+      alias_for: "text-emphasis-color",
     },
     {
       name: "-epub-text-emphasis-style",
-      alias_for: "-webkit-text-emphasis-style",
+      alias_for: "text-emphasis-style",
     },
     {
       name: "-epub-text-orientation",
@@ -7072,6 +7072,22 @@
       alias_for: "shape-outside",
     },
     {
+      name: "-webkit-text-emphasis",
+      alias_for: "text-emphasis",
+    },
+    {
+      name: "-webkit-text-emphasis-color",
+      alias_for: "text-emphasis-color",
+    },
+    {
+      name: "-webkit-text-emphasis-position",
+      alias_for: "text-emphasis-position",
+    },
+    {
+      name: "-webkit-text-emphasis-style",
+      alias_for: "text-emphasis-style",
+    },
+    {
       name: "-webkit-text-size-adjust",
       alias_for: "text-size-adjust",
     },
diff --git a/third_party/blink/renderer/core/css/css_value_keywords.json5 b/third_party/blink/renderer/core/css/css_value_keywords.json5
index 615e854..3959b24 100644
--- a/third_party/blink/renderer/core/css/css_value_keywords.json5
+++ b/third_party/blink/renderer/core/css/css_value_keywords.json5
@@ -966,7 +966,7 @@
     "after",
     "before",
 
-    // -webkit-text-emphasis-position
+    // text-emphasis-position
     "over",
     "under",
     //right
diff --git a/third_party/blink/renderer/core/css/cssom/style_value_factory.cc b/third_party/blink/renderer/core/css/cssom/style_value_factory.cc
index 3eb8fa2..44ae896 100644
--- a/third_party/blink/renderer/core/css/cssom/style_value_factory.cc
+++ b/third_party/blink/renderer/core/css/cssom/style_value_factory.cc
@@ -106,7 +106,7 @@
     case CSSPropertyID::kOutlineColor:
     case CSSPropertyID::kStopColor:
     case CSSPropertyID::kTextDecorationColor:
-    case CSSPropertyID::kWebkitTextEmphasisColor: {
+    case CSSPropertyID::kTextEmphasisColor: {
       // Only 'currentcolor' is supported.
       auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
       if (identifier_value &&
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
index bfb7316..de75bf1 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
@@ -169,7 +169,7 @@
     case CSSPropertyID::kBorderInlineEndColor:
     case CSSPropertyID::kBorderInlineStartColor:
     case CSSPropertyID::kColumnRuleColor:
-    case CSSPropertyID::kWebkitTextEmphasisColor:
+    case CSSPropertyID::kTextEmphasisColor:
     case CSSPropertyID::kWebkitTextFillColor:
     case CSSPropertyID::kWebkitTextStrokeColor:
     case CSSPropertyID::kTextDecorationColor:
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index b7e0c19..72a7c9f 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -8160,14 +8160,14 @@
       style.TextDecorationsInEffect());
 }
 
-const CSSValue* WebkitTextEmphasisColor::ParseSingleValue(
+const CSSValue* TextEmphasisColor::ParseSingleValue(
     CSSParserTokenRange& range,
     const CSSParserContext& context,
     const CSSParserLocalContext&) const {
   return css_parsing_utils::ConsumeColor(range, context);
 }
 
-const blink::Color WebkitTextEmphasisColor::ColorIncludingFallback(
+const blink::Color TextEmphasisColor::ColorIncludingFallback(
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(!visited_link);
@@ -8178,7 +8178,7 @@
                                      style.UsedColorScheme());
 }
 
-const CSSValue* WebkitTextEmphasisColor::CSSValueFromComputedStyleInternal(
+const CSSValue* TextEmphasisColor::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
     const LayoutObject*,
     bool allow_visited_style) const {
@@ -8188,7 +8188,7 @@
 
 // [ over | under ] && [ right | left ]?
 // If [ right | left ] is omitted, it defaults to right.
-const CSSValue* WebkitTextEmphasisPosition::ParseSingleValue(
+const CSSValue* TextEmphasisPosition::ParseSingleValue(
     CSSParserTokenRange& range,
     const CSSParserContext& context,
     const CSSParserLocalContext&) const {
@@ -8237,25 +8237,25 @@
   return list;
 }
 
-const CSSValue* WebkitTextEmphasisPosition::CSSValueFromComputedStyleInternal(
+const CSSValue* TextEmphasisPosition::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
     const LayoutObject*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   switch (style.GetTextEmphasisPosition()) {
-    case TextEmphasisPosition::kOverRight:
+    case blink::TextEmphasisPosition::kOverRight:
       list->Append(*CSSIdentifierValue::Create(CSSValueID::kOver));
       list->Append(*CSSIdentifierValue::Create(CSSValueID::kRight));
       break;
-    case TextEmphasisPosition::kOverLeft:
+    case blink::TextEmphasisPosition::kOverLeft:
       list->Append(*CSSIdentifierValue::Create(CSSValueID::kOver));
       list->Append(*CSSIdentifierValue::Create(CSSValueID::kLeft));
       break;
-    case TextEmphasisPosition::kUnderRight:
+    case blink::TextEmphasisPosition::kUnderRight:
       list->Append(*CSSIdentifierValue::Create(CSSValueID::kUnder));
       list->Append(*CSSIdentifierValue::Create(CSSValueID::kRight));
       break;
-    case TextEmphasisPosition::kUnderLeft:
+    case blink::TextEmphasisPosition::kUnderLeft:
       list->Append(*CSSIdentifierValue::Create(CSSValueID::kUnder));
       list->Append(*CSSIdentifierValue::Create(CSSValueID::kLeft));
       break;
@@ -8263,7 +8263,7 @@
   return list;
 }
 
-const CSSValue* WebkitTextEmphasisStyle::ParseSingleValue(
+const CSSValue* TextEmphasisStyle::ParseSingleValue(
     CSSParserTokenRange& range,
     const CSSParserContext& context,
     const CSSParserLocalContext&) const {
@@ -8298,7 +8298,7 @@
   return nullptr;
 }
 
-const CSSValue* WebkitTextEmphasisStyle::CSSValueFromComputedStyleInternal(
+const CSSValue* TextEmphasisStyle::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
     const LayoutObject*,
     bool allow_visited_style) const {
@@ -8326,7 +8326,7 @@
   return nullptr;
 }
 
-void WebkitTextEmphasisStyle::ApplyInitial(StyleResolverState& state) const {
+void TextEmphasisStyle::ApplyInitial(StyleResolverState& state) const {
   state.Style()->SetTextEmphasisFill(
       ComputedStyleInitialValues::InitialTextEmphasisFill());
   state.Style()->SetTextEmphasisMark(
@@ -8335,7 +8335,7 @@
       ComputedStyleInitialValues::InitialTextEmphasisCustomMark());
 }
 
-void WebkitTextEmphasisStyle::ApplyInherit(StyleResolverState& state) const {
+void TextEmphasisStyle::ApplyInherit(StyleResolverState& state) const {
   state.Style()->SetTextEmphasisFill(
       state.ParentStyle()->GetTextEmphasisFill());
   state.Style()->SetTextEmphasisMark(
@@ -8344,8 +8344,8 @@
       state.ParentStyle()->TextEmphasisCustomMark());
 }
 
-void WebkitTextEmphasisStyle::ApplyValue(StyleResolverState& state,
-                                         const CSSValue& value) const {
+void TextEmphasisStyle::ApplyValue(StyleResolverState& state,
+                                   const CSSValue& value) const {
   if (const auto* list = DynamicTo<CSSValueList>(value)) {
     DCHECK_EQ(list->length(), 2U);
     for (unsigned i = 0; i < 2; ++i) {
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
index bcc8107..2fac9cd5 100644
--- a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
@@ -3068,14 +3068,14 @@
       &style.MaskLayers());
 }
 
-bool WebkitTextEmphasis::ParseShorthand(
+bool TextEmphasis::ParseShorthand(
     bool important,
     CSSParserTokenRange& range,
     const CSSParserContext& context,
     const CSSParserLocalContext&,
     HeapVector<CSSPropertyValue, 256>& properties) const {
   return css_parsing_utils::ConsumeShorthandGreedilyViaLonghands(
-      webkitTextEmphasisShorthand(), important, context, range, properties);
+      textEmphasisShorthand(), important, context, range, properties);
 }
 
 bool WebkitTextStroke::ParseShorthand(
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
index fc69f68..19041ba 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -874,7 +874,13 @@
       style.OverflowY() != EOverflow::kVisible)
     AdjustOverflow(style, element);
 
-  if (StopPropagateTextDecorations(style, element))
+  // TODO(rego): When HighlightInheritance (https://crbug.com/1024156) is
+  // enabled, we're going to inherit the text decorations from the parent
+  // elements, that would cause that we paint the decorations more than once in
+  // the highlight pseudos. This doesn't seem right and there's a spec issue
+  // (https://github.com/w3c/csswg-drafts/issues/6829) about not propagating
+  // text decorations on highlights pseudos.
+  if (StopPropagateTextDecorations(style, element) || state.IsForHighlight())
     style.ClearAppliedTextDecorations();
   else
     style.RestoreParentTextDecorations(layout_parent_style);
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_state.h b/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
index f2270ade..84ad87be 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
@@ -160,6 +160,8 @@
   // reference to the passed value.
   const CSSValue& ResolveLightDarkPair(const CSSValue&);
 
+  bool IsForHighlight() const { return is_for_highlight_; }
+
   bool CanCacheBaseStyle() const { return can_cache_base_style_; }
 
   bool HadNoMatchedProperties() const { return had_no_matched_properties_; }
diff --git a/third_party/blink/renderer/core/css/style_property_serializer.cc b/third_party/blink/renderer/core/css/style_property_serializer.cc
index 30c0aa4..9a46e76 100644
--- a/third_party/blink/renderer/core/css/style_property_serializer.cc
+++ b/third_party/blink/renderer/core/css/style_property_serializer.cc
@@ -347,8 +347,8 @@
     case CSSPropertyID::kListStyle:
     case CSSPropertyID::kOffset:
     case CSSPropertyID::kTextDecoration:
+    case CSSPropertyID::kTextEmphasis:
     case CSSPropertyID::kWebkitMask:
-    case CSSPropertyID::kWebkitTextEmphasis:
     case CSSPropertyID::kWebkitTextStroke:
       return true;
     default:
@@ -558,8 +558,8 @@
       return GetLayeredShorthandValue(webkitMaskRepeatShorthand());
     case CSSPropertyID::kWebkitMask:
       return GetLayeredShorthandValue(webkitMaskShorthand());
-    case CSSPropertyID::kWebkitTextEmphasis:
-      return GetShorthandValue(webkitTextEmphasisShorthand());
+    case CSSPropertyID::kTextEmphasis:
+      return GetShorthandValue(textEmphasisShorthand());
     case CSSPropertyID::kWebkitTextStroke:
       return GetShorthandValue(webkitTextStrokeShorthand());
     case CSSPropertyID::kMarker: {
diff --git a/third_party/blink/renderer/core/frame/history.cc b/third_party/blink/renderer/core/frame/history.cc
index 22c8c03..0528223 100644
--- a/third_party/blink/renderer/core/frame/history.cc
+++ b/third_party/blink/renderer/core/frame/history.cc
@@ -63,7 +63,7 @@
   }
 
   // TODO(crbug.com/1262022): Remove this condition when Fenced Frames
-  // transition to MPArch completely
+  // transition to MPArch completely.
   if (DomWindow()->GetFrame()->IsInFencedFrameTree()) {
     return 1;
   }
@@ -183,6 +183,12 @@
     return;
   }
 
+  // TODO(crbug.com/1262022): Remove this condition when Fenced Frames
+  // transition to MPArch completely.
+  if (DomWindow()->GetFrame()->IsInFencedFrameTree()) {
+    return;
+  }
+
   DCHECK(IsMainThread());
   auto* active_window = LocalDOMWindow::From(script_state);
   if (!active_window)
diff --git a/third_party/blink/renderer/core/frame/root_frame_viewport.cc b/third_party/blink/renderer/core/frame/root_frame_viewport.cc
index a55a624a..b50c6a9 100644
--- a/third_party/blink/renderer/core/frame/root_frame_viewport.cc
+++ b/third_party/blink/renderer/core/frame/root_frame_viewport.cc
@@ -495,10 +495,6 @@
   LayoutViewport().ScrollControlWasSetNeedsPaintInvalidation();
 }
 
-cc::Layer* RootFrameViewport::LayerForScrolling() const {
-  return LayoutViewport().LayerForScrolling();
-}
-
 cc::Layer* RootFrameViewport::LayerForHorizontalScrollbar() const {
   return LayoutViewport().LayerForHorizontalScrollbar();
 }
diff --git a/third_party/blink/renderer/core/frame/root_frame_viewport.h b/third_party/blink/renderer/core/frame/root_frame_viewport.h
index 9e12c1c..e0852fab 100644
--- a/third_party/blink/renderer/core/frame/root_frame_viewport.h
+++ b/third_party/blink/renderer/core/frame/root_frame_viewport.h
@@ -91,7 +91,6 @@
   bool UserInputScrollable(ScrollbarOrientation) const override;
   bool ShouldPlaceVerticalScrollbarOnLeft() const override;
   void ScrollControlWasSetNeedsPaintInvalidation() override;
-  cc::Layer* LayerForScrolling() const override;
   cc::Layer* LayerForHorizontalScrollbar() const override;
   cc::Layer* LayerForVerticalScrollbar() const override;
   cc::Layer* LayerForScrollCorner() const override;
diff --git a/third_party/blink/renderer/core/frame/visual_viewport.h b/third_party/blink/renderer/core/frame/visual_viewport.h
index ac71f018..21c2617 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport.h
+++ b/third_party/blink/renderer/core/frame/visual_viewport.h
@@ -215,7 +215,7 @@
   void ScrollControlWasSetNeedsPaintInvalidation() override {}
   void UpdateScrollOffset(const ScrollOffset&,
                           mojom::blink::ScrollType) override;
-  cc::Layer* LayerForScrolling() const override;
+  cc::Layer* LayerForScrolling() const;
   cc::Layer* LayerForHorizontalScrollbar() const override;
   cc::Layer* LayerForVerticalScrollbar() const override;
   bool ScheduleAnimation() override;
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index a531eb8..d55d1e3 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -1704,6 +1704,12 @@
     case WebInputEvent::Type::kMouseMove:
       event_type = event_type_names::kMousemove;
       break;
+    case WebInputEvent::Type::kMouseEnter:
+    case WebInputEvent::Type::kMouseLeave:
+    case WebInputEvent::Type::kContextMenu:
+      // These should not be normally dispatched but may be due to timing
+      // because pointer lost messaging happens on separate mojo channel.
+      return;
     default:
       NOTREACHED() << input_event.GetType();
   }
diff --git a/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_shadow_dom_delegate_test.cc b/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_shadow_dom_delegate_test.cc
index 5ae9c40..62eacde1 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_shadow_dom_delegate_test.cc
+++ b/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_shadow_dom_delegate_test.cc
@@ -6,6 +6,7 @@
 
 #include "base/test/scoped_feature_list.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/html/html_collection.h"
@@ -17,7 +18,11 @@
 
 namespace blink {
 
-class FencedFrameShadowDOMDelegateTest : public RenderingTest {
+class FencedFrameShadowDOMDelegateTest : private ScopedFencedFramesForTest,
+                                         public RenderingTest {
+ public:
+  FencedFrameShadowDOMDelegateTest() : ScopedFencedFramesForTest(true) {}
+
  protected:
   void SetUp() override {
     PageTestBase::SetUp();
diff --git a/third_party/blink/renderer/core/paint/README.md b/third_party/blink/renderer/core/paint/README.md
index 0bcc84e..2844d22 100644
--- a/third_party/blink/renderer/core/paint/README.md
+++ b/third_party/blink/renderer/core/paint/README.md
@@ -1,6 +1,6 @@
 <!---
   The live version of this document can be viewed at:
-  https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/core/paint/README.md
+  https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/core/paint/README.md
 -->
 
 # renderer/core/paint
@@ -109,10 +109,6 @@
 *   Compositing container chain: same as paint chain, but for compositing
     container.
 
-*   Paint invalidation container: the nearest object on the compositing
-    container chain which is composited. CompositeAfterPaint doesn't have this
-    concept.
-
 *   Visual rect: the bounding box of all pixels that will be painted by a
     for a [display item](../../platform/graphics/paint/README.md#display-items)
     It's in the space of the containing transform property node (see [Building
@@ -141,12 +137,6 @@
 
 This process is done in the following document lifecycle phases:
 
-*   Compositing update (`kInCompositingUpdate`, `kCompositingInputsClean`)
-    *    Decides layerization (GraphicsLayers).
-    *    This is only needed for the
-         [current compositing algorithm](#Current-compositing-algorithm-CompositeBeforePaint_)
-         and will go away with
-         [CompositeAfterPaint](#New-compositing-algorithm-CompositeAfterPaint_).
 *   [PrePaint](#PrePaint) (`kInPrePaint`)
     *    [Paint invalidation](#Paint-invalidation) which invalidates display
          items which need to be painted.
@@ -156,173 +146,16 @@
     *    Groups the display list into paint chunks which share the same
          property tree state.
     *    Commits the results to the compositor.
-        *    [CompositeAfterPaint](##New-compositing-algorithm-CompositeAfterPaint_)
-             will decide layerization at this point.
+        *    Decides which cc::Layers to create based on paint chunks.
         *    Passes the paint chunks to the compositor in a cc::Layer list.
         *    Converts the blink property tree nodes into cc property tree nodes.
 
-
-Compositing decisions are currently made before paint (see
-[Current compositing algorithm](#Current-compositing-algorithm-CompositeBeforePaint_))
-but there is an in-progress refactoring to make compositing decisions after
-paint (see
-[CompositeAfterPaint](##New-compositing-algorithm-CompositeAfterPaint_)). The
-most recent step towards CompositeAfterPaint was a project called
-[BlinkGenPropertyTrees](https://docs.google.com/document/d/17GKr2uIH2O5GthdTyvJpv1qZjoHYoLgrzvCkbCHoID4/view)
-which uses the compositing decisions from the current compositor
-(PaintLayerCompositor, which produces GraphicsLayers) with the new
-CompositeAfterPaint compositor (PaintArtifactCompositor). This is done by a step
-at the end of paint which collects all painted GraphicsLayers as a list of
-[GraphicsLayerDisplayItem](../../platform/graphics/paint/graphics_layer_display_item.h)s.
-Additionaly, [ForeignLayerDisplayItem](../../platform/graphics/paint/foreign_layer_display_item.h)s are used for cc::Layers managed outside blink (e.g.,
-video layers, plugin layers) and are treated as opaque composited content by
-the PaintArtifactCompositor. This approach starts using
-much of the new PaintArtifactCompositor logic (e.g., converting blink property
-trees to cc property trees) without changing how compositing decisions are made.
-
 [Debugging blink objects](https://docs.google.com/document/d/1vgQY11pxRQUDAufxSsc2xKyQCKGPftZ5wZnjY2El4w8/view)
 has information about dumping the paint and compositing datastructures for
 debugging.
 
 
-### Current compositing algorithm (CompositeBeforePaint)
-
-The current compositing system chooses which `LayoutObject`s paint into their
-own composited backing texture. This is called "having a compositing trigger".
-These textures correspond to GraphicsLayers. There are also additional
-`GraphicsLayer`s which represent property tree-related effects.
-
-All elements which do not have a compositing trigger paint into the texture
-of the nearest `LayoutObject`with a compositing trigger on its
-*compositing container chain* (except for squashed layers; see below). For
-historical, practical and implementation detail reasons, only `LayoutObject`s
-with `PaintLayer`s can have a compositing trigger. See
-[crbug.com/370604](https://crbug.com/370604) for a bug tracking this limitation,
-which is often referred to as the **fundamental compositing bug**.
-
-The various compositing triggers are listed in
-[compositing_reasons.h](../../platform/graphics/compositing_reasons.h) and fall
-in to several categories:
-
-1. Direct reasons due to CSS style (see `CompositingReason::kComboAllDirectStyleDeterminedReasons`)
-2. Direct reasons due to other conditions (see `CompositingReason::kComboAllDirectNonStyleDeterminedReasons`)
-3. Composited scrolling-dependent reasons (see `CompositingReason::kComboAllCompositedScrollingDeterminedReasons`)
-4. Composited descendant-dependent reasons (see `CompositingReason::kComboCompositedDescendants`)
-5. Overlap-dependent reasons (See `CompositingReasons::kComboSquashableReasons`)
-
-The triggers have no effect unless `PaintLayerCompositor::CanBeComposited`
-returns true.
-
-Category (1) always triggers compositing of a `LayoutObject` based on its own
-style. Category (2) triggers based on the `LayoutObject`'s style, its DOM
-ancestors, and whether it is a certain kind of frame root. Category (3)
-triggers based on whether composited scrolling applies to the `LayoutObject`,
-or the `LayoutObject` moves relative to a composited scroller (position: fixed
-or position: sticky). Category (4) triggers if there are any stacking
-descendants of the `LayoutObject` that end up composited. Category 5 triggers
-if the `LayoutObject` paints after and overlaps (or may overlap) another
-composited layer.
-
-Note that composited scrolling is special. Several ways it is special:
-
- * Composited descendants do _not_ necessarily cause composited scrolling of an
-ancestor.
- * The presence of LCD text prevents composited scrolling in the
-absence of other overriding triggers.
- * Local frame roots always use
-composited scrolling if they have overflow.
- * Non-local frame roots use
-composited scrolling if they have overflow and any composited descendants.
- * Composited scrolling is indicated by a bit on PaintLayerScrollableArea, not
- a direct compositing reason. This bit is then transformed into a compositing
- reason from category (3) during the CompositingRequirementsUpdater
-
-Note that overlap triggers have two special behaviors:
-
- * Any `LayoutObject`
-which may overlap a `LayoutObject` that uses composited scrolling or a
-transform animation, paints after it, and scrolls with respect to it, receives
-an overlap trigger. In some cases this trigger is too aggressive.
- * Inline CSS
-transform is treated as if it was a transform animation. (This is a heuristic
-to speed up the compositing step but leads to more composited layers.)
-
-The sequence of work during the `DocumentLifecycle` to compute these triggers
-is as follows:
-
- * `kInStyleRecalc`: compute (1) and most of (4) by calling
-`CompositingReasonFinder::PotentialCompositingReasonsFromStyle` and caching
-the result on `PaintLayer`, accessible via
-`PaintLayer::PotentialCompositingReasonsFromStyle`. Dirty bits in
-`StyleDifference` determine whether this has to be re-computed on a particular
-lifecycle update.
- * `kInCompositingUpdate`: compute (2) `CompositingInputsUpdater`. Also
- set the composited scrolling bit on `PaintLayerScrollableArea` if applicable.
- * `kCompositingInputsClean`: compute (3), the rest of (4), and (5), in
-`CompositingRequirementsUpdater`
-
-
-The flow of data from the LayoutObject tree to the cc::Layer list and cc
-property trees is described below:
-
-```
-from layout
-  |
-  v
-+------------------------------+
-| LayoutObject/PaintLayer tree |-----------+
-+------------------------------+           |
-  |                                        |
-  | PaintLayerCompositor::UpdateIfNeeded() |
-  |   CompositingInputsUpdater::Update()   |
-  |   CompositingLayerAssigner::Assign()   |
-  |   GraphicsLayerUpdater::Update()       | PrePaintTreeWalk::Walk()
-  |   GraphicsLayerTreeBuilder::Rebuild()  |   PaintPropertyTreeBuider::UpdatePropertiesForSelf()
-  v                                        |
-+--------------------+                   +------------------+
-| GraphicsLayer tree |<------------------|  Property trees  |
-+--------------------+                   +------------------+
-      |                                    |              |
-      |<-----------------------------------+              |
-      | LocalFrameView::PaintTree()                       |
-      |   LocalFrameView::PaintGraphicsLayerRecursively() |
-      |     GraphicsLayer::Paint()                        |
-      |       CompositedLayerMapping::PaintContents()     |
-      |         PaintLayerPainter::PaintLayerContents()   |
-      |           ObjectPainter::Paint()                  |
-      v                                                   |
-    +---------------------------------+                   |
-    | DisplayItemList/PaintChunk list |                   |
-    +---------------------------------+                   |
-      |                                                   |
-      |<--------------------------------------------------+
-      | PaintChunksToCcLayer::Convert()                   |
-      v                                                   |
-+--------------------------------------------------+      |
-| GraphicsLayerDisplayItem/ForeignLayerDisplayItem |      |
-+--------------------------------------------------+      |
-  |                                                       |
-  |    LocalFrameView::PushPaintArtifactToCompositor()    |
-  |         PaintArtifactCompositor::Update()             |
-  +--------------------+       +--------------------------+
-                       |       |
-                       v       v
-        +----------------+  +-----------------------+
-        | cc::Layer list |  |   cc property trees   |
-        +----------------+  +-----------------------+
-                |              |
-  +-------------+--------------+
-  | to compositor
-  v
-```
-[Debugging blink objects](https://docs.google.com/document/d/1vgQY11pxRQUDAufxSsc2xKyQCKGPftZ5wZnjY2El4w8/view)
-has information about dumping these paint and compositing datastructures for
-debugging.
-
-### New compositing algorithm (CompositeAfterPaint)
-
-This is a new mode under development. In this mode, layerization decisions are
-made after paint.
+### Compositing algorithm
 
 The process starts with pre-paint to generate property trees. During paint,
 each generated display item will be associated with a property tree state.
@@ -380,22 +213,6 @@
 has information about dumping these paint and compositing datastructures for
 debugging.
 
-### Comparison of the current and new compositing algorithms
-
-The
-[current compositing design](#Current-compositing-algorithm-CompositeBeforePaint_)
-is an incremental step towards the new
-[CompositeAfterPaint](##New-compositing-algorithm-CompositeAfterPaint_) design
-and was launched as [BlinkGenPropertyTrees](https://docs.google.com/document/d/17GKr2uIH2O5GthdTyvJpv1qZjoHYoLgrzvCkbCHoID4/view). The design before
-BlinkGenPropertyTrees is not described in this document.
-
-
-|                                 | Current (CompositeBeforePaint)               | New (CompositeAfterPaint) |
-|---------------------------------|:---------------------------------------------|:--------------------------|
-| REF::CompositeAfterPaintEnabled | False                                        | True                      |
-| Layerization                    | PaintLayerCompositor, CompositedLayerMapping | PaintArtifactCompositor   |
-| PaintController                 | One per GraphicsLayer                        | One per LocalFrameView    |
-
 ## PrePaint
 [`PrePaintTreeWalk`](pre_paint_tree_walk.h)
 
@@ -423,24 +240,8 @@
 At the beginning of the PrePaint tree walk, a root `PaintInvalidatorContext`
 is created for the root `LayoutView`. During the tree walk, one
 `PaintInvalidatorContext` is created for each visited object based on the
-`PaintInvalidatorContext` passed from the parent object. It tracks the following
-information to provide O(1) complexity access to them if possible:
-
-*   Paint invalidation container (Slimming Paint v1 only): As described by
-    the definitions in [Other glossaries](#Other-glossaries), the paint
-    invalidation container for stacked objects can differ from normal objects,
-    we have to track both separately. Here is an example:
-
-        <div style="overflow: scroll">
-            <div id=A style="position: absolute"></div>
-            <div id=B></div>
-        </div>
-
-    If the scroller is composited (for high-DPI screens for example), it is the
-    paint invalidation container for div B, but not A.
-
-*   Painting layer: the layer which will initiate painting of the current
-    object. It's the same value as `LayoutObject::PaintingLayer()`.
+`PaintInvalidatorContext` passed from the parent object. It tracks the painting
+layer which will initiate painting of the current object.
 
 [`PaintInvalidator`](PaintInvalidator.h) initializes `PaintInvalidatorContext`
 for the current object, then calls `LayoutObject::InvalidatePaint()` which
@@ -608,21 +409,14 @@
 Paint walks the LayoutObject tree in paint-order and produces a list of
 display items. This is implemented using static painter classes
 (e.g., [`BlockPainter`](block_painter.cc)) and appends display items to a
-[`PaintController`](../../platform/graphics/paint/paint_controller.h). During
+[`PaintController`](../../platform/graphics/paint/paint_controller.h). There
+is only one `PaintController` for the entire `LocalFrameView`. During
 this treewalk, the current property tree state is maintained (see:
 `PaintController::UpdateCurrentPaintChunkProperties`). The `PaintController`
 segments the display item list into
 [`PaintChunk`](../../platform/graphics/paint/paint_chunk.h)s which are
 sequential display items that share a common property tree state.
 
-With the
-[current compositing algorithm](#Current-compositing-algorithm-CompositeBeforePaint_),
-the paint-order `LayoutObject` treewalk is initiated by `GraphicsLayer`s, and
-each `GraphicsLayer` contains a `PaintController`. In the new compositing
-approach,
-[CompositeAfterPaint](##New-compositing-algorithm-CompositeAfterPaint_), there
-is only one `PaintController` for the entire `LocalFrameView`.
-
 ### Paint result caching
 
 `PaintController` holds the previous painting result as a cache of display
@@ -705,20 +499,13 @@
    to prevent compositor scrolling of non-composited scrollers, plugins with
    blocking scroll event handlers, and resize handles.
 
-   If `scroll_translation` is not null, this is also used for
-   CompositeAfterPaint to force a special cc::Layer that is marked as being
-   scrollable when composited scrolling is needed for the scroller.
+   If `scroll_translation` is not null, this is also used to force a special
+   cc::Layer that is marked as being scrollable when composited scrolling is
+   needed for the scroller.
 
 ### Scrollbar painting
 
-For now in pre-CompositeAfterPaint, we have distinct paths for composited
-scrollbars and non-composited scrollbars. For a composited scrollbar,
-PaintArtifactCompositor creates a GraphicsLayer, then ScrollingCoordinator
-creates the cc scrollbar layer which is set as the content layer of the
-GraphicsLayer. For a non-composited scrollbar, ScrollableAreaPainter paints
-the scrollbar into various drawing display items.
-
-In CompositeAfterPaint, during painting, for a non-custom scrollbar we create a
+During painting, for a non-custom scrollbar we create a
 [ScrollbarDisplayItem](../../platform/graphics/paint/scrollbar_display_item.h)
 which contains a [cc::Scrollbar](../../../../cc/input/scrollbar.h) and other
 information that are needed to actually paint the scrollbar into a paint record
@@ -728,8 +515,7 @@
 of type cc::SolidColorScrollbarLayer, cc::PaintedScrollbarLayer or
 cc::PaintedOverlayScrollbarLayer depending on the type of the scrollbar.
 
-In CompositeAfterPaint, custom scrollbars are still painted into drawing
-display items directly.
+Custom scrollbars are still painted into drawing display items directly.
 
 ### PaintNG
 
@@ -738,10 +524,11 @@
 LayoutObjects, LayoutNG will generate an NGFragment tree.
 
 NGPaintFragments are:
-* immutable
-* all coordinates are physical. See
+
+*    immutable
+*    all coordinates are physical. See
 [layout_box_model_object.h](../layout/layout_box_model_object.h).
-* instead of Location(), NGFragment has Offset(), a physical offset from parent
+*    instead of Location(), NGFragment has Offset(), a physical offset from parent
 fragment.
 
 The goal is for PaintNG to eventually paint from NGFragment tree,
diff --git a/third_party/blink/renderer/core/paint/highlight_painting_utils.cc b/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
index 217220f..d0059ec 100644
--- a/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
+++ b/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
@@ -171,39 +171,6 @@
   return element->CachedStyleForPseudoElement(pseudo, pseudo_argument);
 }
 
-// Returns highlight styles for the given node, inheriting through the “tree” of
-// highlight pseudo styles mirroring the originating element tree. None of the
-// returned styles are influenced by originating elements or pseudo-elements.
-scoped_refptr<const ComputedStyle> HighlightPseudoStyle(
-    Node* node,
-    const ComputedStyle& style,
-    PseudoId pseudo,
-    const AtomicString& pseudo_argument = g_null_atom) {
-  if (!RuntimeEnabledFeatures::HighlightInheritanceEnabled()) {
-    return HighlightPseudoStyleWithOriginatingInheritance(node, pseudo,
-                                                          pseudo_argument);
-  }
-
-  if (!style.HighlightData())
-    return nullptr;
-
-  switch (pseudo) {
-    case kPseudoIdSelection:
-      return style.HighlightData()->Selection();
-    case kPseudoIdTargetText:
-      return style.HighlightData()->TargetText();
-    case kPseudoIdSpellingError:
-      return style.HighlightData()->SpellingError();
-    case kPseudoIdGrammarError:
-      return style.HighlightData()->GrammarError();
-    case kPseudoIdHighlight:
-      return style.HighlightData()->CustomHighlight(pseudo_argument);
-    default:
-      NOTREACHED();
-      return nullptr;
-  }
-}
-
 // Paired cascade: when we encounter any highlight colors, we make all other
 // highlight color properties default to initial, rather than the UA default.
 // https://drafts.csswg.org/css-pseudo-4/#highlight-cascade
@@ -234,7 +201,8 @@
   }
 
   scoped_refptr<const ComputedStyle> pseudo_style =
-      HighlightPseudoStyle(node, style, pseudo, pseudo_argument);
+      HighlightPaintingUtils::HighlightPseudoStyle(node, style, pseudo,
+                                                   pseudo_argument);
 
   mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
   if (pseudo_style && (!RuntimeEnabledFeatures::HighlightInheritanceEnabled() ||
@@ -253,6 +221,39 @@
 
 }  // anonymous namespace
 
+// Returns highlight styles for the given node, inheriting through the “tree” of
+// highlight pseudo styles mirroring the originating element tree. None of the
+// returned styles are influenced by originating elements or pseudo-elements.
+scoped_refptr<const ComputedStyle> HighlightPaintingUtils::HighlightPseudoStyle(
+    Node* node,
+    const ComputedStyle& style,
+    PseudoId pseudo,
+    const AtomicString& pseudo_argument) {
+  if (!RuntimeEnabledFeatures::HighlightInheritanceEnabled()) {
+    return HighlightPseudoStyleWithOriginatingInheritance(node, pseudo,
+                                                          pseudo_argument);
+  }
+
+  if (!style.HighlightData())
+    return nullptr;
+
+  switch (pseudo) {
+    case kPseudoIdSelection:
+      return style.HighlightData()->Selection();
+    case kPseudoIdTargetText:
+      return style.HighlightData()->TargetText();
+    case kPseudoIdSpellingError:
+      return style.HighlightData()->SpellingError();
+    case kPseudoIdGrammarError:
+      return style.HighlightData()->GrammarError();
+    case kPseudoIdHighlight:
+      return style.HighlightData()->CustomHighlight(pseudo_argument);
+    default:
+      NOTREACHED();
+      return nullptr;
+  }
+}
+
 Color HighlightPaintingUtils::HighlightBackgroundColor(
     const Document& document,
     const ComputedStyle& style,
@@ -335,8 +336,7 @@
     PseudoId pseudo,
     const GlobalPaintFlags global_paint_flags) {
   return HighlightColor(document, style, node, pseudo,
-                        GetCSSPropertyWebkitTextEmphasisColor(),
-                        global_paint_flags);
+                        GetCSSPropertyTextEmphasisColor(), global_paint_flags);
 }
 
 TextPaintStyle HighlightPaintingUtils::HighlightPaintingStyle(
diff --git a/third_party/blink/renderer/core/paint/highlight_painting_utils.h b/third_party/blink/renderer/core/paint/highlight_painting_utils.h
index 4fb2912..e428ea7 100644
--- a/third_party/blink/renderer/core/paint/highlight_painting_utils.h
+++ b/third_party/blink/renderer/core/paint/highlight_painting_utils.h
@@ -57,6 +57,12 @@
       const AtomicString& pseudo_argument = g_null_atom);
   static absl::optional<Color>
   HighlightTextDecorationColor(const ComputedStyle&, Node*, PseudoId);
+
+  static scoped_refptr<const ComputedStyle> HighlightPseudoStyle(
+      Node* node,
+      const ComputedStyle& style,
+      PseudoId pseudo,
+      const AtomicString& pseudo_argument = g_null_atom);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.cc
index c76ca6f8..177ad822 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.cc
@@ -19,6 +19,7 @@
 #include "third_party/blink/renderer/core/paint/document_marker_painter.h"
 #include "third_party/blink/renderer/core/paint/highlight_painting_utils.h"
 #include "third_party/blink/renderer/core/paint/inline_text_box_painter.h"
+#include "third_party/blink/renderer/core/paint/ng/ng_text_decoration_painter.h"
 #include "third_party/blink/renderer/core/paint/ng/ng_text_painter.h"
 #include "third_party/blink/renderer/core/paint/paint_auto_dark_mode.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
@@ -430,11 +431,27 @@
                 text_style, paint_info_,
                 highlight_pseudo_marker.GetPseudoArgument());
 
+        scoped_refptr<const ComputedStyle> pseudo_style =
+            HighlightPaintingUtils::HighlightPseudoStyle(
+                node_, style_, highlight_pseudo_marker.GetPseudoId(),
+                highlight_pseudo_marker.GetPseudoArgument());
+        PhysicalRect decoration_rect = fragment_item_.LocalRect(
+            text, paint_start_offset, paint_end_offset);
+        decoration_rect.Move(PhysicalOffset(box_origin_));
+        NGTextDecorationPainter decoration_painter(
+            text_painter_, fragment_item_, paint_info_,
+            pseudo_style ? *pseudo_style : style_, final_text_style,
+            decoration_rect, selection_);
+
+        decoration_painter.Begin(NGTextDecorationPainter::kOriginating);
+        decoration_painter.PaintExceptLineThrough();
+
         text_painter_.Paint(paint_start_offset, paint_end_offset,
                             paint_end_offset - paint_start_offset,
                             final_text_style, kInvalidDOMNodeId,
                             auto_dark_mode);
 
+        decoration_painter.PaintOnlyLineThrough();
       } break;
 
       default:
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index c8ab00b..6e324fb 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -102,7 +102,6 @@
 #include "third_party/blink/renderer/core/scroll/scroll_animator_base.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
 #include "third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h"
-#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "ui/base/ui_base_features.h"
@@ -139,9 +138,6 @@
       in_overflow_relayout_(false),
       allow_second_overflow_relayout_(false),
       needs_composited_scrolling_(false),
-      rebuild_horizontal_scrollbar_layer_(false),
-      rebuild_vertical_scrollbar_layer_(false),
-      previous_vertical_scrollbar_on_left_(false),
       needs_scroll_offset_clamp_(false),
       needs_relayout_(false),
       had_horizontal_scrollbar_before_relayout_(false),
@@ -312,38 +308,6 @@
   return &GetLayoutBox()->GetFrame()->GetSmoothScrollSequencer();
 }
 
-cc::Layer* PaintLayerScrollableArea::LayerForHorizontalScrollbar() const {
-  if (auto* graphics_layer = GraphicsLayerForHorizontalScrollbar())
-    return graphics_layer->ContentsLayer();
-  return nullptr;
-}
-
-cc::Layer* PaintLayerScrollableArea::LayerForVerticalScrollbar() const {
-  if (auto* graphics_layer = GraphicsLayerForVerticalScrollbar())
-    return graphics_layer->ContentsLayer();
-  return nullptr;
-}
-
-cc::Layer* PaintLayerScrollableArea::LayerForScrollCorner() const {
-  if (auto* graphics_layer = GraphicsLayerForScrollCorner())
-    return &graphics_layer->CcLayer();
-  return nullptr;
-}
-
-GraphicsLayer* PaintLayerScrollableArea::GraphicsLayerForHorizontalScrollbar()
-    const {
-  return nullptr;
-}
-
-GraphicsLayer* PaintLayerScrollableArea::GraphicsLayerForVerticalScrollbar()
-    const {
-  return nullptr;
-}
-
-GraphicsLayer* PaintLayerScrollableArea::GraphicsLayerForScrollCorner() const {
-  return nullptr;
-}
-
 bool PaintLayerScrollableArea::IsActive() const {
   Page* page = GetLayoutBox()->GetFrame()->GetPage();
   return page && page->GetFocusController().IsActive();
@@ -405,16 +369,6 @@
   return gfx::Rect();
 }
 
-void PaintLayerScrollableArea::SetScrollbarNeedsPaintInvalidation(
-    ScrollbarOrientation orientation) {
-  if (auto* graphics_layer = orientation == kHorizontalScrollbar
-                                 ? GraphicsLayerForHorizontalScrollbar()
-                                 : GraphicsLayerForVerticalScrollbar()) {
-    graphics_layer->InvalidateContents();
-  }
-  ScrollableArea::SetScrollbarNeedsPaintInvalidation(orientation);
-}
-
 void PaintLayerScrollableArea::SetScrollCornerNeedsPaintInvalidation() {
   ScrollableArea::SetScrollCornerNeedsPaintInvalidation();
 }
@@ -610,7 +564,7 @@
 
   if (!box->BackgroundNeedsFullPaintInvalidation()) {
     auto background_paint_location = box->GetBackgroundPaintLocation();
-    bool background_paint_in_graphics_layer =
+    bool background_paint_in_border_box =
         background_paint_location & kBackgroundPaintInBorderBoxSpace;
     bool background_paint_in_scrolling_contents =
         background_paint_location & kBackgroundPaintInContentsSpace;
@@ -620,7 +574,7 @@
     // frame_view->InvalidateBackgroundAttachmentFixedDescendantsOnScroll().
     const auto& background_layers = box->StyleRef().BackgroundLayers();
     if (background_layers.AnyLayerHasLocalAttachmentImage() &&
-        background_paint_in_graphics_layer) {
+        background_paint_in_border_box) {
       // Local-attachment background image scrolls, so needs invalidation if it
       // paints in non-scrolling space.
       box->SetBackgroundNeedsFullPaintInvalidation();
@@ -631,7 +585,7 @@
       box->SetBackgroundNeedsFullPaintInvalidation();
     } else if (background_layers.AnyLayerHasLocalAttachment() &&
                background_layers.AnyLayerUsesContentBox() &&
-               background_paint_in_graphics_layer &&
+               background_paint_in_border_box &&
                (box->PaddingLeft() || box->PaddingTop() ||
                 box->PaddingRight() || box->PaddingBottom())) {
       // Local attachment content box background needs invalidation if there is
@@ -1341,14 +1295,6 @@
 
   UpdateScrollCornerStyle();
 
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    bool vertical_scrollbar_on_left = ShouldPlaceVerticalScrollbarOnLeft();
-    if (vertical_scrollbar_on_left != previous_vertical_scrollbar_on_left_) {
-      rebuild_vertical_scrollbar_layer_ = true;
-      previous_vertical_scrollbar_on_left_ = vertical_scrollbar_on_left;
-    }
-  }
-
   if (!old_style || old_style->UsedColorScheme() != UsedColorScheme() ||
       old_style->ScrollbarWidth() !=
           GetLayoutBox()->StyleRef().ScrollbarWidth()) {
@@ -1925,13 +1871,12 @@
   if (HasOverlayOverflowControls())
     return true;
 
-  // In CAP the global root scrollbars and corner also paint as overlay so that
-  // they appear on top of all content within the viewport. This is important
-  // since these scrollbar's transform parent is the 'overscroll elasticity'
+  // The global root scrollbars and corner also paint as overlay so that they
+  // appear on top of all content within the viewport. This is important since
+  // these scrollbar's transform parent is the 'overscroll elasticity'
   // transform node of the visual viewport, i.e. they don't move during elastic
   // overscroll or on pinch zoom.
-  return (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
-          GetLayoutBox() && GetLayoutBox()->IsGlobalRootScroller());
+  return GetLayoutBox() && GetLayoutBox()->IsGlobalRootScroller();
 }
 
 void PaintLayerScrollableArea::PositionOverflowControls() {
@@ -2398,15 +2343,13 @@
   if (did_scroll_overflow == ScrollsOverflow())
     return;
 
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    // Change of scrolls_overflow may affect whether we create ScrollTranslation
-    // which is referenced from ScrollDisplayItem. Invalidate scrollbars (but
-    // not their parts) to repaint the display item.
-    if (auto* scrollbar = HorizontalScrollbar())
-      scrollbar->SetNeedsPaintInvalidation(kNoPart);
-    if (auto* scrollbar = VerticalScrollbar())
-      scrollbar->SetNeedsPaintInvalidation(kNoPart);
-  }
+  // Change of scrolls_overflow may affect whether we create ScrollTranslation
+  // which is referenced from ScrollDisplayItem. Invalidate scrollbars (but not
+  // their parts) to repaint the display item.
+  if (auto* scrollbar = HorizontalScrollbar())
+    scrollbar->SetNeedsPaintInvalidation(kNoPart);
+  if (auto* scrollbar = VerticalScrollbar())
+    scrollbar->SetNeedsPaintInvalidation(kNoPart);
 
   if (RuntimeEnabledFeatures::ImplicitRootScrollerEnabled() &&
       scrolls_overflow_) {
@@ -2572,25 +2515,13 @@
 }
 
 bool PaintLayerScrollableArea::UsesCompositedScrolling() const {
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
-    return GetLayoutBox()->UsesCompositedScrolling();
-  return ScrollableArea::UsesCompositedScrolling();
+  return GetLayoutBox()->UsesCompositedScrolling();
 }
 
 void PaintLayerScrollableArea::UpdateNeedsCompositedScrolling(
     bool force_prefer_compositing_to_lcd_text) {
-#if DCHECK_IS_ON()
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    DCHECK_EQ(DocumentLifecycle::kInPrePaint,
-              GetDocument()->Lifecycle().GetState());
-  } else {
-    DCHECK(GetDocument()->Lifecycle().GetState() ==
-               DocumentLifecycle::kInCompositingInputsUpdate ||
-           GetDocument()->Lifecycle().GetState() ==
-               DocumentLifecycle::kInCompositingAssignmentsUpdate)
-        << " " << GetDocument()->Lifecycle().ToString();
-  }
-#endif
+  DCHECK_EQ(DocumentLifecycle::kInPrePaint,
+            GetDocument()->Lifecycle().GetState());
 
   bool new_needs_composited_scrolling =
       ComputeNeedsCompositedScrolling(force_prefer_compositing_to_lcd_text);
@@ -2598,8 +2529,7 @@
     return;
 
   needs_composited_scrolling_ = new_needs_composited_scrolling;
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
-    GetLayoutBox()->SetShouldCheckForPaintInvalidation();
+  GetLayoutBox()->SetShouldCheckForPaintInvalidation();
 }
 
 bool PaintLayerScrollableArea::VisualViewportSuppliesScrollbars() const {
@@ -2625,11 +2555,6 @@
   return false;
 }
 
-void PaintLayerScrollableArea::ResetRebuildScrollbarLayerFlags() {
-  rebuild_horizontal_scrollbar_layer_ = false;
-  rebuild_vertical_scrollbar_layer_ = false;
-}
-
 cc::AnimationHost* PaintLayerScrollableArea::GetCompositorAnimationHost()
     const {
   return layer_->GetLayoutObject().GetFrameView()->GetCompositorAnimationHost();
@@ -2725,10 +2650,6 @@
     return;
 
   ScrollableArea()->SetScrollbarNeedsPaintInvalidation(orientation);
-  if (orientation == kHorizontalScrollbar)
-    ScrollableArea()->rebuild_horizontal_scrollbar_layer_ = true;
-  else
-    ScrollableArea()->rebuild_vertical_scrollbar_layer_ = true;
 
   if (!scrollbar->IsCustomScrollbar())
     ScrollableArea()->WillRemoveScrollbar(*scrollbar, orientation);
@@ -2931,10 +2852,7 @@
     layer_->DirtyStackingContextZOrderLists();
   }
 
-  if (!scrollbar.IsCustomScrollbar() &&
-      !(orientation == kHorizontalScrollbar
-            ? GraphicsLayerForHorizontalScrollbar()
-            : GraphicsLayerForVerticalScrollbar())) {
+  if (!scrollbar.IsCustomScrollbar()) {
     ObjectPaintInvalidator(*GetLayoutBox())
         .SlowSetPaintingLayerNeedsRepaintAndInvalidateDisplayItemClient(
             scrollbar, PaintInvalidationReason::kScrollControl);
@@ -2961,7 +2879,6 @@
 
 bool PaintLayerScrollableArea::ShouldDirectlyCompositeScrollbar(
     const Scrollbar& scrollbar) const {
-  DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
   // Don't composite non-scrollable scrollbars.
   if (!scrollbar.Maximum())
     return false;
@@ -2989,7 +2906,6 @@
     const PaintInvalidatorContext& context,
     bool needs_paint_invalidation,
     Scrollbar* scrollbar,
-    GraphicsLayer* graphics_layer,
     bool& previously_was_overlay,
     bool& previously_was_directly_composited,
     gfx::Rect& visual_rect) {
@@ -3030,7 +2946,7 @@
 
   previously_was_overlay = is_overlay;
 
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && scrollbar) {
+  if (scrollbar) {
     bool directly_composited = ShouldDirectlyCompositeScrollbar(*scrollbar);
     if (directly_composited != previously_was_directly_composited) {
       needs_paint_invalidation = true;
@@ -3045,10 +2961,7 @@
   if (scrollbar &&
       ScrollControlNeedsPaintInvalidation(new_visual_rect, visual_rect,
                                           needs_paint_invalidation)) {
-    if (graphics_layer)
-      graphics_layer->Invalidate(PaintInvalidationReason::kScrollControl);
-    else
-      context.painting_layer->SetNeedsRepaint();
+    context.painting_layer->SetNeedsRepaint();
     scrollbar->Invalidate(PaintInvalidationReason::kScrollControl);
     if (auto* custom_scrollbar = DynamicTo<CustomScrollbar>(scrollbar))
       custom_scrollbar->InvalidateDisplayItemClientsOfScrollbarParts();
@@ -3064,13 +2977,11 @@
 
   InvalidatePaintOfScrollbarIfNeeded(
       context, HorizontalScrollbarNeedsPaintInvalidation(),
-      HorizontalScrollbar(), GraphicsLayerForHorizontalScrollbar(),
-      horizontal_scrollbar_previously_was_overlay_,
+      HorizontalScrollbar(), horizontal_scrollbar_previously_was_overlay_,
       horizontal_scrollbar_previously_was_directly_composited_,
       horizontal_scrollbar_visual_rect_);
   InvalidatePaintOfScrollbarIfNeeded(
       context, VerticalScrollbarNeedsPaintInvalidation(), VerticalScrollbar(),
-      GraphicsLayerForVerticalScrollbar(),
       vertical_scrollbar_previously_was_overlay_,
       vertical_scrollbar_previously_was_directly_composited_,
       vertical_scrollbar_visual_rect_);
@@ -3098,14 +3009,11 @@
       ObjectPaintInvalidator(*resizer).InvalidateDisplayItemClient(
           *resizer, PaintInvalidationReason::kScrollControl);
     }
-    if (auto* graphics_layer = GraphicsLayerForScrollCorner()) {
-      graphics_layer->Invalidate(PaintInvalidationReason::kScrollControl);
-    } else {
-      context.painting_layer->SetNeedsRepaint();
-      ObjectPaintInvalidator(*GetLayoutBox())
-          .InvalidateDisplayItemClient(GetScrollCornerDisplayItemClient(),
-                                       PaintInvalidationReason::kGeometry);
-    }
+
+    context.painting_layer->SetNeedsRepaint();
+    ObjectPaintInvalidator(*GetLayoutBox())
+        .InvalidateDisplayItemClient(GetScrollCornerDisplayItemClient(),
+                                     PaintInvalidationReason::kGeometry);
   }
 
   ClearNeedsPaintInvalidationForScrollControls();
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
index 3a8b14b..4bb0ee1 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
@@ -60,7 +60,6 @@
 enum ResizerHitTestType { kResizerForPointer, kResizerForTouch };
 
 class ComputedStyle;
-class GraphicsLayer;
 class HitTestResult;
 class LayoutBox;
 class LayoutCustomScrollbarPart;
@@ -95,33 +94,28 @@
 // scrollLeft).
 //
 // The size and scroll origin of the scrollable area are based on layout
-// dimensions. They are recomputed after layout in updateScrollDimensions.
+// dimensions. They are recomputed after layout in |UpdateScrollDimensions|.
 //
-// updateScrollDimensions also determines if scrollbars need to be allocated,
+// |UpdateScrollDimensions| also determines if scrollbars need to be allocated,
 // destroyed or updated as a result of layout. This is based on the value of the
 // 'overflow' property. Having non-overlay scrollbars automatically allocates a
-// scrollcorner (m_scrollCorner), which is used to style the intersection of the
-// two scrollbars.
+// scrollcorner (|scroll_corner_|), which is used to style the intersection of
+// the two scrollbars.
 //
 // Note that scrollbars are placed based on the LayoutBox's computed
 // 'direction'. See https://webkit.org/b/54623 for some context.
 //
-// The ‘resize' property allocates a resizer (m_resizer), which is overlaid on
-// top of the scroll corner. It is used to resize an element using the mouse.
-//
-// The scrollbars and scroll corner can also be hardware accelerated
-// and thus get their own GraphicsLayer (see the layerFor* functions).
-// This only happens if the associated PaintLayer is itself composited.
-//
+// The ‘resize' property allocates a resizer (|resizer_|), which is overlaid on
+// top of the scroll corner. It is used to resize an element using the mouse. A
+// resizer can exist when there are no scrollbars.
 //
 // ***** OVERLAY OVERFLOW CONTROLS *****
-// Overlay overflow controls are painted on top of the box's content. As such
-// they don't use any space in the box. Software overlay overflow controls are
-// painted by PaintLayerPainter::PaintOverlayOverflowControlsForFragments after
-// all content as part of a separate tree traversal. The reason for this 2nd
-// traversal is that they need to be painted on top of everything. Hardware
-// accelerated overlay overflow controls are painted into their associated
-// GraphicsLayers by CompositedLayerMapping::PaintScrollableArea.
+// Overlay overflow controls are painted on top of the box's content, including
+// overlay scrollbars and resizers (regardless of whether the scrollbars are
+// overlaid). As such, they don't use any space in the box. Overlay overflow
+// controls are painted by
+// |PaintLayerPainter::PaintOverlayOverflowControlsForFragments| after all
+// scrolling contents.
 class CORE_EXPORT PaintLayerScrollableArea final
     : public GarbageCollected<PaintLayerScrollableArea>,
       public ScrollableArea {
@@ -298,21 +292,10 @@
 
   void DidCompositorScroll(const gfx::PointF&) override;
 
-  // GraphicsLayers for the scrolling components.
-  // Any function can return nullptr if they are not accelerated.
-  GraphicsLayer* GraphicsLayerForHorizontalScrollbar() const;
-  GraphicsLayer* GraphicsLayerForVerticalScrollbar() const;
-  GraphicsLayer* GraphicsLayerForScrollCorner() const;
-
-  cc::Layer* LayerForHorizontalScrollbar() const override;
-  cc::Layer* LayerForVerticalScrollbar() const override;
-  cc::Layer* LayerForScrollCorner() const override;
-
   bool ShouldScrollOnMainThread() const override;
   bool IsActive() const override;
   bool IsScrollCornerVisible() const override;
   gfx::Rect ScrollCornerRect() const override;
-  void SetScrollbarNeedsPaintInvalidation(ScrollbarOrientation) override;
   void SetScrollCornerNeedsPaintInvalidation() override;
   gfx::Rect ConvertFromScrollbarToContainingEmbeddedContentView(
       const Scrollbar&,
@@ -507,14 +490,6 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> GetTimerTaskRunner() const final;
 
-  bool ShouldRebuildHorizontalScrollbarLayer() const {
-    return rebuild_horizontal_scrollbar_layer_;
-  }
-  bool ShouldRebuildVerticalScrollbarLayer() const {
-    return rebuild_vertical_scrollbar_layer_;
-  }
-  void ResetRebuildScrollbarLayerFlags();
-
   // Did DelayScrollOffsetClampScope prevent us from running
   // clampScrollOffsetsAfterLayout() in updateAfterLayout()?
   bool NeedsScrollOffsetClamp() const { return needs_scroll_offset_clamp_; }
@@ -728,7 +703,6 @@
       const PaintInvalidatorContext&,
       bool needs_paint_invalidation,
       Scrollbar* scrollbar,
-      GraphicsLayer* graphics_layer,
       bool& previously_was_overlay,
       bool& previously_was_directly_composited,
       gfx::Rect& visual_rect);
@@ -753,13 +727,6 @@
   // no longer need this bit.
   unsigned needs_composited_scrolling_ : 1;
 
-  // Set to indicate that a scrollbar layer, if present, needs to be rebuilt
-  // in the next compositing update because the underlying blink::Scrollbar
-  // instance has been reconstructed.
-  unsigned rebuild_horizontal_scrollbar_layer_ : 1;
-  unsigned rebuild_vertical_scrollbar_layer_ : 1;
-  unsigned previous_vertical_scrollbar_on_left_ : 1;
-
   unsigned needs_scroll_offset_clamp_ : 1;
   unsigned needs_relayout_ : 1;
   unsigned had_horizontal_scrollbar_before_relayout_ : 1;
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
index 93125ee..e4f7bcb 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
@@ -1435,28 +1435,18 @@
     const auto& visual_viewport =
         document.View()->GetPage()->GetVisualViewport();
 
-    if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-      const auto& paint_chunks = ContentPaintChunks();
-      bool found_root_scrollbar = false;
-      for (const auto& chunk : paint_chunks) {
-        if (chunk.id ==
-            PaintChunk::Id(root_scrollable->VerticalScrollbar()->Id(),
-                           DisplayItem::kCustomScrollbarHitTest)) {
-          EXPECT_EQ(
-              &chunk.properties.Transform(),
-              visual_viewport.GetOverscrollElasticityTransformNode()->Parent());
-          found_root_scrollbar = true;
-        }
+    const auto& paint_chunks = ContentPaintChunks();
+    bool found_root_scrollbar = false;
+    for (const auto& chunk : paint_chunks) {
+      if (chunk.id == PaintChunk::Id(root_scrollable->VerticalScrollbar()->Id(),
+                                     DisplayItem::kCustomScrollbarHitTest)) {
+        EXPECT_EQ(
+            &chunk.properties.Transform(),
+            visual_viewport.GetOverscrollElasticityTransformNode()->Parent());
+        found_root_scrollbar = true;
       }
-      EXPECT_TRUE(found_root_scrollbar);
-    } else {
-      auto* vertical_scrollbar_layer =
-          root_scrollable->GraphicsLayerForVerticalScrollbar();
-      ASSERT_TRUE(vertical_scrollbar_layer);
-      EXPECT_EQ(
-          &vertical_scrollbar_layer->GetPropertyTreeState().Transform(),
-          visual_viewport.GetOverscrollElasticityTransformNode()->Parent());
     }
+    EXPECT_TRUE(found_root_scrollbar);
   }
 
   // Non root scrollbar should use scroller's transform node.
@@ -1470,27 +1460,17 @@
                                 .FirstFragment()
                                 .LocalBorderBoxProperties();
 
-    if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-      const auto& paint_chunks = ContentPaintChunks();
-      bool found_subscroller_scrollbar = false;
-      for (const auto& chunk : paint_chunks) {
-        if (chunk.id ==
-            PaintChunk::Id(scrollable_area->VerticalScrollbar()->Id(),
-                           DisplayItem::kCustomScrollbarHitTest)) {
-          EXPECT_EQ(&chunk.properties.Transform(),
-                    &paint_properties.Transform());
+    const auto& paint_chunks = ContentPaintChunks();
+    bool found_subscroller_scrollbar = false;
+    for (const auto& chunk : paint_chunks) {
+      if (chunk.id == PaintChunk::Id(scrollable_area->VerticalScrollbar()->Id(),
+                                     DisplayItem::kCustomScrollbarHitTest)) {
+        EXPECT_EQ(&chunk.properties.Transform(), &paint_properties.Transform());
 
-          found_subscroller_scrollbar = true;
-        }
+        found_subscroller_scrollbar = true;
       }
-      EXPECT_TRUE(found_subscroller_scrollbar);
-    } else {
-      auto* vertical_scrollbar_layer =
-          scrollable_area->GraphicsLayerForVerticalScrollbar();
-      ASSERT_TRUE(vertical_scrollbar_layer);
-      EXPECT_EQ(&vertical_scrollbar_layer->GetPropertyTreeState().Transform(),
-                &paint_properties.Transform());
     }
+    EXPECT_TRUE(found_subscroller_scrollbar);
   }
 }
 
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 92a55bf..83bb64f 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
@@ -837,14 +837,7 @@
   EXPECT_FALSE(visual_viewport.LayerForHorizontalScrollbar());
   EXPECT_FALSE(visual_viewport.LayerForVerticalScrollbar());
   ASSERT_TRUE(GetLayoutView().GetScrollableArea());
-  auto* scrollbar_layer = GetLayoutView()
-                              .GetScrollableArea()
-                              ->GraphicsLayerForHorizontalScrollbar();
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    ASSERT_TRUE(scrollbar_layer);
-    EXPECT_EQ(&TransformPaintPropertyNode::Root(),
-              &scrollbar_layer->GetPropertyTreeState().Transform());
-  } else {
+  {
     auto& chunk = *(ContentPaintChunks().begin() + 1);
     EXPECT_EQ(DisplayItem::kScrollbarHorizontal, chunk.id.type);
     EXPECT_EQ(&TransformPaintPropertyNode::Root(),
@@ -858,11 +851,7 @@
 
   UpdateAllLifecyclePhasesForTest();
   EXPECT_TRUE(visual_viewport.GetDeviceEmulationTransformNode());
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    ASSERT_TRUE(scrollbar_layer);
-    EXPECT_EQ(visual_viewport.GetDeviceEmulationTransformNode(),
-              &scrollbar_layer->GetPropertyTreeState().Transform());
-  } else {
+  {
     auto& chunk = *(ContentPaintChunks().begin() + 1);
     EXPECT_EQ(DisplayItem::kScrollbarHorizontal, chunk.id.type);
     EXPECT_EQ(visual_viewport.GetDeviceEmulationTransformNode(),
@@ -875,11 +864,7 @@
 
   UpdateAllLifecyclePhasesForTest();
   EXPECT_FALSE(visual_viewport.GetDeviceEmulationTransformNode());
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    ASSERT_TRUE(scrollbar_layer);
-    EXPECT_EQ(&TransformPaintPropertyNode::Root(),
-              &scrollbar_layer->GetPropertyTreeState().Transform());
-  } else {
+  {
     auto& chunk = *(ContentPaintChunks().begin() + 1);
     EXPECT_EQ(DisplayItem::kScrollbarHorizontal, chunk.id.type);
     EXPECT_EQ(&TransformPaintPropertyNode::Root(),
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 658df0b..907c9dda 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
@@ -756,59 +756,62 @@
       inner_fragmentainer_idx = PreviousInnerFragmentainerIndex(fragment);
     context.current_fragmentainer.fragmentainer_idx = *inner_fragmentainer_idx;
 
-    if (UNLIKELY(!context.tree_builder_context)) {
-      WalkChildren(actual_parent, box_fragment, context);
-      continue;
-    }
+    PaintPropertyTreeBuilderFragmentContext::ContainingBlockContext*
+        containing_block_context = nullptr;
+    if (LIKELY(context.tree_builder_context)) {
+      containing_block_context =
+          &context.tree_builder_context->fragments[0].current;
+      containing_block_context->paint_offset += child.offset;
 
-    auto* containing_block_context =
-        &context.tree_builder_context->fragments[0].current;
-    containing_block_context->paint_offset += child.offset;
+      const PhysicalOffset paint_offset =
+          containing_block_context->paint_offset;
+      // Keep track of the paint offset at the fragmentainer. This is needed
+      // when entering OOF descendants. OOFs have the nearest fragmentainer as
+      // their containing block, so when entering them during LayoutObject tree
+      // traversal, we have to compensate for this.
+      containing_block_context->paint_offset_for_oof_in_fragmentainer =
+          paint_offset;
 
-    const PhysicalOffset paint_offset = containing_block_context->paint_offset;
-    // Keep track of the paint offset at the fragmentainer. This is needed
-    // when entering OOF descendants. OOFs have the nearest fragmentainer as
-    // their containing block, so when entering them during LayoutObject tree
-    // traversal, we have to compensate for this.
-    containing_block_context->paint_offset_for_oof_in_fragmentainer =
-        paint_offset;
-
-    if (flow_thread) {
-      // Create corresponding |FragmentData|. Hit-testing needs
-      // |FragmentData.PaintOffset|.
-      if (fragmentainer_fragment_data) {
-        DCHECK(!box_fragment->IsFirstForNode());
+      if (flow_thread) {
+        // Create corresponding |FragmentData|. Hit-testing needs
+        // |FragmentData.PaintOffset|.
+        if (fragmentainer_fragment_data) {
+          DCHECK(!box_fragment->IsFirstForNode());
 #if DCHECK_IS_ON()
-        DCHECK_EQ(fragmentainer_owner_box, box_fragment->OwnerLayoutBox());
+          DCHECK_EQ(fragmentainer_owner_box, box_fragment->OwnerLayoutBox());
 #endif
-        fragmentainer_fragment_data =
-            &fragmentainer_fragment_data->EnsureNextFragment();
-      } else {
-        const LayoutBox* owner_box = box_fragment->OwnerLayoutBox();
-#if DCHECK_IS_ON()
-        DCHECK(!fragmentainer_owner_box);
-        fragmentainer_owner_box = owner_box;
-#endif
-        fragmentainer_fragment_data =
-            &owner_box->GetMutableForPainting().FirstFragment();
-        if (box_fragment->IsFirstForNode()) {
-          fragmentainer_fragment_data->ClearNextFragment();
-        } else {
-          // |box_fragment| is nested in another fragmentainer, and that it is
-          // the first one in this loop, but not the first one for the
-          // |LayoutObject|. Append a new |FragmentData| to the last one.
           fragmentainer_fragment_data =
-              &fragmentainer_fragment_data->LastFragment().EnsureNextFragment();
+              &fragmentainer_fragment_data->EnsureNextFragment();
+        } else {
+          const LayoutBox* owner_box = box_fragment->OwnerLayoutBox();
+#if DCHECK_IS_ON()
+          DCHECK(!fragmentainer_owner_box);
+          fragmentainer_owner_box = owner_box;
+#endif
+          fragmentainer_fragment_data =
+              &owner_box->GetMutableForPainting().FirstFragment();
+          if (box_fragment->IsFirstForNode()) {
+            fragmentainer_fragment_data->ClearNextFragment();
+          } else {
+            // |box_fragment| is nested in another fragmentainer, and that it is
+            // the first one in this loop, but not the first one for the
+            // |LayoutObject|. Append a new |FragmentData| to the last one.
+            fragmentainer_fragment_data =
+                &fragmentainer_fragment_data->LastFragment()
+                     .EnsureNextFragment();
+          }
         }
+        fragmentainer_fragment_data->SetPaintOffset(paint_offset);
+        fragmentainer_fragment_data->SetFragmentID(
+            context.current_fragmentainer.fragmentainer_idx);
       }
-      fragmentainer_fragment_data->SetPaintOffset(paint_offset);
-      fragmentainer_fragment_data->SetFragmentID(
-          context.current_fragmentainer.fragmentainer_idx);
     }
 
     WalkChildren(actual_parent, box_fragment, context);
 
-    containing_block_context->paint_offset -= child.offset;
+    if (containing_block_context)
+      containing_block_context->paint_offset -= child.offset;
+
     (*inner_fragmentainer_idx)++;
   }
 
@@ -829,8 +832,14 @@
   // means that their containing block lives outside the fragmentation context
   // root. Walk these missed fixepos elements now.
   if (!pending_fixedpos_missables_.IsEmpty()) {
-    for (const auto& fixedpos : pending_fixedpos_missables_) {
+    // First make a copy, clear the original set, and then walk the copy. There
+    // may be descendants attempting to walk the set as well, which will cause
+    // an infinite recursion.
+    HeapHashSet<Member<const LayoutObject>> copy;
+    copy.swap(pending_fixedpos_missables_);
+    for (const auto& fixedpos : copy) {
       DCHECK(!walked_fixedpos_.Contains(fixedpos));
+      walked_fixedpos_.insert(fixedpos);
       Walk(*fixedpos, context, /* pre_paint_info */ nullptr);
     }
   }
diff --git a/third_party/blink/renderer/core/paint/scrollable_area_painter.cc b/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
index 3f5acc75..75b7356 100644
--- a/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
+++ b/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
@@ -19,7 +19,6 @@
 #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
-#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h"
 #include "third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.h"
@@ -40,7 +39,7 @@
   if (!cull_rect.Intersects(visual_rect))
     return;
 
-  const auto& client = DisplayItemClientForCorner();
+  const auto& client = GetScrollableArea().GetScrollCornerDisplayItemClient();
   if (const auto* resizer = GetScrollableArea().Resizer()) {
     CustomScrollbarTheme::PaintIntoRect(*resizer, context,
                                         PhysicalRect(visual_rect));
@@ -83,8 +82,8 @@
   gfx::Rect touch_rect = scrollable_area_->ResizerCornerRect(kResizerForTouch);
   touch_rect.Offset(ToRoundedVector2d(paint_offset));
   context.GetPaintController().RecordScrollHitTestData(
-      DisplayItemClientForCorner(), DisplayItem::kResizerScrollHitTest, nullptr,
-      touch_rect);
+      GetScrollableArea().GetScrollCornerDisplayItemClient(),
+      DisplayItem::kResizerScrollHitTest, nullptr, touch_rect);
 }
 
 void ScrollableAreaPainter::DrawPlatformResizerImage(
@@ -181,8 +180,7 @@
     clip = properties->OverflowControlsClip();
 
   const TransformPaintPropertyNodeOrAlias* transform = nullptr;
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
-      box.IsGlobalRootScroller()) {
+  if (box.IsGlobalRootScroller()) {
     LocalFrameView* frame_view = box.GetFrameView();
     DCHECK(frame_view);
     const auto* page = frame_view->GetPage();
@@ -207,25 +205,21 @@
                                           box, DisplayItem::kOverflowControls);
   }
 
-  if (GetScrollableArea().HorizontalScrollbar() &&
-      !GetScrollableArea().GraphicsLayerForHorizontalScrollbar()) {
+  if (GetScrollableArea().HorizontalScrollbar()) {
     PaintScrollbar(context, *GetScrollableArea().HorizontalScrollbar(),
                    paint_offset, paint_info.GetCullRect());
   }
-  if (GetScrollableArea().VerticalScrollbar() &&
-      !GetScrollableArea().GraphicsLayerForVerticalScrollbar()) {
+  if (GetScrollableArea().VerticalScrollbar()) {
     PaintScrollbar(context, *GetScrollableArea().VerticalScrollbar(),
                    paint_offset, paint_info.GetCullRect());
   }
 
-  if (!GetScrollableArea().GraphicsLayerForScrollCorner()) {
-    // We fill our scroll corner with white if we have a scrollbar that doesn't
-    // run all the way up to the edge of the box.
-    PaintScrollCorner(context, paint_offset, paint_info.GetCullRect());
+  // We fill our scroll corner with white if we have a scrollbar that doesn't
+  // run all the way up to the edge of the box.
+  PaintScrollCorner(context, paint_offset, paint_info.GetCullRect());
 
-    // Paint our resizer last, since it sits on top of the scroll corner.
-    PaintResizer(context, paint_offset, paint_info.GetCullRect());
-  }
+  // Paint our resizer last, since it sits on top of the scroll corner.
+  PaintResizer(context, paint_offset, paint_info.GetCullRect());
 }
 
 void ScrollableAreaPainter::PaintScrollbar(GraphicsContext& context,
@@ -263,11 +257,6 @@
     return;
   }
 
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    scrollbar.Paint(context, paint_offset);
-    return;
-  }
-
   if (context.GetPaintController().UseCachedItemIfPossible(scrollbar, type))
     return;
 
@@ -309,7 +298,7 @@
     NOTREACHED();
   }
 
-  const auto& client = DisplayItemClientForCorner();
+  const auto& client = GetScrollableArea().GetScrollCornerDisplayItemClient();
   theme->PaintScrollCorner(context, GetScrollableArea().VerticalScrollbar(),
                            client, visual_rect,
                            GetScrollableArea().UsedColorScheme());
@@ -319,12 +308,4 @@
   return *scrollable_area_;
 }
 
-const DisplayItemClient& ScrollableAreaPainter::DisplayItemClientForCorner()
-    const {
-  if (const auto* graphics_layer =
-          GetScrollableArea().GraphicsLayerForScrollCorner())
-    return *graphics_layer;
-  return GetScrollableArea().GetScrollCornerDisplayItemClient();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/scrollable_area_painter.h b/third_party/blink/renderer/core/paint/scrollable_area_painter.h
index 6fbd1af..ab6b809 100644
--- a/third_party/blink/renderer/core/paint/scrollable_area_painter.h
+++ b/third_party/blink/renderer/core/paint/scrollable_area_painter.h
@@ -15,7 +15,6 @@
 namespace blink {
 
 class CullRect;
-class DisplayItemClient;
 class GraphicsContext;
 class Scrollbar;
 struct PaintInfo;
@@ -55,7 +54,6 @@
                                 const gfx::Rect& resizer_corner_rect);
 
   PaintLayerScrollableArea& GetScrollableArea() const;
-  const DisplayItemClient& DisplayItemClientForCorner() const;
 
   PaintLayerScrollableArea* scrollable_area_;
 };
diff --git a/third_party/blink/renderer/core/paint/text_painter_base.cc b/third_party/blink/renderer/core/paint/text_painter_base.cc
index 6d14ed7e..c24cb3c 100644
--- a/third_party/blink/renderer/core/paint/text_painter_base.cc
+++ b/third_party/blink/renderer/core/paint/text_painter_base.cc
@@ -167,7 +167,7 @@
     text_style.stroke_color =
         style.VisitedDependentColor(GetCSSPropertyWebkitTextStrokeColor());
     text_style.emphasis_mark_color =
-        style.VisitedDependentColor(GetCSSPropertyWebkitTextEmphasisColor());
+        style.VisitedDependentColor(GetCSSPropertyTextEmphasisColor());
     text_style.shadow = style.TextShadow();
 
     // Adjust text color when printing with a white background.
diff --git a/third_party/blink/renderer/core/paint/text_painter_test.cc b/third_party/blink/renderer/core/paint/text_painter_test.cc
index be60d51..db8cfeb 100644
--- a/third_party/blink/renderer/core/paint/text_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/text_painter_test.cc
@@ -79,7 +79,7 @@
   GetDocument().body()->SetInlineStyleProperty(
       CSSPropertyID::kWebkitTextStrokeColor, CSSValueID::kLime);
   GetDocument().body()->SetInlineStyleProperty(
-      CSSPropertyID::kWebkitTextEmphasisColor, CSSValueID::kBlue);
+      CSSPropertyID::kTextEmphasisColor, CSSValueID::kBlue);
   GetDocument().body()->SetInlineStyleProperty(
       CSSPropertyID::kWebkitTextStrokeWidth, 4,
       CSSPrimitiveValue::UnitType::kPixels);
@@ -109,7 +109,7 @@
   GetDocument().body()->SetInlineStyleProperty(
       CSSPropertyID::kWebkitTextStrokeColor, CSSValueID::kLime);
   GetDocument().body()->SetInlineStyleProperty(
-      CSSPropertyID::kWebkitTextEmphasisColor, CSSValueID::kBlue);
+      CSSPropertyID::kTextEmphasisColor, CSSValueID::kBlue);
   GetDocument().body()->SetInlineStyleProperty(
       CSSPropertyID::kWebkitTextStrokeWidth, 4,
       CSSPrimitiveValue::UnitType::kPixels);
@@ -134,7 +134,7 @@
   GetDocument().body()->SetInlineStyleProperty(
       CSSPropertyID::kWebkitTextStrokeColor, CSSValueID::kLime);
   GetDocument().body()->SetInlineStyleProperty(
-      CSSPropertyID::kWebkitTextEmphasisColor, CSSValueID::kBlue);
+      CSSPropertyID::kTextEmphasisColor, CSSValueID::kBlue);
   GetDocument().body()->SetInlineStyleProperty(
       CSSPropertyID::kWebkitPrintColorAdjust, CSSValueID::kEconomy);
   GetDocument().GetSettings()->SetShouldPrintBackgrounds(false);
@@ -159,7 +159,7 @@
   GetDocument().body()->SetInlineStyleProperty(
       CSSPropertyID::kWebkitTextStrokeColor, "rgb(220, 255, 220)");
   GetDocument().body()->SetInlineStyleProperty(
-      CSSPropertyID::kWebkitTextEmphasisColor, "rgb(220, 220, 255)");
+      CSSPropertyID::kTextEmphasisColor, "rgb(220, 220, 255)");
   GetDocument().body()->SetInlineStyleProperty(
       CSSPropertyID::kWebkitPrintColorAdjust, CSSValueID::kEconomy);
   GetDocument().GetSettings()->SetShouldPrintBackgrounds(false);
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc
index e38892e..73f6068 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -55,7 +55,6 @@
 #include "third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
-#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/timer.h"
@@ -110,7 +109,6 @@
       scrollbar_captured_(false),
       mouse_over_scrollbar_(false),
       has_been_disposed_(false),
-      uses_composited_scrolling_(false),
       compositor_task_runner_(std::move(compositor_task_runner)) {
   DCHECK(compositor_task_runner_);
 }
@@ -488,13 +486,6 @@
   return true;
 }
 
-// NOTE: Only called from Internals for testing.
-void ScrollableArea::UpdateScrollOffsetFromInternals(
-    const gfx::Vector2d& offset) {
-  ScrollOffsetChanged(ScrollOffset(offset),
-                      mojom::blink::ScrollType::kProgrammatic);
-}
-
 void ScrollableArea::RegisterScrollCompleteCallback(ScrollCallback callback) {
   DCHECK(!HasBeenDisposed());
   pending_scroll_complete_callbacks_.push_back(std::move(callback));
@@ -645,21 +636,12 @@
   else
     vertical_scrollbar_needs_paint_invalidation_ = true;
 
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    // GetLayoutBox() may be null in some unit tests.
-    if (auto* box = GetLayoutBox()) {
-      auto* frame_view = GetLayoutBox()->GetFrameView();
-      if (auto* compositor = frame_view->GetPaintArtifactCompositor()) {
-        compositor->SetScrollbarNeedsDisplay(
-            GetScrollbarElementId(orientation));
-      }
+  // GetLayoutBox() may be null in some unit tests.
+  if (auto* box = GetLayoutBox()) {
+    auto* frame_view = GetLayoutBox()->GetFrameView();
+    if (auto* compositor = frame_view->GetPaintArtifactCompositor()) {
+      compositor->SetScrollbarNeedsDisplay(GetScrollbarElementId(orientation));
     }
-  } else {
-    cc::Layer* layer = orientation == kHorizontalScrollbar
-                           ? LayerForHorizontalScrollbar()
-                           : LayerForVerticalScrollbar();
-    if (layer)
-      layer->SetNeedsDisplay();
   }
 
   ScrollControlWasSetNeedsPaintInvalidation();
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.h b/third_party/blink/renderer/core/scroll/scrollable_area.h
index 82dc3b10..92341851 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.h
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.h
@@ -280,8 +280,8 @@
   // Note that this function just set the scrollbar itself needs repaint by
   // blink during paint, but doesn't set the scrollbar parts (thumb or track)
   // of an accelerated scrollbar needing repaint by the compositor.
-  // Use Scrollbar::SetNeedsPaintInvaldiation() instead.
-  virtual void SetScrollbarNeedsPaintInvalidation(ScrollbarOrientation);
+  // Use Scrollbar::SetNeedsPaintInvalidation() instead.
+  void SetScrollbarNeedsPaintInvalidation(ScrollbarOrientation);
 
   virtual void SetScrollCornerNeedsPaintInvalidation();
 
@@ -389,9 +389,6 @@
 
   virtual bool ScrollAnimatorEnabled() const { return false; }
 
-  // NOTE: Only called from Internals for testing.
-  void UpdateScrollOffsetFromInternals(const gfx::Vector2d&);
-
   gfx::Vector2d ClampScrollOffset(const gfx::Vector2d&) const;
   ScrollOffset ClampScrollOffset(const ScrollOffset&) const;
 
@@ -404,12 +401,8 @@
   virtual void DeregisterForAnimation() {}
 
   virtual bool UsesCompositedScrolling() const {
-    DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
-    return uses_composited_scrolling_;
-  }
-  void SetUsesCompositedScrolling(bool uses_composited_scrolling) {
-    DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
-    uses_composited_scrolling_ = uses_composited_scrolling;
+    NOTREACHED();
+    return false;
   }
   virtual bool ShouldScrollOnMainThread() const { return false; }
   void MainThreadScrollingDidChange();
@@ -446,7 +439,6 @@
 
   // Note that in CompositeAfterPaint, these methods always return nullptr
   // except for VisualViewport.
-  virtual cc::Layer* LayerForScrolling() const { return nullptr; }
   virtual cc::Layer* LayerForHorizontalScrollbar() const { return nullptr; }
   virtual cc::Layer* LayerForVerticalScrollbar() const { return nullptr; }
   virtual cc::Layer* LayerForScrollCorner() const { return nullptr; }
@@ -669,7 +661,6 @@
   unsigned scrollbar_captured_ : 1;
   unsigned mouse_over_scrollbar_ : 1;
   unsigned has_been_disposed_ : 1;
-  unsigned uses_composited_scrolling_ : 1;
 
   scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
 };
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 85f4098..a56450d 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -141,8 +141,8 @@
 class StopColor;
 class Stroke;
 class TextDecorationColor;
+class TextEmphasisColor;
 class WebkitTapHighlightColor;
-class WebkitTextEmphasisColor;
 class WebkitTextFillColor;
 class WebkitTextStrokeColor;
 
@@ -255,8 +255,8 @@
   friend class css_longhand::StopColor;
   friend class css_longhand::Stroke;
   friend class css_longhand::TextDecorationColor;
+  friend class css_longhand::TextEmphasisColor;
   friend class css_longhand::WebkitTapHighlightColor;
-  friend class css_longhand::WebkitTextEmphasisColor;
   friend class css_longhand::WebkitTextFillColor;
   friend class css_longhand::WebkitTextStrokeColor;
   // Access to private Appearance() and HasAppearance().
diff --git a/third_party/blink/renderer/core/style/computed_style_diff_functions.json5 b/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
index e5d8136..398784c 100644
--- a/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
+++ b/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
@@ -79,7 +79,7 @@
                 "-webkit-text-security", "hyphens", "HyphenationLimitBefore",
                 "HyphenationLimitAfter", "-webkit-hyphenate-character",
                 "image-orientation", "-webkit-ruby-position",
-                "TextEmphasisMark", "-webkit-text-emphasis-position",
+                "TextEmphasisMark", "text-emphasis-position",
                 "TextEmphasisCustomMark", "text-justify", "text-orientation",
                 "text-combine-upright", "tab-size", "text-size-adjust",
                 "list-style-image", "line-height-step",
@@ -455,7 +455,7 @@
           },
           {
             method: "TextEmphasisColor()",
-            field_dependencies: ["-webkit-text-emphasis-color"]
+            field_dependencies: ["text-emphasis-color"]
           },
           {
             method: "InternalVisitedTextFillColor()",
diff --git a/third_party/blink/renderer/modules/webtransport/web_transport.cc b/third_party/blink/renderer/modules/webtransport/web_transport.cc
index c5dade0..e4c2939 100644
--- a/third_party/blink/renderer/modules/webtransport/web_transport.cc
+++ b/third_party/blink/renderer/modules/webtransport/web_transport.cc
@@ -1117,14 +1117,15 @@
   bool had_csp_failure = false;
   if (!execution_context->GetContentSecurityPolicyForCurrentWorld()
            ->AllowConnectToSource(url_, url_, RedirectStatus::kNoRedirect)) {
-    auto dom_exception = V8ThrowDOMException::CreateOrEmpty(
-        script_state_->GetIsolate(), DOMExceptionCode::kSecurityError,
-        "Failed to connect to '" + url_.ElidedString() + "'",
+    v8::Local<v8::Value> error = WebTransportError::Create(
+        script_state_->GetIsolate(),
+        /*stream_error_code=*/absl::nullopt,
         "Refused to connect to '" + url_.ElidedString() +
-            "' because it violates the document's Content Security Policy");
+            "' because it violates the document's Content Security Policy",
+        WebTransportError::Source::kSession);
 
-    ready_resolver_->Reject(dom_exception);
-    closed_resolver_->Reject(dom_exception);
+    ready_resolver_->Reject(error);
+    closed_resolver_->Reject(error);
 
     had_csp_failure = true;
   }
diff --git a/third_party/blink/renderer/modules/xr/README.md b/third_party/blink/renderer/modules/xr/README.md
index 7489ca8..ea9d2ac 100644
--- a/third_party/blink/renderer/modules/xr/README.md
+++ b/third_party/blink/renderer/modules/xr/README.md
@@ -1,4 +1,6 @@
 # WebXR Blink Module
+_For a more thorough/high level overview of the entire WebXR stack, please refer to
+[components/webxr](https://source.chromium.org/chromium/chromium/src/+/main:components/webxr/README.md)_
 
 The WebXR API enables Virtual Reality (VR) and Augmented Reality (AR) features on the Web.
 
@@ -15,4 +17,6 @@
  - [Anchors](https://immersive-web.github.io/anchors/)
  - [Lighting Estimation](https://immersive-web.github.io/lighting-estimation/)
 
-The Blink interfaces are supported by the backends implemented in chrome/browser/vr/ and device/vr/
+The Blink interfaces are supported by the backends implemented in
+[chrome/browser/vr/](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/vr) and
+[device/vr/](https://source.chromium.org/chromium/chromium/src/+/main:device/vr)
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 15ae230c..88c18c5 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -653,6 +653,8 @@
     "fonts/opentype/open_type_caps_support.cc",
     "fonts/opentype/open_type_caps_support.h",
     "fonts/opentype/open_type_caps_support_mpl.cc",
+    "fonts/opentype/open_type_cpal_lookup.cc",
+    "fonts/opentype/open_type_cpal_lookup.h",
     "fonts/opentype/open_type_math_stretch_data.h",
     "fonts/opentype/open_type_math_support.cc",
     "fonts/opentype/open_type_math_support.h",
@@ -2023,6 +2025,7 @@
     "fonts/generic_font_family_settings_test.cc",
     "fonts/opentype/font_format_check_test.cc",
     "fonts/opentype/font_settings_test.cc",
+    "fonts/opentype/open_type_cpal_lookup_test.cc",
     "fonts/opentype/open_type_math_support_test.cc",
     "fonts/opentype/open_type_vertical_data_test.cc",
     "fonts/opentype/variable_axes_names_test.cc",
@@ -2386,8 +2389,7 @@
 
     # Required by some font tests.
     "//third_party/blink/web_tests/external/wpt/fonts/",
-
-    # Required by some font tests.
+    "//third_party/blink/web_tests/external/wpt/css/css-fonts/resources",
     "//third_party/blink/web_tests/third_party/",
   ]
 
diff --git a/third_party/blink/renderer/platform/exported/video_capture/web_video_capture_impl_manager_test.cc b/third_party/blink/renderer/platform/exported/video_capture/web_video_capture_impl_manager_test.cc
index a236bd3..9dbc862 100644
--- a/third_party/blink/renderer/platform/exported/video_capture/web_video_capture_impl_manager_test.cc
+++ b/third_party/blink/renderer/platform/exported/video_capture/web_video_capture_impl_manager_test.cc
@@ -71,7 +71,8 @@
     // For every Start(), expect a corresponding Stop() call.
     EXPECT_CALL(*this, Stop(_));
     // Simulate device started.
-    OnStateChanged(media::mojom::VideoCaptureState::STARTED);
+    OnStateChanged(media::mojom::blink::VideoCaptureResult::NewState(
+        media::mojom::VideoCaptureState::STARTED));
   }
 
   MOCK_METHOD1(Stop, void(const base::UnguessableToken&));
diff --git a/third_party/blink/renderer/platform/fonts/opentype/open_type_cpal_lookup.cc b/third_party/blink/renderer/platform/fonts/opentype/open_type_cpal_lookup.cc
new file mode 100644
index 0000000..66ba9a11
--- /dev/null
+++ b/third_party/blink/renderer/platform/fonts/opentype/open_type_cpal_lookup.cc
@@ -0,0 +1,49 @@
+// 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 "third_party/blink/renderer/platform/fonts/opentype/open_type_cpal_lookup.h"
+
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face_from_typeface.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/harfbuzz-ng/utils/hb_scoped.h"
+#include "third_party/skia/include/core/SkStream.h"
+
+// clang-format off
+#include <hb.h>
+#include <hb-ot.h>
+// clang-format on
+
+namespace {
+SkFontTableTag kCpalTag = SkSetFourByteTag('C', 'P', 'A', 'L');
+
+}  // namespace
+
+namespace blink {
+
+/* static */
+absl::optional<uint16_t> OpenTypeCpalLookup::FirstThemedPalette(
+    sk_sp<SkTypeface> typeface,
+    PaletteUse palette_use) {
+  if (!typeface || !typeface->getTableSize(kCpalTag))
+    return absl::nullopt;
+
+  HbScoped<hb_face_t> face(HbFaceFromSkTypeface(typeface));
+
+  if (!face || !hb_ot_color_has_palettes(face.get()))
+    return absl::nullopt;
+
+  int num_palettes = hb_ot_color_palette_get_count(face.get());
+
+  const hb_ot_color_palette_flags_t desired_flag =
+      palette_use == kUsableWithLightBackground
+          ? HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND
+          : HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND;
+  for (int i = 0; i < num_palettes; ++i) {
+    if (hb_ot_color_palette_get_flags(face.get(), i) == desired_flag)
+      return i;
+  }
+  return absl::nullopt;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/opentype/open_type_cpal_lookup.h b/third_party/blink/renderer/platform/fonts/opentype/open_type_cpal_lookup.h
new file mode 100644
index 0000000..ba721ac
--- /dev/null
+++ b/third_party/blink/renderer/platform/fonts/opentype/open_type_cpal_lookup.h
@@ -0,0 +1,33 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_CPAL_LOOKUP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_CPAL_LOOKUP_H_
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+
+namespace blink {
+
+/* Tools for inspecting the font palette of a COLR/CPAL font to find dark/light
+ * mode preferred palettes and resolve string-based palette overrides as
+ * specified in font-palette or @font-palette-values CSS. */
+class PLATFORM_EXPORT OpenTypeCpalLookup {
+ public:
+  enum PaletteUse { kUsableWithLightBackground, kUsableWithDarkBackground };
+
+  /* Return the index of the first palette useful for the specified
+   * palette use, dark or light. Important: The SkTypeface passed in
+   * should allow efficient access to its internal data buffer using
+   * SkTypeface::openStream, which is not the case for CoreText-backed
+   * SkTypeface objects.
+   */
+  static absl::optional<uint16_t> FirstThemedPalette(sk_sp<SkTypeface> typeface,
+                                                     PaletteUse palette_use);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_CPAL_LOOKUP_H_
diff --git a/third_party/blink/renderer/platform/fonts/opentype/open_type_cpal_lookup_test.cc b/third_party/blink/renderer/platform/fonts/opentype/open_type_cpal_lookup_test.cc
new file mode 100644
index 0000000..add4766
--- /dev/null
+++ b/third_party/blink/renderer/platform/fonts/opentype/open_type_cpal_lookup_test.cc
@@ -0,0 +1,82 @@
+// 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 "third_party/blink/renderer/platform/fonts/opentype/open_type_cpal_lookup.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/file_path_conversion.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+
+#include <utility>
+#include <vector>
+
+namespace {
+String pathToColrPalettesTestFont() {
+  base::FilePath wpt_palette_font_path(
+      blink::StringToFilePath(blink::test::BlinkWebTestsDir()));
+  wpt_palette_font_path = wpt_palette_font_path.Append(FILE_PATH_LITERAL(
+      "external/wpt/css/css-fonts/resources/COLR-palettes-test-font.ttf"));
+  return blink::FilePathToString(wpt_palette_font_path);
+}
+String pathToNonColrTestFont() {
+  return blink::test::BlinkWebTestsFontsTestDataPath("Ahem.ttf");
+}
+}  // namespace
+
+namespace blink {
+
+class OpenTypeCpalLookupTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    FontDescription::VariantLigatures ligatures;
+
+    Font colr_palette_font = blink::test::CreateTestFont(
+        "Ahem", pathToColrPalettesTestFont(), 16, &ligatures);
+    colr_palette_typeface_ =
+        sk_ref_sp(colr_palette_font.PrimaryFont()->PlatformData().Typeface());
+
+    Font non_colr_font = blink::test::CreateTestFont(
+        "Ahem", pathToNonColrTestFont(), 16, &ligatures);
+    non_colr_ahem_typeface_ =
+        sk_ref_sp(non_colr_font.PrimaryFont()->PlatformData().Typeface());
+  }
+
+  sk_sp<SkTypeface> colr_palette_typeface_;
+  sk_sp<SkTypeface> non_colr_ahem_typeface_;
+};
+
+TEST_F(OpenTypeCpalLookupTest, NoResultForNonColr) {
+  for (auto& palette_use : {OpenTypeCpalLookup::kUsableWithLightBackground,
+                            OpenTypeCpalLookup::kUsableWithDarkBackground}) {
+    absl::optional<uint16_t> palette_result =
+        OpenTypeCpalLookup::FirstThemedPalette(non_colr_ahem_typeface_,
+                                               palette_use);
+    EXPECT_FALSE(palette_result.has_value());
+  }
+}
+
+TEST_F(OpenTypeCpalLookupTest, DarkLightPalettes) {
+  // COLR-palettes-test-font.tff dumped with FontTools has
+  //     <palette index="2" type="1">[...]
+  //     <palette index="3" type="2">
+  // meaning palette index 2 is the first palette usable for light backgrounds,
+  // and palette index 3 is the first palette usable for dark background.
+  std::vector<std::pair<OpenTypeCpalLookup::PaletteUse, uint16_t>> expectations{
+      {OpenTypeCpalLookup::kUsableWithLightBackground, 2},
+      {OpenTypeCpalLookup::kUsableWithDarkBackground, 3}};
+  for (auto& expectation : expectations) {
+    absl::optional<uint16_t> palette_result =
+        OpenTypeCpalLookup::FirstThemedPalette(colr_palette_typeface_,
+                                               expectation.first);
+    EXPECT_TRUE(palette_result.has_value());
+    EXPECT_EQ(*palette_result, expectation.second);
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/paint/README.md b/third_party/blink/renderer/platform/graphics/paint/README.md
index ea389f5c..c581876 100644
--- a/third_party/blink/renderer/platform/graphics/paint/README.md
+++ b/third_party/blink/renderer/platform/graphics/paint/README.md
@@ -1,6 +1,6 @@
 <!---
   The live version of this document can be viewed at:
-  https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/platform/graphics/paint/README.md
+  https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/platform/graphics/paint/README.md
 -->
 
 # Platform paint code
@@ -18,10 +18,10 @@
 
 ## Paint artifact
 
-The CompositeAfterPaint [paint artifact](paint_artifact.h) consists of a list of
-display items in paint order (ideally mostly or all drawings), partitioned into
-*paint chunks* which define certain *paint properties* which affect how the
-content should be drawn or composited.
+[Paint artifact](paint_artifact.h) consists of a list of display items in paint
+order (ideally mostly or all drawings), partitioned into *paint chunks* which
+define certain *paint properties* which affect how the content should be drawn
+or composited.
 
 ## Paint properties
 
@@ -156,13 +156,6 @@
 Holds a `PaintRecord` which contains the paint operations required to draw some
 atom of content.
 
-#### [GraphicsLayerDisplayItem](graphics_layer_display_item.h)
-
-Placeholder for `GraphicsLayers` allocated by the pre-CompositeAfterPaint
-compositing logic. Each one of these may or may not ultimately produce a
-`cc::PictureLayer`, depending on the layer squashing mechanism. This class
-becomes obsolete with CompositeAfterPaint.
-
 #### [ForeignLayerDisplayItem](foreign_layer_display_item.h)
 
 Draws an atom of content, but using a `cc::Layer` produced by some agent outside
@@ -170,12 +163,15 @@
 to a `cc::Layer`, they are always the only display item in their paint chunk,
 and are ineligible for squashing with other layers.
 
-#### [ScrollHitTestDisplayItem](scroll_hit_test_display_item.h)
+#### [ScrollbarDisplayItem](scrollbar_display_item.h)
 
-Placeholder for creating a cc::Layer for scrolling in paint order. Hit testing
-in the compositor requires both property trees (scroll nodes) and a scrollable
-`cc::layer` in paint order. This should be associated with the scroll
-translation paint property node as well as any overflow clip nodes.
+Contains a [cc::Scrollbar](../../../../../../cc/input/scrollbar.h) and other
+information that are needed to paint a scrollbar into a paint record or to
+create a cc scrollbar layer. During the PaintArtifactCompositor update, we
+decide whether to composite the scrollbar and, if not composited, actually paint
+the scrollbar as a paint record, otherwise create a cc scrollbar layer of type
+cc::SolidColorScrollbarLayer, cc::PaintedScrollbarLayer or
+cc::PaintedOverlayScrollbarLayer depending on the type of the scrollbar.
 
 ## Paint controller
 
@@ -211,16 +207,34 @@
 responsible for consuming the `PaintArtifact` produced by the `PaintController`,
 and converting it into a form suitable for the compositor to consume.
 
-At present, `PaintArtifactCompositor` creates a cc layer tree, with one layer
-for each paint chunk. In the future, it is expected that we will use heuristics
-to combine paint chunks into a smaller number of layers.
+`PaintArtifactCompositor` creates a list of cc::Layers from the paint chunks.
+The entry point to layerization is `PaintArtifactCompositor::LayerizeGroup`.
+This algorithm has to make tradeoffs between GPU memory and reducing the costs
+when things change. The algorithm starts by creating `PendingLayer`s for each
+paint chunk, and then tries to combine `PendingLayer`s whenever possible.
+Reasons that prevent combining `PendingLayer`s include: having a direct
+compositing reason on property nodes, having interleaving composited content
+(known as "overlap testing"), and avoiding wasting large areas (known as
+"sparsity", see `kMergeSparsityAreaTolerance`). Once the list of `PendingLayer`s
+is known, `cc::Layer`s area created for each (see: PaintChunksToCcLayer).
 
-The owner of the `PaintArtifactCompositor` (e.g. `WebView`) can then attach its
-root layer to the overall layer hierarchy to be displayed to the user.
+### Direct paint property updates
 
-In the future we would like to explore moving to a single shared property tree
-representation across both cc and
-Blink. See [Web Page Geometries](https://goo.gl/MwVIto) for more.
+PaintArtifactCompositor::Update is expensive and can be avoided for simple
+paint property updates where layerization is known to not change. For example,
+the `kWillChangeTransform` direct compositing reason will force the layerization
+algorithm to create cc::Layers so the will-change content can move without
+needing to change cc::Layers. `DirectlyUpdateTransform` can then be used to
+update the transform property node without doing a full
+PaintArtifactCompositor::Update.
+
+### Repaint-only updates
+
+PaintArtifactCompositor::Update is expensive and can be avoided for simple
+paint changes where layerization is known to not change. For example, if the
+color of a display item changes in a way that does not affect layerization, we
+can just update the display items of the existing cc::Layers. This is
+implemented in `UpdateRepaintedLayers`.
 
 ## Raster invalidation
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.cc b/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.cc
index e5ad12b..c70f2039 100644
--- a/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.cc
@@ -9,6 +9,20 @@
 
 namespace blink {
 
+PaintPropertyChangeType ClipPaintPropertyNode::State::ComputeChange(
+    const State& other) const {
+  if (local_transform_space != other.local_transform_space ||
+      paint_clip_rect != other.paint_clip_rect ||
+      clip_path != other.clip_path) {
+    return PaintPropertyChangeType::kChangedOnlyValues;
+  }
+  if (layout_clip_rect_excluding_overlay_scrollbars !=
+      other.layout_clip_rect_excluding_overlay_scrollbars) {
+    return PaintPropertyChangeType::kChangedOnlyNonRerasterValues;
+  }
+  return PaintPropertyChangeType::kUnchanged;
+}
+
 const ClipPaintPropertyNode& ClipPaintPropertyNode::Root() {
   DEFINE_STATIC_REF(
       ClipPaintPropertyNode, root,
diff --git a/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h
index 576e3f2..568c8353 100644
--- a/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h
@@ -74,7 +74,7 @@
  public:
   // To make it less verbose and more readable to construct and update a node,
   // a struct with default values is used to represent the state.
-  struct State {
+  struct PLATFORM_EXPORT State {
     State(scoped_refptr<const TransformPaintPropertyNodeOrAlias>
               local_transform_space,
           const gfx::RectF& layout_clip_rect,
@@ -96,18 +96,7 @@
       paint_clip_rect = paint_clip_rect_arg;
     }
 
-    PaintPropertyChangeType ComputeChange(const State& other) const {
-      if (local_transform_space != other.local_transform_space ||
-          paint_clip_rect != other.paint_clip_rect ||
-          clip_path != other.clip_path) {
-        return PaintPropertyChangeType::kChangedOnlyValues;
-      }
-      if (layout_clip_rect_excluding_overlay_scrollbars !=
-          other.layout_clip_rect_excluding_overlay_scrollbars) {
-        return PaintPropertyChangeType::kChangedOnlyNonRerasterValues;
-      }
-      return PaintPropertyChangeType::kUnchanged;
-    }
+    PaintPropertyChangeType ComputeChange(const State& other) const;
 
     friend class ClipPaintPropertyNode;
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc
index fa8026f..7068756 100644
--- a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc
@@ -8,6 +8,75 @@
 
 namespace blink {
 
+namespace {
+
+PaintPropertyChangeType ComputeBackdropFilterChange(
+    const EffectPaintPropertyNode::BackdropFilterInfo* a,
+    const EffectPaintPropertyNode::BackdropFilterInfo* b,
+    bool is_running_backdrop_filter_animation_on_compositor) {
+  if (!a && !b)
+    return PaintPropertyChangeType::kUnchanged;
+  if (!a || !b || a->bounds != b->bounds ||
+      a->mask_element_id != b->mask_element_id)
+    return PaintPropertyChangeType::kChangedOnlyValues;
+  if (a->operations != b->operations) {
+    return is_running_backdrop_filter_animation_on_compositor
+               ? PaintPropertyChangeType::kChangedOnlyCompositedValues
+               : PaintPropertyChangeType::kChangedOnlyValues;
+  }
+  return PaintPropertyChangeType::kUnchanged;
+}
+
+} // anonymous namespace
+
+PaintPropertyChangeType EffectPaintPropertyNode::State::ComputeChange(
+    const State& other,
+    const AnimationState& animation_state) const {
+  if (local_transform_space != other.local_transform_space ||
+      output_clip != other.output_clip || blend_mode != other.blend_mode ||
+      document_transition_shared_element_id !=
+          other.document_transition_shared_element_id ||
+      shared_element_resource_id != other.shared_element_resource_id) {
+    return PaintPropertyChangeType::kChangedOnlyValues;
+  }
+  bool opacity_changed = opacity != other.opacity;
+  bool opacity_change_is_simple =
+      opacity_changed && opacity != 1.f && other.opacity != 1.f;
+  if (opacity_changed && !opacity_change_is_simple &&
+      !animation_state.is_running_opacity_animation_on_compositor) {
+    return PaintPropertyChangeType::kChangedOnlyValues;
+  }
+  bool filter_changed = filter != other.filter;
+  if (filter_changed &&
+      !animation_state.is_running_filter_animation_on_compositor) {
+    return PaintPropertyChangeType::kChangedOnlyValues;
+  }
+  auto backdrop_filter_changed = ComputeBackdropFilterChange(
+      backdrop_filter_info.get(), other.backdrop_filter_info.get(),
+      animation_state.is_running_backdrop_filter_animation_on_compositor);
+  if (backdrop_filter_changed == PaintPropertyChangeType::kChangedOnlyValues) {
+    return PaintPropertyChangeType::kChangedOnlyValues;
+  }
+  bool non_reraster_values_changed =
+      direct_compositing_reasons != other.direct_compositing_reasons ||
+      compositor_element_id != other.compositor_element_id;
+  bool simple_values_changed =
+      opacity_change_is_simple &&
+      !animation_state.is_running_opacity_animation_on_compositor;
+  if (non_reraster_values_changed && simple_values_changed)
+    return PaintPropertyChangeType::kChangedOnlyValues;
+  if (non_reraster_values_changed)
+    return PaintPropertyChangeType::kChangedOnlyNonRerasterValues;
+  if (simple_values_changed)
+    return PaintPropertyChangeType::kChangedOnlySimpleValues;
+
+  if (opacity_changed || filter_changed ||
+      backdrop_filter_changed != PaintPropertyChangeType::kUnchanged) {
+    return PaintPropertyChangeType::kChangedOnlyCompositedValues;
+  }
+  return PaintPropertyChangeType::kUnchanged;
+}
+
 const EffectPaintPropertyNode& EffectPaintPropertyNode::Root() {
   DEFINE_STATIC_REF(EffectPaintPropertyNode, root,
                     base::AdoptRef(new EffectPaintPropertyNode(
diff --git a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
index 527cf9c5..448b0fd 100644
--- a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
@@ -86,28 +86,11 @@
     // The compositor element id for any masks that are applied to elements that
     // also have backdrop-filters applied.
     CompositorElementId mask_element_id;
-
-    static PaintPropertyChangeType ComputeChange(
-        const BackdropFilterInfo* a,
-        const BackdropFilterInfo* b,
-        bool is_running_backdrop_filter_animation_on_compositor) {
-      if (!a && !b)
-        return PaintPropertyChangeType::kUnchanged;
-      if (!a || !b || a->bounds != b->bounds ||
-          a->mask_element_id != b->mask_element_id)
-        return PaintPropertyChangeType::kChangedOnlyValues;
-      if (a->operations != b->operations) {
-        return is_running_backdrop_filter_animation_on_compositor
-                   ? PaintPropertyChangeType::kChangedOnlyCompositedValues
-                   : PaintPropertyChangeType::kChangedOnlyValues;
-      }
-      return PaintPropertyChangeType::kUnchanged;
-    }
   };
 
   // To make it less verbose and more readable to construct and update a node,
   // a struct with default values is used to represent the state.
-  struct State {
+  struct PLATFORM_EXPORT State {
     // The local transform space serves two purposes:
     // 1. Assign a depth mapping for 3D depth sorting against other paint chunks
     //    and effects under the same parent.
@@ -146,52 +129,7 @@
 
     PaintPropertyChangeType ComputeChange(
         const State& other,
-        const AnimationState& animation_state) {
-      if (local_transform_space != other.local_transform_space ||
-          output_clip != other.output_clip || blend_mode != other.blend_mode ||
-          document_transition_shared_element_id !=
-              other.document_transition_shared_element_id ||
-          shared_element_resource_id != other.shared_element_resource_id) {
-        return PaintPropertyChangeType::kChangedOnlyValues;
-      }
-      bool opacity_changed = opacity != other.opacity;
-      bool opacity_change_is_simple =
-          opacity_changed && opacity != 1.f && other.opacity != 1.f;
-      if (opacity_changed && !opacity_change_is_simple &&
-          !animation_state.is_running_opacity_animation_on_compositor) {
-        return PaintPropertyChangeType::kChangedOnlyValues;
-      }
-      bool filter_changed = filter != other.filter;
-      if (filter_changed &&
-          !animation_state.is_running_filter_animation_on_compositor) {
-        return PaintPropertyChangeType::kChangedOnlyValues;
-      }
-      auto backdrop_filter_changed = BackdropFilterInfo::ComputeChange(
-          backdrop_filter_info.get(), other.backdrop_filter_info.get(),
-          animation_state.is_running_backdrop_filter_animation_on_compositor);
-      if (backdrop_filter_changed ==
-          PaintPropertyChangeType::kChangedOnlyValues) {
-        return PaintPropertyChangeType::kChangedOnlyValues;
-      }
-      bool non_reraster_values_changed =
-          direct_compositing_reasons != other.direct_compositing_reasons ||
-          compositor_element_id != other.compositor_element_id;
-      bool simple_values_changed =
-          opacity_change_is_simple &&
-          !animation_state.is_running_opacity_animation_on_compositor;
-      if (non_reraster_values_changed && simple_values_changed)
-        return PaintPropertyChangeType::kChangedOnlyValues;
-      if (non_reraster_values_changed)
-        return PaintPropertyChangeType::kChangedOnlyNonRerasterValues;
-      if (simple_values_changed)
-        return PaintPropertyChangeType::kChangedOnlySimpleValues;
-
-      if (opacity_changed || filter_changed ||
-          backdrop_filter_changed != PaintPropertyChangeType::kUnchanged) {
-        return PaintPropertyChangeType::kChangedOnlyCompositedValues;
-      }
-      return PaintPropertyChangeType::kUnchanged;
-    }
+        const AnimationState& animation_state) const;
   };
 
   // This node is really a sentinel, and does not represent a real effect.
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc b/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc
index aae86bc..c8a84d5 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc
@@ -180,8 +180,10 @@
 TEST_F(PaintPropertyNodeTest, TransformChangeAncestor) {
   ResetAllChanged();
   ExpectUnchangedState();
-  transform.ancestor->Update(
-      *transform.root, TransformPaintPropertyNode::State{gfx::Vector2dF(1, 2)});
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
+            transform.ancestor->Update(
+                *transform.root,
+                TransformPaintPropertyNode::State{gfx::Vector2dF(1, 2)}));
 
   // Test descendant->Changed(ancestor).
   EXPECT_CHANGE_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
@@ -211,10 +213,12 @@
 TEST_F(PaintPropertyNodeTest, ClipChangeAncestor) {
   ResetAllChanged();
   ExpectUnchangedState();
-  clip.ancestor->Update(
-      *clip.root, ClipPaintPropertyNode::State(transform.ancestor.get(),
-                                               gfx::RectF(1, 2, 3, 4),
-                                               FloatRoundedRect(1, 2, 3, 4)));
+  EXPECT_EQ(
+      PaintPropertyChangeType::kChangedOnlyValues,
+      clip.ancestor->Update(
+          *clip.root, ClipPaintPropertyNode::State(
+                          transform.ancestor.get(), gfx::RectF(1, 2, 3, 4),
+                          FloatRoundedRect(1, 2, 3, 4))));
 
   // Test descendant->Changed(ancestor).
   EXPECT_TRUE(clip.ancestor->Changed(
@@ -252,7 +256,8 @@
   // The initial test starts with opacity 0.5, and we're changing it to 0.9
   // here.
   state.opacity = 0.9;
-  effect.ancestor->Update(*effect.root, std::move(state));
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
+            effect.ancestor->Update(*effect.root, std::move(state)));
 
   // Test descendant->Changed(ancestor).
   EXPECT_CHANGE_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
@@ -278,6 +283,31 @@
   ExpectUnchangedState();
 }
 
+TEST_F(PaintPropertyNodeTest, ChangeOpacityDuringCompositedAnimation) {
+  ResetAllChanged();
+  ExpectUnchangedState();
+
+  EffectPaintPropertyNode::State state{transform.child1.get(),
+                                       clip.child1.get()};
+  state.compositor_element_id = effect.child1->GetCompositorElementId();
+  // The initial test starts with opacity 0.5, and we're changing it to 0.9
+  // here.
+  state.opacity = 0.9;
+
+  EffectPaintPropertyNode::AnimationState animation_state;
+  animation_state.is_running_opacity_animation_on_compositor = true;
+
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlyCompositedValues,
+            effect.child1->Update(*effect.ancestor, std::move(state),
+                                  animation_state));
+
+  EXPECT_CHANGE_EQ(PaintPropertyChangeType::kChangedOnlyCompositedValues,
+                   effect.child1, STATE(root), nullptr);
+
+  ResetAllChanged();
+  ExpectUnchangedState();
+}
+
 TEST_F(PaintPropertyNodeTest, EffectOpacityChangesToOne) {
   ResetAllChanged();
   ExpectUnchangedState();
@@ -286,7 +316,8 @@
   // The initial test starts with opacity 0.5, and we're changing it to 1
   // here.
   state.opacity = 1.f;
-  effect.ancestor->Update(*effect.root, std::move(state));
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlyValues,
+            effect.ancestor->Update(*effect.root, std::move(state)));
 
   // Test descendant->Changed(ancestor).
   EXPECT_CHANGE_EQ(PaintPropertyChangeType::kChangedOnlyValues, effect.ancestor,
@@ -318,7 +349,8 @@
   ExpectUnchangedState();
   TransformPaintPropertyNode::State state;
   state.direct_compositing_reasons = CompositingReason::kWillChangeTransform;
-  transform.child1->Update(*transform.ancestor, std::move(state));
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlyNonRerasterValues,
+            transform.child1->Update(*transform.ancestor, std::move(state)));
 
   EXPECT_CHANGE_EQ(PaintPropertyChangeType::kChangedOnlyNonRerasterValues,
                    transform.child1, *transform.root);
@@ -332,10 +364,11 @@
   ExpectUnchangedState();
   TransformPaintPropertyNode::AnimationState animation_state;
   animation_state.is_running_animation_on_compositor = true;
-  transform.child1->Update(
-      *transform.ancestor,
-      TransformPaintPropertyNode::State{TransformationMatrix().Scale(2)},
-      animation_state);
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlyCompositedValues,
+            transform.child1->Update(*transform.ancestor,
+                                     TransformPaintPropertyNode::State{
+                                         TransformationMatrix().Scale(2)},
+                                     animation_state));
 
   EXPECT_FALSE(transform.child1->Changed(
       PaintPropertyChangeType::kChangedOnlyValues, *transform.root));
@@ -353,10 +386,12 @@
   ExpectUnchangedState();
   TransformPaintPropertyNode::AnimationState animation_state;
   animation_state.is_running_animation_on_compositor = true;
-  transform.child1->Update(*transform.ancestor,
-                           TransformPaintPropertyNode::State{
-                               {TransformationMatrix(), FloatPoint3D(1, 2, 3)}},
-                           animation_state);
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
+            transform.child1->Update(
+                *transform.ancestor,
+                TransformPaintPropertyNode::State{
+                    {TransformationMatrix(), FloatPoint3D(1, 2, 3)}},
+                animation_state));
 
   EXPECT_TRUE(transform.child1->Changed(
       PaintPropertyChangeType::kChangedOnlySimpleValues, *transform.root));
@@ -368,9 +403,10 @@
 TEST_F(PaintPropertyNodeTest, TransformChangeOneChild) {
   ResetAllChanged();
   ExpectUnchangedState();
-  transform.child1->Update(
-      *transform.ancestor,
-      TransformPaintPropertyNode::State{gfx::Vector2dF(1, 2)});
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
+            transform.child1->Update(
+                *transform.ancestor,
+                TransformPaintPropertyNode::State{gfx::Vector2dF(1, 2)}));
 
   // Test descendant->Changed(ancestor).
   EXPECT_CHANGE_EQ(PaintPropertyChangeType::kUnchanged, transform.ancestor,
@@ -416,10 +452,11 @@
 TEST_F(PaintPropertyNodeTest, ClipChangeOneChild) {
   ResetAllChanged();
   ExpectUnchangedState();
-  clip.child1->Update(
-      *clip.root, ClipPaintPropertyNode::State(transform.ancestor.get(),
-                                               gfx::RectF(1, 2, 3, 4),
-                                               FloatRoundedRect(1, 2, 3, 4)));
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlyValues,
+            clip.child1->Update(*clip.root, ClipPaintPropertyNode::State(
+                                                transform.ancestor.get(),
+                                                gfx::RectF(1, 2, 3, 4),
+                                                FloatRoundedRect(1, 2, 3, 4))));
 
   // Test descendant->Changed(ancestor).
   EXPECT_FALSE(clip.ancestor->Changed(
@@ -471,7 +508,8 @@
   EffectPaintPropertyNode::State state{transform.ancestor.get(),
                                        clip.ancestor.get()};
   state.opacity = 0.9;
-  effect.child1->Update(*effect.root, std::move(state));
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlyValues,
+            effect.child1->Update(*effect.root, std::move(state)));
 
   // Test descendant->Changed(PaintPropertyChangeType::kChangedOnlyValues,
   // ancestor).
@@ -523,8 +561,10 @@
 TEST_F(PaintPropertyNodeTest, TransformReparent) {
   ResetAllChanged();
   ExpectUnchangedState();
-  transform.child1->Update(*transform.child2, TransformPaintPropertyNode::State{
-                                                  gfx::Vector2dF(1, 2)});
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlyValues,
+            transform.child1->Update(
+                *transform.child2,
+                TransformPaintPropertyNode::State{gfx::Vector2dF(1, 2)}));
   EXPECT_FALSE(transform.ancestor->Changed(
       PaintPropertyChangeType::kChangedOnlyValues, *transform.root));
   EXPECT_TRUE(transform.child1->Changed(
@@ -547,9 +587,10 @@
 TEST_F(PaintPropertyNodeTest, ClipLocalTransformSpaceChange) {
   ResetAllChanged();
   ExpectUnchangedState();
-  transform.child1->Update(
-      *transform.ancestor,
-      TransformPaintPropertyNode::State{gfx::Vector2dF(1, 2)});
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
+            transform.child1->Update(
+                *transform.ancestor,
+                TransformPaintPropertyNode::State{gfx::Vector2dF(1, 2)}));
 
   // We check that we detect the change from the transform. However, right now
   // we report simple value change which may be a bit confusing. See
@@ -594,8 +635,10 @@
 
   ResetAllChanged();
   ExpectUnchangedState();
-  transform.ancestor->Update(
-      *transform.root, TransformPaintPropertyNode::State{gfx::Vector2dF(1, 2)});
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
+            transform.ancestor->Update(
+                *transform.root,
+                TransformPaintPropertyNode::State{gfx::Vector2dF(1, 2)}));
 
   // We check that we detect the change from the transform. However, right now
   // we report simple value change which may be a bit confusing. See
@@ -639,42 +682,51 @@
   EXPECT_EQ(PaintPropertyChangeType::kUnchanged, NodeChanged(*t));
 
   // Translation doesn't affect 2d axis alignment.
-  t->Update(t0(), TransformPaintPropertyNode::State{gfx::Vector2dF(30, 40)});
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
+            t->Update(t0(), TransformPaintPropertyNode::State{
+                                gfx::Vector2dF(30, 40)}));
   EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues, NodeChanged(*t));
   t->ClearChangedToRoot();
   EXPECT_EQ(PaintPropertyChangeType::kUnchanged, NodeChanged(*t));
 
   // Scale doesn't affect 2d axis alignment.
-  t->Update(t0(), TransformPaintPropertyNode::State{
-                      TransformationMatrix().Scale3d(2, 3, 4)});
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
+            t->Update(t0(), TransformPaintPropertyNode::State{
+                                TransformationMatrix().Scale3d(2, 3, 4)}));
   EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues, NodeChanged(*t));
   t->ClearChangedToRoot();
   EXPECT_EQ(PaintPropertyChangeType::kUnchanged, NodeChanged(*t));
 
   // Rotation affects 2d axis alignment.
-  t->Update(t0(), TransformPaintPropertyNode::State{
-                      TransformationMatrix(t->Matrix()).Rotate(45)});
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlyValues,
+            t->Update(t0(), TransformPaintPropertyNode::State{
+                                TransformationMatrix(t->Matrix()).Rotate(45)}));
   EXPECT_EQ(PaintPropertyChangeType::kChangedOnlyValues, NodeChanged(*t));
   t->ClearChangedToRoot();
   EXPECT_EQ(PaintPropertyChangeType::kUnchanged, NodeChanged(*t));
 
   // Changing scale but keeping original rotation doesn't change 2d axis
   // alignment and is treated as simple.
-  t->Update(t0(), TransformPaintPropertyNode::State{
-                      TransformationMatrix(t->Matrix()).Scale3d(3, 4, 5)});
+  EXPECT_EQ(
+      PaintPropertyChangeType::kChangedOnlySimpleValues,
+      t->Update(t0(), TransformPaintPropertyNode::State{
+                          TransformationMatrix(t->Matrix()).Scale3d(3, 4, 5)}));
   EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues, NodeChanged(*t));
   t->ClearChangedToRoot();
   EXPECT_EQ(PaintPropertyChangeType::kUnchanged, NodeChanged(*t));
 
   // Change rotation rotation again changes 2d axis alignment.
-  t->Update(t0(), TransformPaintPropertyNode::State{
-                      TransformationMatrix(t->Matrix()).Rotate(10)});
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlyValues,
+            t->Update(t0(), TransformPaintPropertyNode::State{
+                                TransformationMatrix(t->Matrix()).Rotate(10)}));
   EXPECT_EQ(PaintPropertyChangeType::kChangedOnlyValues, NodeChanged(*t));
   t->ClearChangedToRoot();
   EXPECT_EQ(PaintPropertyChangeType::kUnchanged, NodeChanged(*t));
 
   // Reset the transform back to simple translation changes 2d axis alignment.
-  t->Update(t0(), TransformPaintPropertyNode::State{gfx::Vector2dF(1, 2)});
+  EXPECT_EQ(
+      PaintPropertyChangeType::kChangedOnlyValues,
+      t->Update(t0(), TransformPaintPropertyNode::State{gfx::Vector2dF(1, 2)}));
   EXPECT_EQ(PaintPropertyChangeType::kChangedOnlyValues, NodeChanged(*t));
   t->ClearChangedToRoot();
   EXPECT_EQ(PaintPropertyChangeType::kUnchanged, NodeChanged(*t));
diff --git a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc
index 801c721..479414e 100644
--- a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc
@@ -23,6 +23,25 @@
 
 }  // namespace
 
+PaintPropertyChangeType ScrollPaintPropertyNode::State::ComputeChange(
+    const State& other) const {
+  if (container_rect != other.container_rect ||
+      contents_size != other.contents_size ||
+      user_scrollable_horizontal != other.user_scrollable_horizontal ||
+      user_scrollable_vertical != other.user_scrollable_vertical ||
+      prevent_viewport_scrolling_from_inner !=
+          other.prevent_viewport_scrolling_from_inner ||
+      max_scroll_offset_affected_by_page_scale !=
+          other.max_scroll_offset_affected_by_page_scale ||
+      main_thread_scrolling_reasons != other.main_thread_scrolling_reasons ||
+      compositor_element_id != other.compositor_element_id ||
+      overscroll_behavior != other.overscroll_behavior ||
+      snap_container_data != other.snap_container_data) {
+    return PaintPropertyChangeType::kChangedOnlyValues;
+  }
+  return PaintPropertyChangeType::kUnchanged;
+}
+
 const ScrollPaintPropertyNode& ScrollPaintPropertyNode::Root() {
   DEFINE_STATIC_REF(
       ScrollPaintPropertyNode, root,
diff --git a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
index 5fc58f2..e054bd3 100644
--- a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
@@ -41,7 +41,7 @@
  public:
   // To make it less verbose and more readable to construct and update a node,
   // a struct with default values is used to represent the state.
-  struct State {
+  struct PLATFORM_EXPORT State {
     gfx::Rect container_rect;
     gfx::Size contents_size;
     bool user_scrollable_horizontal = false;
@@ -65,24 +65,7 @@
         cc::OverscrollBehavior(cc::OverscrollBehavior::Type::kAuto);
     absl::optional<cc::SnapContainerData> snap_container_data;
 
-    PaintPropertyChangeType ComputeChange(const State& other) const {
-      if (container_rect != other.container_rect ||
-          contents_size != other.contents_size ||
-          user_scrollable_horizontal != other.user_scrollable_horizontal ||
-          user_scrollable_vertical != other.user_scrollable_vertical ||
-          prevent_viewport_scrolling_from_inner !=
-              other.prevent_viewport_scrolling_from_inner ||
-          max_scroll_offset_affected_by_page_scale !=
-              other.max_scroll_offset_affected_by_page_scale ||
-          main_thread_scrolling_reasons !=
-              other.main_thread_scrolling_reasons ||
-          compositor_element_id != other.compositor_element_id ||
-          overscroll_behavior != other.overscroll_behavior ||
-          snap_container_data != other.snap_container_data) {
-        return PaintPropertyChangeType::kChangedOnlyValues;
-      }
-      return PaintPropertyChangeType::kUnchanged;
-    }
+    PaintPropertyChangeType ComputeChange(const State& other) const;
   };
 
   // This node is really a sentinel, and does not represent a real scroll.
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
index a1f1138..d0f6578 100644
--- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
@@ -6,6 +6,86 @@
 
 namespace blink {
 
+PaintPropertyChangeType TransformPaintPropertyNode::State::ComputeChange(
+    const State& other,
+    const AnimationState& animation_state) const {
+  // Whether or not a node is considered a frame root should be invariant.
+  DCHECK_EQ(flags.is_frame_paint_offset_translation,
+            other.flags.is_frame_paint_offset_translation);
+
+  if (flags.flattens_inherited_transform !=
+          other.flags.flattens_inherited_transform ||
+      flags.in_subtree_of_page_scale != other.flags.in_subtree_of_page_scale ||
+      flags.animation_is_axis_aligned !=
+          other.flags.animation_is_axis_aligned ||
+      flags.delegates_to_parent_for_backface !=
+          other.flags.delegates_to_parent_for_backface ||
+      flags.is_frame_paint_offset_translation !=
+          other.flags.is_frame_paint_offset_translation ||
+      flags.is_for_svg_child != other.flags.is_for_svg_child ||
+      backface_visibility != other.backface_visibility ||
+      rendering_context_id != other.rendering_context_id ||
+      compositor_element_id != other.compositor_element_id ||
+      scroll != other.scroll ||
+      scroll_translation_for_fixed != other.scroll_translation_for_fixed ||
+      !StickyConstraintEquals(other) ||
+      visible_frame_element_id != other.visible_frame_element_id) {
+    return PaintPropertyChangeType::kChangedOnlyValues;
+  }
+
+  bool matrix_changed =
+      !transform_and_origin.TransformEquals(other.transform_and_origin);
+  bool origin_changed =
+      transform_and_origin.Origin() != other.transform_and_origin.Origin();
+  bool transform_changed = matrix_changed || origin_changed;
+
+  bool transform_has_simple_change = true;
+  if (!transform_changed) {
+    transform_has_simple_change = false;
+  } else if (!origin_changed &&
+             animation_state.is_running_animation_on_compositor) {
+    // |is_running_animation_on_compositor| means a transform animation is
+    // running. Composited transform origin animations are not supported so
+    // origin changes need to be considered as simple changes.
+    transform_has_simple_change = false;
+  } else if (matrix_changed &&
+             !transform_and_origin.ChangePreserves2dAxisAlignment(
+                 other.transform_and_origin)) {
+    // An additional cc::EffectNode may be required if
+    // blink::TransformPaintPropertyNode is not axis-aligned (see:
+    // PropertyTreeManager::NeedsSyntheticEffect). Changes to axis alignment
+    // are therefore treated as non-simple. We do not need to check origin
+    // because axis alignment is not affected by transform origin.
+    transform_has_simple_change = false;
+  }
+
+  // If the transform changed, and it's not simple then we need to report
+  // values change.
+  if (transform_changed && !transform_has_simple_change &&
+      !animation_state.is_running_animation_on_compositor) {
+    return PaintPropertyChangeType::kChangedOnlyValues;
+  }
+
+  bool non_reraster_values_changed =
+      direct_compositing_reasons != other.direct_compositing_reasons;
+  // Both simple value change and non-reraster change is upgraded to value
+  // change.
+  if (non_reraster_values_changed && transform_has_simple_change)
+    return PaintPropertyChangeType::kChangedOnlyValues;
+  if (non_reraster_values_changed)
+    return PaintPropertyChangeType::kChangedOnlyNonRerasterValues;
+  if (transform_has_simple_change)
+    return PaintPropertyChangeType::kChangedOnlySimpleValues;
+  // At this point, our transform change isn't simple, and the above checks
+  // didn't return a values change, so it must mean that we're running a
+  // compositor animation here.
+  if (transform_changed) {
+    DCHECK(animation_state.is_running_animation_on_compositor);
+    return PaintPropertyChangeType::kChangedOnlyCompositedValues;
+  }
+  return PaintPropertyChangeType::kUnchanged;
+}
+
 // The root of the transform tree. The root transform node references the root
 // scroll node.
 const TransformPaintPropertyNode& TransformPaintPropertyNode::Root() {
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
index 14fc520..62db524 100644
--- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
@@ -156,7 +156,7 @@
 
   // To make it less verbose and more readable to construct and update a node,
   // a struct with default values is used to represent the state.
-  struct State {
+  struct PLATFORM_EXPORT State {
     TransformAndOrigin transform_and_origin;
     scoped_refptr<const ScrollPaintPropertyNode> scroll;
     scoped_refptr<const TransformPaintPropertyNode>
@@ -182,84 +182,7 @@
 
     PaintPropertyChangeType ComputeChange(
         const State& other,
-        const AnimationState& animation_state) const {
-      // Whether or not a node is considered a frame root should be invariant.
-      DCHECK_EQ(flags.is_frame_paint_offset_translation,
-                other.flags.is_frame_paint_offset_translation);
-
-      if (flags.flattens_inherited_transform !=
-              other.flags.flattens_inherited_transform ||
-          flags.in_subtree_of_page_scale !=
-              other.flags.in_subtree_of_page_scale ||
-          flags.animation_is_axis_aligned !=
-              other.flags.animation_is_axis_aligned ||
-          flags.delegates_to_parent_for_backface !=
-              other.flags.delegates_to_parent_for_backface ||
-          flags.is_frame_paint_offset_translation !=
-              other.flags.is_frame_paint_offset_translation ||
-          flags.is_for_svg_child != other.flags.is_for_svg_child ||
-          backface_visibility != other.backface_visibility ||
-          rendering_context_id != other.rendering_context_id ||
-          compositor_element_id != other.compositor_element_id ||
-          scroll != other.scroll ||
-          scroll_translation_for_fixed != other.scroll_translation_for_fixed ||
-          !StickyConstraintEquals(other) ||
-          visible_frame_element_id != other.visible_frame_element_id) {
-        return PaintPropertyChangeType::kChangedOnlyValues;
-      }
-
-      bool matrix_changed =
-          !transform_and_origin.TransformEquals(other.transform_and_origin);
-      bool origin_changed =
-          transform_and_origin.Origin() != other.transform_and_origin.Origin();
-      bool transform_changed = matrix_changed || origin_changed;
-
-      bool transform_has_simple_change = true;
-      if (!transform_changed) {
-        transform_has_simple_change = false;
-      } else if (!origin_changed &&
-                 animation_state.is_running_animation_on_compositor) {
-        // |is_running_animation_on_compositor| means a transform animation is
-        // running. Composited transform origin animations are not supported so
-        // origin changes need to be considered as simple changes.
-        transform_has_simple_change = false;
-      } else if (matrix_changed &&
-                 !transform_and_origin.ChangePreserves2dAxisAlignment(
-                     other.transform_and_origin)) {
-        // An additional cc::EffectNode may be required if
-        // blink::TransformPaintPropertyNode is not axis-aligned (see:
-        // PropertyTreeManager::NeedsSyntheticEffect). Changes to axis alignment
-        // are therefore treated as non-simple. We do not need to check origin
-        // because axis alignment is not affected by transform origin.
-        transform_has_simple_change = false;
-      }
-
-      // If the transform changed, and it's not simple then we need to report
-      // values change.
-      if (transform_changed && !transform_has_simple_change &&
-          !animation_state.is_running_animation_on_compositor) {
-        return PaintPropertyChangeType::kChangedOnlyValues;
-      }
-
-      bool non_reraster_values_changed =
-          direct_compositing_reasons != other.direct_compositing_reasons;
-      // Both simple value change and non-reraster change is upgraded to value
-      // change.
-      if (non_reraster_values_changed && transform_has_simple_change)
-        return PaintPropertyChangeType::kChangedOnlyValues;
-      if (non_reraster_values_changed)
-        return PaintPropertyChangeType::kChangedOnlyNonRerasterValues;
-      if (transform_has_simple_change)
-        return PaintPropertyChangeType::kChangedOnlySimpleValues;
-      // At this point, our transform change isn't simple, and the above checks
-      // didn't return a values change, so it must mean that we're running a
-      // compositor animation here.
-      if (transform_changed) {
-        DCHECK(animation_state.is_running_animation_on_compositor);
-        return PaintPropertyChangeType::kChangedOnlyCompositedValues;
-      }
-      return PaintPropertyChangeType::kUnchanged;
-    }
+        const AnimationState& animation_state) const;
 
     bool StickyConstraintEquals(const State& other) const {
       if (!sticky_constraint && !other.sticky_constraint)
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
index c809dd1..e7b552a 100644
--- a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
@@ -357,8 +357,7 @@
 void RawResourceClient::DidDownloadToBlob(Resource*,
                                           scoped_refptr<BlobDataHandle>) {}
 
-RawResourceClientStateChecker::RawResourceClientStateChecker()
-    : state_(kNotAddedAsClient) {}
+RawResourceClientStateChecker::RawResourceClientStateChecker() = default;
 
 NOINLINE void RawResourceClientStateChecker::WillAddClient() {
   SECURITY_CHECK(state_ == kNotAddedAsClient);
@@ -367,7 +366,8 @@
 
 NOINLINE void RawResourceClientStateChecker::WillRemoveClient() {
   SECURITY_CHECK(state_ != kNotAddedAsClient);
-  state_ = kNotAddedAsClient;
+  SECURITY_CHECK(state_ != kDetached);
+  state_ = kDetached;
 }
 
 NOINLINE void RawResourceClientStateChecker::RedirectReceived() {
@@ -440,6 +440,7 @@
   base::debug::Alias(&mark2);
 
   SECURITY_CHECK(state_ != kNotAddedAsClient);
+  SECURITY_CHECK(state_ != kDetached);
   SECURITY_CHECK(state_ != kNotifyFinished);
 
   // TODO(https://crbug.com/1158346): Remove these CHECKs once the investigation
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource.h b/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
index 3608238f..0f14e02c 100644
--- a/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
@@ -220,9 +220,10 @@
     kDataReceived,
     kDataDownloaded,
     kDidDownloadToBlob,
-    kNotifyFinished
+    kNotifyFinished,
+    kDetached,
   };
-  State state_;
+  State state_ = kNotAddedAsClient;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_response.h b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
index 71b055b..84a9b4a 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_response.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
@@ -520,6 +520,7 @@
   // Whether the resource came from the cache and validated over the network.
   bool is_validated_ : 1;
 
+  // [spec] https://fetch.spec.whatwg.org/#response-request-includes-credentials
   // The request's |includeCredentials| value from the "HTTP-network fetch"
   // algorithm.
   // See: https://fetch.spec.whatwg.org/#concept-http-network-fetch
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 9a0b43c..5253b87 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -500,16 +500,6 @@
     {
       name: "CorsRFC1918",
     },
-    // This block isn't really used on its own. Blink doesn't really have to
-    // know about COEP:credentialless. However it is used for "declaring" the
-    // OriginTrial feature name. This is similar to:
-    // https://chromium-review.googlesource.com/c/chromium/src/+/2366778
-    // for CrossOriginOpenerPolicyReporting.
-    {
-      name: "CrossOriginEmbedderPolicyCredentialless",
-      origin_trial_feature_name: "CrossOriginEmbedderPolicyCredentiallessOriginTrial",
-      status: "experimental",
-    },
     {
       name: "CrossOriginIsolation",
       status: "stable",
@@ -1061,7 +1051,6 @@
       // Portals, as we require the support of the browser process to fully
       // enable the feature. Enabling this runtime enabled feature alone has no
       // effect.
-      status: "experimental",
       origin_trial_feature_name: "FencedFrames",
     },
     {
@@ -2432,6 +2421,7 @@
     },
     {
       name: "WebAppDarkMode",
+      origin_trial_feature_name: "WebAppDarkMode",
       status: "experimental",
     },
     {
diff --git a/third_party/blink/renderer/platform/video_capture/video_capture_impl.cc b/third_party/blink/renderer/platform/video_capture/video_capture_impl.cc
index dbbfa2de1..2739ebf 100644
--- a/third_party/blink/renderer/platform/video_capture/video_capture_impl.cc
+++ b/third_party/blink/renderer/platform/video_capture/video_capture_impl.cc
@@ -750,13 +750,29 @@
   gpu_memory_buffer_support_ = std::move(gpu_memory_buffer_support);
 }
 
-void VideoCaptureImpl::OnStateChanged(media::mojom::VideoCaptureState state) {
-  DVLOG(1) << __func__ << " state: " << state;
+void VideoCaptureImpl::OnStateChanged(
+    media::mojom::blink::VideoCaptureResultPtr result) {
   DCHECK_CALLED_ON_VALID_THREAD(io_thread_checker_);
 
   // Stop the startup deadline timer as something has happened.
   startup_timeout_.Stop();
 
+  if (result->which() ==
+      media::mojom::blink::VideoCaptureResult::Tag::ERROR_CODE) {
+    DVLOG(1) << __func__ << " Failed with an error.";
+    OnLog("VideoCaptureImpl changing state to VIDEO_CAPTURE_STATE_ERROR");
+    for (const auto& client : clients_)
+      client.second.state_update_cb.Run(blink::VIDEO_CAPTURE_STATE_ERROR);
+    clients_.clear();
+    state_ = VIDEO_CAPTURE_STATE_ERROR;
+
+    RecordStartOutcomeUMA(start_timedout_ ? VideoCaptureStartOutcome::kTimedout
+                                          : VideoCaptureStartOutcome::kFailed);
+    return;
+  }
+
+  media::mojom::VideoCaptureState state = result->get_state();
+  DVLOG(1) << __func__ << " state: " << state;
   switch (state) {
     case media::mojom::VideoCaptureState::STARTED:
       OnLog("VideoCaptureImpl changing state to VIDEO_CAPTURE_STATE_STARTED");
@@ -787,17 +803,6 @@
       for (const auto& client : clients_)
         client.second.state_update_cb.Run(blink::VIDEO_CAPTURE_STATE_RESUMED);
       break;
-    case media::mojom::VideoCaptureState::FAILED:
-      OnLog("VideoCaptureImpl changing state to VIDEO_CAPTURE_STATE_ERROR");
-      for (const auto& client : clients_)
-        client.second.state_update_cb.Run(blink::VIDEO_CAPTURE_STATE_ERROR);
-      clients_.clear();
-      state_ = VIDEO_CAPTURE_STATE_ERROR;
-
-      RecordStartOutcomeUMA(start_timedout_
-                                ? VideoCaptureStartOutcome::kTimedout
-                                : VideoCaptureStartOutcome::kFailed);
-      break;
     case media::mojom::VideoCaptureState::ENDED:
       OnLog("VideoCaptureImpl changing state to VIDEO_CAPTURE_STATE_ENDED");
       // We'll only notify the client that the stream has stopped.
@@ -1097,7 +1102,8 @@
 
   start_timedout_ = true;
 
-  OnStateChanged(media::mojom::VideoCaptureState::FAILED);
+  OnStateChanged(media::mojom::blink::VideoCaptureResult::NewErrorCode(
+      media::VideoCaptureError::kVideoCaptureImplTimedOutOnStart));
 }
 
 void VideoCaptureImpl::OnDeviceSupportedFormats(
diff --git a/third_party/blink/renderer/platform/video_capture/video_capture_impl.h b/third_party/blink/renderer/platform/video_capture/video_capture_impl.h
index 7c68827..a8366dbd 100644
--- a/third_party/blink/renderer/platform/video_capture/video_capture_impl.h
+++ b/third_party/blink/renderer/platform/video_capture/video_capture_impl.h
@@ -111,7 +111,8 @@
       std::unique_ptr<gpu::GpuMemoryBufferSupport> gpu_memory_buffer_support);
 
   // media::mojom::VideoCaptureObserver implementation.
-  void OnStateChanged(media::mojom::VideoCaptureState state) override;
+  void OnStateChanged(
+      media::mojom::blink::VideoCaptureResultPtr result) override;
   void OnNewBuffer(
       int32_t buffer_id,
       media::mojom::blink::VideoBufferHandlePtr buffer_handle) override;
diff --git a/third_party/blink/renderer/platform/video_capture/video_capture_impl_test.cc b/third_party/blink/renderer/platform/video_capture/video_capture_impl_test.cc
index f8ba2d1..759fae8 100644
--- a/third_party/blink/renderer/platform/video_capture/video_capture_impl_test.cc
+++ b/third_party/blink/renderer/platform/video_capture/video_capture_impl_test.cc
@@ -171,7 +171,8 @@
     ON_CALL(mock_video_capture_host_, DoStart(_, _, _))
         .WillByDefault(InvokeWithoutArgs([this]() {
           video_capture_impl_->OnStateChanged(
-              media::mojom::VideoCaptureState::STARTED);
+              media::mojom::blink::VideoCaptureResult::NewState(
+                  media::mojom::VideoCaptureState::STARTED));
         }));
 
     video_capture_impl_->SetGpuMemoryBufferSupportForTesting(
@@ -258,10 +259,6 @@
         &VideoCaptureImplTest::OnDeviceFormatsInUse, base::Unretained(this)));
   }
 
-  void OnStateChanged(media::mojom::VideoCaptureState state) {
-    video_capture_impl_->OnStateChanged(state);
-  }
-
   const base::UnguessableToken session_id_ = base::UnguessableToken::Create();
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
@@ -711,7 +708,8 @@
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, session_id_, params_small_))
       .WillOnce(DoAll(InvokeWithoutArgs([this]() {
                         video_capture_impl_->OnStateChanged(
-                            media::mojom::VideoCaptureState::STARTED);
+                            media::mojom::blink::VideoCaptureResult::NewState(
+                                media::mojom::VideoCaptureState::STARTED));
                       }),
                       SaveArg<2>(&params)));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
@@ -733,7 +731,9 @@
 
   StartCapture(0, params_small_);
 
-  OnStateChanged(media::mojom::VideoCaptureState::ENDED);
+  video_capture_impl_->OnStateChanged(
+      media::mojom::blink::VideoCaptureResult::NewState(
+          media::mojom::VideoCaptureState::ENDED));
 
   StopCapture(0);
 }
@@ -747,7 +747,9 @@
 
   StartCapture(0, params_small_);
 
-  OnStateChanged(media::mojom::VideoCaptureState::FAILED);
+  video_capture_impl_->OnStateChanged(
+      media::mojom::blink::VideoCaptureResult::NewErrorCode(
+          media::VideoCaptureError::kIntentionalErrorRaisedByUnitTest));
 
   StopCapture(0);
 
@@ -778,14 +780,18 @@
 
   EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
   EXPECT_CALL(mock_video_capture_host_, RequestRefreshFrame(_));
-  video_capture_impl_->OnStateChanged(media::mojom::VideoCaptureState::STARTED);
+  video_capture_impl_->OnStateChanged(
+      media::mojom::blink::VideoCaptureResult::NewState(
+          media::mojom::VideoCaptureState::STARTED));
   Mock::VerifyAndClearExpectations(this);
   Mock::VerifyAndClearExpectations(&mock_video_capture_host_);
 
   // Additional STARTED will cause RequestRefreshFrame a second time.
   EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
   EXPECT_CALL(mock_video_capture_host_, RequestRefreshFrame(_));
-  video_capture_impl_->OnStateChanged(media::mojom::VideoCaptureState::STARTED);
+  video_capture_impl_->OnStateChanged(
+      media::mojom::blink::VideoCaptureResult::NewState(
+          media::mojom::VideoCaptureState::STARTED));
   Mock::VerifyAndClearExpectations(this);
   Mock::VerifyAndClearExpectations(&mock_video_capture_host_);
 
@@ -816,14 +822,18 @@
 
   EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
   EXPECT_CALL(mock_video_capture_host_, RequestRefreshFrame(_));
-  video_capture_impl_->OnStateChanged(media::mojom::VideoCaptureState::STARTED);
+  video_capture_impl_->OnStateChanged(
+      media::mojom::blink::VideoCaptureResult::NewState(
+          media::mojom::VideoCaptureState::STARTED));
   Mock::VerifyAndClearExpectations(this);
   Mock::VerifyAndClearExpectations(&mock_video_capture_host_);
 
   // Additional STARTED will cause RequestRefreshFrame a second time.
   EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
   EXPECT_CALL(mock_video_capture_host_, RequestRefreshFrame(_));
-  video_capture_impl_->OnStateChanged(media::mojom::VideoCaptureState::STARTED);
+  video_capture_impl_->OnStateChanged(
+      media::mojom::blink::VideoCaptureResult::NewState(
+          media::mojom::VideoCaptureState::STARTED));
   Mock::VerifyAndClearExpectations(this);
   Mock::VerifyAndClearExpectations(&mock_video_capture_host_);
 
@@ -853,14 +863,18 @@
 
   EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
   EXPECT_CALL(mock_video_capture_host_, RequestRefreshFrame(_));
-  video_capture_impl_->OnStateChanged(media::mojom::VideoCaptureState::STARTED);
+  video_capture_impl_->OnStateChanged(
+      media::mojom::blink::VideoCaptureResult::NewState(
+          media::mojom::VideoCaptureState::STARTED));
   Mock::VerifyAndClearExpectations(this);
   Mock::VerifyAndClearExpectations(&mock_video_capture_host_);
 
   // Additional STARTED will cause RequestRefreshFrame a second time.
   EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
   EXPECT_CALL(mock_video_capture_host_, RequestRefreshFrame(_));
-  video_capture_impl_->OnStateChanged(media::mojom::VideoCaptureState::STARTED);
+  video_capture_impl_->OnStateChanged(
+      media::mojom::blink::VideoCaptureResult::NewState(
+          media::mojom::VideoCaptureState::STARTED));
   Mock::VerifyAndClearExpectations(this);
   Mock::VerifyAndClearExpectations(&mock_video_capture_host_);
 
@@ -904,7 +918,9 @@
 
   // Finally callback that the capture has started, should respond.
   EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
-  video_capture_impl_->OnStateChanged(media::mojom::VideoCaptureState::STARTED);
+  video_capture_impl_->OnStateChanged(
+      media::mojom::blink::VideoCaptureResult::NewState(
+          media::mojom::VideoCaptureState::STARTED));
 
   EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STOPPED));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
@@ -924,7 +940,8 @@
       .WillByDefault(InvokeWithoutArgs([this]() {
         // Go straight to Failed. Do not pass Go. Do not collect £200.
         video_capture_impl_->OnStateChanged(
-            media::mojom::VideoCaptureState::FAILED);
+            media::mojom::blink::VideoCaptureResult::NewErrorCode(
+                media::VideoCaptureError::kIntentionalErrorRaisedByUnitTest));
       }));
 
   StartCapture(0, params_small_);
diff --git a/third_party/blink/renderer/platform/weborigin/kurl.cc b/third_party/blink/renderer/platform/weborigin/kurl.cc
index cb24ef0..19bc02e 100644
--- a/third_party/blink/renderer/platform/weborigin/kurl.cc
+++ b/third_party/blink/renderer/platform/weborigin/kurl.cc
@@ -162,18 +162,18 @@
   // and including feed would allow feeds to potentially let someone's blog
   // read the contents of the clipboard on a drag, even without a drop.
   // Likewise with using the FrameLoader::shouldTreatURLAsLocal() function.
-  return ProtocolIs("file");
+  return ProtocolIs(url::kFileScheme);
 }
 
 bool ProtocolIsJavaScript(const String& url) {
-  return ProtocolIs(url, "javascript");
+  return ProtocolIs(url, url::kJavaScriptScheme);
 }
 
 const KURL& BlankURL() {
   DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<KURL>, static_blank_url, ());
   KURL& blank_url = *static_blank_url;
   if (blank_url.IsNull())
-    blank_url = KURL(AtomicString("about:blank"));
+    blank_url = KURL(AtomicString(url::kAboutBlankURL));
   return blank_url;
 }
 
@@ -181,7 +181,7 @@
   DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<KURL>, static_srcdoc_url, ());
   KURL& srcdoc_url = *static_srcdoc_url;
   if (srcdoc_url.IsNull())
-    srcdoc_url = KURL(AtomicString("about:srcdoc"));
+    srcdoc_url = KURL(AtomicString(url::kAboutSrcdocURL));
   return srcdoc_url;
 }
 
@@ -332,7 +332,7 @@
 }
 
 bool KURL::ProtocolIsJavaScript() const {
-  return ComponentStringView(parsed_.scheme) == "javascript";
+  return ComponentStringView(parsed_.scheme) == url::kJavaScriptScheme;
 }
 
 bool KURL::ProtocolIsInHTTPFamily() const {
@@ -497,8 +497,8 @@
   if (SchemeRegistry::IsSpecialScheme(Protocol()) &&
       SchemeRegistry::IsSpecialScheme(new_protocol_canon)) {
     // The protocol is lower-cased during canonicalization.
-    const bool new_protocol_is_file = new_protocol_canon == "file";
-    const bool old_protocol_is_file = ProtocolIs("file");
+    const bool new_protocol_is_file = new_protocol_canon == url::kFileScheme;
+    const bool old_protocol_is_file = ProtocolIs(url::kFileScheme);
 
     // https://url.spec.whatwg.org/#scheme-state
     // 3. If url includes credentials or has a non-null port, and buffer is
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index a892500..5cbf3bf 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -703,6 +703,7 @@
 crbug.com/591099 external/wpt/css/css-writing-modes/line-box-height-vlr-023.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes/ortho-htb-alongside-vrl-floats-006.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes/ortho-htb-alongside-vrl-floats-014.xht [ Failure ]
+crbug.com/1281318 external/wpt/css/css-writing-modes/padding-percent-orthogonal-dynamic.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vlr-001.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vlr-004.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes/sizing-orthog-prct-htb-in-vlr-001.xht [ Failure ]
@@ -722,7 +723,6 @@
 crbug.com/591099 external/wpt/css/css-writing-modes/vertical-alignment-003.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes/vertical-alignment-009.xht [ Failure ]
 
-
 ### external/wpt/css/cssom-view/
 crbug.com/997705 external/wpt/css/cssom-view/getBoundingClientRect-empty-inline.html [ Failure ]
 crbug.com/953479 external/wpt/css/cssom-view/offsetTopLeft-empty-inline.html [ Failure ]
@@ -1327,6 +1327,9 @@
 crbug.com/667785 external/wpt/css/css-grid/table-grid-item-005.html [ Failure ]
 
 ### external/wpt/css/css-highlight-api/
+crbug.com/1274174 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-* [ Failure ]
+
+### external/wpt/css/css-highlight-api/
 crbug.com/591099 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-inheritance-* [ Failure ]
 
 ### external/wpt/css/css-pseudo/
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index 3f310bd..536cf9be 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -1835,6 +1835,7 @@
 external/wpt/webusb/usbDevice_claimInterface-manual.https.html [ Skip ]
 external/wpt/webusb/usbDevice_controlTransferIn-manual.https.html [ Skip ]
 external/wpt/webusb/usbDevice_transferIn-manual.https.html [ Skip ]
+external/wpt/webusb/usbDevice_reset-manual.https.html [ Skip ]
 wpt_internal/hid/hidDevice_collections-manual.https.html [ Skip ]
 external/wpt/serial/serialPort_loopback-manual.https.html [ Skip ]
 external/wpt/serial/serialPort_loopback_BreakError-manual.https.html [ Skip ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 8256fb0..385b895 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -254,6 +254,12 @@
 crbug.com/1093466 virtual/portals/http/tests/portals/* [ Pass ]
 crbug.com/1093466 virtual/portals/wpt_internal/portals/* [ Pass ]
 
+# These tests require fenced-frames
+# Keep this in sync with VirtualTestSuites.
+crbug.com/1123606 wpt_internal/fenced_frame/* [ Skip ]
+crbug.com/1123606 fenced_frame/* [ Skip ]
+crbug.com/1123606 virtual/fenced-frame-shadow-dom/* [ Pass ]
+
 # These tests require the experimental prerender feature.
 # See https://crbug.com/1126305.
 crbug.com/1126305 external/wpt/speculation-rules/prerender/* [ Skip ]
@@ -827,8 +833,11 @@
 crbug.com/666433 external/wpt/css/css-text-decor/text-emphasis-style-021.html [ Failure ]
 crbug.com/666433 external/wpt/css/css-text-decor/text-emphasis-style-filled-001.xht [ Failure ]
 crbug.com/666433 external/wpt/css/css-text-decor/text-emphasis-style-open-001.xht [ Failure ]
+crbug.com/1281409 external/wpt/css/css-text-decor/text-emphasis-style-property-010Cf.html [ Failure ]
+crbug.com/1281409 external/wpt/css/css-text-decor/text-emphasis-style-property-010Cn.html [ Failure ]
 crbug.com/666433 external/wpt/css/css-text-decor/text-emphasis-style-shape-001.xht [ Failure ]
 crbug.com/666433 external/wpt/css/css-text-decor/text-emphasis-style-string-001.xht [ Failure ]
+crbug.com/1281289 external/wpt/css/css-text/letter-spacing/letter-spacing-211.html [ Failure ]
 crbug.com/949909 external/wpt/css/css-text-decor/text-underline-position-073-manual.html [ Skip ]
 crbug.com/949909 external/wpt/css/css-text-decor/text-underline-position-072-manual.html [ Skip ]
 crbug.com/949909 external/wpt/css/css-text-decor/text-underline-position-071-manual.html [ Skip ]
@@ -1505,6 +1514,7 @@
 crbug.com/1035708 external/wpt/css/css-pseudo/target-text-dynamic-002.html [ Failure ]
 crbug.com/1035708 external/wpt/css/css-pseudo/target-text-dynamic-003.html [ Failure ]
 crbug.com/1035708 external/wpt/css/css-pseudo/target-text-dynamic-004.html [ Failure ]
+crbug.com/1179938 external/wpt/css/css-pseudo/target-text-text-decoration-001.html [ Failure ]
 
 crbug.com/1205953 external/wpt/css/css-will-change/will-change-fixpos-cb-position-1.html [ Failure ]
 crbug.com/1207788 external/wpt/css/css-will-change/will-change-stacking-context-mask-1.html [ Failure ]
@@ -2812,6 +2822,7 @@
 crbug.com/626703 [ Fuchsia ] external/wpt/dom/historical.html [ Skip ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-025.html [ Failure ]
 crbug.com/626703 [ Mac10.15 ] external/wpt/html/cross-origin-embedder-policy/reporting-subresource-corp.https.html [ Skip Timeout ]
 crbug.com/626703 external/wpt/css/css-text/hyphens/hyphenate-character-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/hyphens/hyphenate-character-002.html [ Failure ]
@@ -7442,6 +7453,8 @@
 crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-004.html [ Failure ]
 crbug.com/1163437 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-below-grammar.html [ Failure ]
 crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-below-target-text.html [ Failure ]
+crbug.com/1274174 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-001.html [ Pass ]
+crbug.com/1274174 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-dynamic-001.html [ Pass ]
 crbug.com/1147859 [ Linux ] external/wpt/css/css-highlight-api/painting/* [ Failure ]
 crbug.com/1147859 [ Linux ] virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/* [ Failure ]
 crbug.com/1024156 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-inheritance-* [ Failure ]
@@ -8133,7 +8146,7 @@
 crbug.com/1274919 http/tests/inspector-protocol/page/consecutive-navigate.js [ Pass Timeout ]
 
 # Sheriff 2021-11-23
-crbug.com/1270963 wpt_internal/fenced_frame/create-credential.https.html [ Skip ]
+crbug.com/1270963 virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/create-credential.https.html [ Skip ]
 crbug.com/1046784 http/tests/devtools/tracing/timeline-receive-response-event.js [ Failure Pass ]
 crbug.com/1273282 [ Win7 ] external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.commit.html [ Failure Pass ]
 
@@ -8187,6 +8200,43 @@
 # Sheriff 2021-12-13
 crbug.com/1279586 [ Mac ] external/wpt/IndexedDB/idbobjectstore-rename-abort.html [ Failure ]
 
-# Sheriff 2021-12-15
-crbug.com/1280387 [ Mac ] external/wpt/dom/abort/event.any.html [ Failure ]
-crbug.com/1280387 [ Mac ] external/wpt/dom/abort/event.any.worker.html [ Failure ]
+# Sheriff 2021-12-20
+crbug.com/1259188 [ Mac10.15 ] virtual/gpu-rasterization/images/color-profile-animate.html [ Failure Pass ]
+
+# Sheriff 2021-12-21
+crbug.com/1281780 [ Mac ] media/video-poster-after-playing.html [ Failure Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/compositing/2d.composite.canvas.source-out.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/compositing/2d.composite.clip.xor.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/compositing/2d.composite.operation.darker.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/compositing/2d.composite.uncovered.pattern.copy.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-in.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.null.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.wrongtype.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.path.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.zero.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/drawing-text-to-the-canvas/2d.text.draw.fill.maxWidth.large-manual.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-5.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.current.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/line-styles/2d.line.cap.round.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/line-styles/2d.line.cap.square.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/line-styles/2d.line.miter.obtuse.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/manual/fill-and-stroke-styles/2d.fillStyle.parse.current.notrendered.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/manual/wide-gamut-canvas/canvas-drawImage-offscreenCanvas.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/path-objects/2d.path.isPointInPath.transform.1.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/path-objects/2d.path.roundrect.3.radii.1.double.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/path-objects/2d.path.roundrect.4.radii.4.dompointinit.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/pixel-manipulation/2d.imageData.create2.type.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/pixel-manipulation/2d.imageData.get.order.cols.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/pixel-manipulation/2d.imageData.object.round.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/shadows/2d.shadow.canvas.transparent.2.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/shadows/2d.shadow.enable.off.2.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/text-styles/2d.text.draw.space.collapse.start.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/text-styles/2d.text.font.parse.tiny.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowOffsetY.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/element/transformations/2d.transformation.transform.multiply.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/offscreen/2d.conformance.requirements.missingargs.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/offscreen/compositing/2d.composite.globalAlpha.default.html [ Crash Pass ]
+crbug.com/1281782 [ Mac ] virtual/no-alloc-direct-call/external/wpt/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.html [ Crash Pass ]
+crbug.com/1281792 external/wpt/event-timing/min-duration-threshold.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 30b7bbc..29b04f6 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1017,7 +1017,7 @@
       "http/tests/inspector-protocol/target/auto-attach-auction-worklet.js"
     ],
     "args": [
-      "--enable-features=InterestGroupStorage,AdInterestGroupAPI,Fledge"
+      "--enable-features=InterestGroupStorage,AdInterestGroupAPI,Fledge,FencedFrames:implementation_type/shadow_dom"
     ]
   },
   {
@@ -1057,6 +1057,14 @@
     "args": ["--enable-features=FencedFrames:implementation_type/mparch,Prerender2"]
   },
   {
+    "prefix": "fenced-frame-shadow-dom",
+    "bases": [
+      "fenced_frame",
+      "wpt_internal/fenced_frame"
+    ],
+    "args": ["--enable-features=FencedFrames:implementation_type/shadow_dom,Prerender2"]
+  },
+  {
     "prefix": "disable-custom-element-default-style",
     "bases": ["external/wpt/css/css-cascade/presentational-hints-cascade.html"],
     "args": ["--disable-blink-features=CustomElementDefaultStyle"]
diff --git a/third_party/blink/web_tests/W3CImportExpectations b/third_party/blink/web_tests/W3CImportExpectations
index a0e58136..4698e66 100644
--- a/third_party/blink/web_tests/W3CImportExpectations
+++ b/third_party/blink/web_tests/W3CImportExpectations
@@ -372,12 +372,9 @@
 external/wpt/webdriver/tests/new_session/invalid_capabilities.py [ Skip ]
 external/wpt/webdriver/tests/new_session/response.py [ Skip ]
 external/wpt/webdriver/tests/send_alert_text/send.py [ Skip ]
-external/wpt/webdriver/tests/bidi/session_subscribe/subscribe.py [ Skip ]
-external/wpt/webdriver/tests/bidi/session_unsubscribe/unsubscribe.py [ Skip ]
-external/wpt/webdriver/tests/bidi/log_entry_added/__init__.py [ Skip ]
-external/wpt/webdriver/tests/bidi/log_entry_added/console_args.py [ Skip ]
-external/wpt/webdriver/tests/bidi/log_entry_added/javascript.py [ Skip ]
-external/wpt/webdriver/tests/bidi/log_entry_added/subscription.py [ Skip ]
+
+# Skip all bidi tests for now
+external/wpt/webdriver/tests/bidi [ Skip ]
 
 # WebVr was never supported on Chrome more than as an experimental feature,
 # and even that level of support is now removed. WebVr was replaced with WebXr.
@@ -427,10 +424,6 @@
 external/wpt/webdriver/tests/find_element_from_shadow_root/find.py [ Skip ]
 external/wpt/webdriver/tests/find_elements_from_shadow_root/find.py [ Skip ]
 
-# Sheriff 09/2021, crbug.com/1249864
-external/wpt/webdriver/tests/bidi/new_session/connect.py [ Skip ]
-external/wpt/webdriver/tests/bidi/log_entry_added/console.py [ Skip ]
-
 # Sheriff 16/09/2021, crbug.com/1250215
 external/wpt/html/dom/idlharness.worker.html [ Skip ]
 external/wpt/html/dom/idlharness.https.html [ Skip ]
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 f2d877a3..fad5b6c 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
@@ -13731,6 +13731,13 @@
       {}
      ]
     ],
+    "usbDevice_reset-manual.https.html": [
+     "63a0c356ee4efb34dc3367ba8f1605d995a819f7",
+     [
+      null,
+      {}
+     ]
+    ],
     "usbDevice_transferIn-manual.https.html": [
      "c0fad37e2090c1dfc06f48c978bb9fdf424f4fa9",
      [
@@ -242379,6 +242386,10 @@
        "ecd72b7516c41c83b6e8d320c9b0b87f9c231781",
        []
       ],
+      "percentage-padding-orthogonal-expected.txt": [
+       "7dde7f870c4d04ff81e8727b5e90aec1f79907de",
+       []
+      ],
       "pseudo-elements-002.tentative-ref.html": [
        "66bddf379d133e6c541c003c8e7a280118b678a4",
        []
@@ -357628,7 +357639,7 @@
        ]
       ],
       "layer-import-parsing.html": [
-       "110998e09b430f7f51299d02eb158aa33247da7e",
+       "f879ba88973965781104aca14e4a4bea5581e79a",
        [
         null,
         {}
@@ -357650,7 +357661,7 @@
       ]
      ],
      "revert-layer-008.html": [
-      "e21a178a59eec255e81cfe1ab82fc8bba93b3a62",
+      "cafb17dee1bef11a0e22795301903ceb856d2d29",
       [
        null,
        {}
@@ -358374,6 +358385,13 @@
         {}
        ]
       ],
+      "percentage-padding-orthogonal.html": [
+       "8e0922b200d50c71ecaa9d355f34c3ef692b4413",
+       [
+        null,
+        {}
+       ]
+      ],
       "pseudo-elements-001.html": [
        "c5fd15f40b060f5ad3bd04895f6e0dd191000177",
        [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/change-block-background.html b/third_party/blink/web_tests/external/wpt/css/css-break/change-block-background.html
new file mode 100644
index 0000000..48d33e3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/change-block-background.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+  <link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1278652">
+  <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+  <div style="columns:5; column-gap:0; column-fill:auto; width:100px; height:100px;">
+    <div style="height:200px; background:green;"></div>
+    <div id="elm" style="height:300px; background:red;"></div>
+  </div>
+  <script>
+    requestAnimationFrame(()=> {
+      requestAnimationFrame(()=> {
+        elm.style.background = "green";
+        document.documentElement.classList.remove("reftest-wait");
+      });
+    });
+  </script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-cascade/parsing/layer-import-parsing.html b/third_party/blink/web_tests/external/wpt/css/css-cascade/parsing/layer-import-parsing.html
index 110998e..f879ba8 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-cascade/parsing/layer-import-parsing.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-cascade/parsing/layer-import-parsing.html
@@ -10,7 +10,6 @@
     const style = document.createElement("style");
     document.head.append(style);
     const {sheet} = style;
-    document.head.removeChild(style);
     const {cssRules} = sheet;
 
     assert_equals(cssRules.length, 0, "Sheet should have no rules");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-cascade/revert-layer-008.html b/third_party/blink/web_tests/external/wpt/css/css-cascade/revert-layer-008.html
index e21a178..cafb17de 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-cascade/revert-layer-008.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-cascade/revert-layer-008.html
@@ -31,8 +31,10 @@
   return new Promise(resolve => requestAnimationFrame(resolve));
 }
 promise_test(async () => {
-  await raf();
   const target = document.getElementById('target');
+  getComputedStyle(target).getPropertyValue('font-size');
+
+  await raf();
   target.classList.toggle('reverted');
 
   const result = getComputedStyle(target).getPropertyValue('font-size');
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/percentage-padding-orthogonal-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/percentage-padding-orthogonal-expected.txt
new file mode 100644
index 0000000..7dde7f8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/percentage-padding-orthogonal-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS #container height measured with 500px width. Both container children visible
+PASS #container width 400px after padding is applied.
+PASS #container width 400px after padding is applied. #second is removed from the rendering
+PASS #container height measured with 499px width. Both container children visible
+FAIL #container width 399px after padding is applied. #second is removed from the rendering assert_equals: expected 399 but got 400
+PASS #container width 399x after padding is applied. #second is removed from the rendering
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/percentage-padding-orthogonal.html b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/percentage-padding-orthogonal.html
new file mode 100644
index 0000000..8e0922b2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/percentage-padding-orthogonal.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<title>CSS Container Queries Test: @container queries affecting height affecting percentage padding</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+  #vertical {
+    width: 500px;
+    writing-mode: vertical-lr;
+  }
+
+  #padded {
+    width: 100%;
+    box-sizing: border-box;
+    padding-right: 100%;
+    background-color: orange;
+  }
+
+  #horizontal {
+    writing-mode: horizontal-tb;
+    width: 100%;
+  }
+
+  #container {
+    width: 100%;
+    container-type: inline-size;
+    background-color: green;
+  }
+
+  #first, #second { height: 50px; }
+
+  @container size(width <= 400px) {
+    #second { display: none; }
+  }
+</style>
+<div id="vertical">
+  <div id="padded">
+    <div id="horizontal">
+      <div id="container">
+        <div id="first"></div>
+        <div id="second"></div>
+      </div>
+    </div>
+  </div>
+</div>
+<script>
+  setup(() => assert_implements_container_queries());
+
+  test(() => assert_equals(padded.offsetHeight, 100),
+       "#container height measured with 500px width. Both container children visible");
+  test(() => assert_equals(container.offsetWidth, 400),
+       "#container width 400px after padding is applied.");
+  test(() => assert_equals(container.offsetHeight, 50),
+       "#container width 400px after padding is applied. #second is removed from the rendering");
+
+  // Reduce width by 1px to test that a re-layout is not stateful.
+  vertical.style.width = "399px";
+
+  test(() => assert_equals(padded.offsetHeight, 100),
+       "#container height measured with 499px width. Both container children visible");
+  test(() => assert_equals(container.offsetWidth, 399),
+       "#container width 399px after padding is applied. #second is removed from the rendering");
+  test(() => assert_equals(container.offsetHeight, 50),
+       "#container width 399x after padding is applied. #second is removed from the rendering");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-001-ref.html
new file mode 100644
index 0000000..568e5f9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-001-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Highlight API Test: Reference ::highlight painting text-decoration</title>
+<style>
+  div {
+    float: left;
+    margin: 0.5em;
+    /* Setting transparent color is a workaround to avoid hitting the following Chromium bug: https://crbug.com/1179938.
+       This could be removed once that bug is fixed. */
+    color: transparent;
+  }
+</style>
+<p>The test passes if it matches the reference file.</p>
+<script>
+  let textDecorationStyles = [
+    "solid green underline", "double green underline", "dotted green underline", "dashed green underline", "wavy green underline",
+    "solid blue overline", "double blue overline", "dotted blue overline", "dashed blue overline", "wavy blue overline",
+    "solid magenta line-through", "double magenta line-through", "dotted magenta line-through", "dashed magenta line-through", "wavy magenta line-through",
+    "solid brown underline overline line-through", "double brown underline overline line-through", "dotted brown underline overline line-through", "dashed brown underline overline line-through", "wavy brown underline overline line-through",
+  ];
+
+  textDecorationStyles.forEach((textDecorationStyle, index) => {
+    document.styleSheets[0].insertRule(`#id${index} { text-decoration: ${textDecorationStyle}; }`);
+    let element = document.createElement("div");
+    element.id = `id${index}`;
+    element.innerHTML = textDecorationStyle;
+    document.body.appendChild(element);
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-001.html b/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-001.html
new file mode 100644
index 0000000..df6e5dd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-001.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Highlight API Test: ::highlight painting text-decoration</title>
+<link rel="help" href="https://drafts.csswg.org/css-highlight-api-1/">
+<link rel="match" href="custom-highlight-painting-text-decoration-001-ref.html">
+<meta name="assert" content="This test checks that ::highlight pseudo-element paints text-decorations defined by it.">
+<style>
+  div {
+    float: left;
+    margin: 0.5em;
+    /* Setting transparent color is a workaround to avoid hitting the following Chromium bug: https://crbug.com/1179938.
+       This could be removed once that bug is fixed. */
+    color: transparent;
+  }
+</style>
+<p>The test passes if it matches the reference file.</p>
+<script>
+  let textDecorationStyles = [
+    "solid green underline", "double green underline", "dotted green underline", "dashed green underline", "wavy green underline",
+    "solid blue overline", "double blue overline", "dotted blue overline", "dashed blue overline", "wavy blue overline",
+    "solid magenta line-through", "double magenta line-through", "dotted magenta line-through", "dashed magenta line-through", "wavy magenta line-through",
+    "solid brown underline overline line-through", "double brown underline overline line-through", "dotted brown underline overline line-through", "dashed brown underline overline line-through", "wavy brown underline overline line-through",
+  ];
+
+  textDecorationStyles.forEach((textDecorationStyle, index) => {
+    document.styleSheets[0].insertRule(`::highlight(h${index}) { text-decoration: ${textDecorationStyle}; }`);
+    let element = document.createElement("div");
+    element.innerHTML = textDecorationStyle;
+    document.body.appendChild(element);
+    let range = new Range();
+    range.setStart(element, 0);
+    range.setEnd(element, 1);
+    CSS.highlights.set(`h${index}`, new Highlight(range));
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-dynamic-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-dynamic-001-ref.html
new file mode 100644
index 0000000..3a713aa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-dynamic-001-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Highlight API Test: Reference ::highlight dynamic change text-decoration</title>
+<style>
+  ::highlight(example) {
+    text-decoration: solid underline green;
+  }
+</style>
+<p>The test passes if it line below has a green underline.</p>
+<div id="target">target</div>
+<script>
+  let range = new Range();
+  range.setStart(target, 0);
+  range.setEnd(target, 1);
+  CSS.highlights.set(`example`, new Highlight(range));
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-dynamic-001.html b/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-dynamic-001.html
new file mode 100644
index 0000000..050c7c7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-dynamic-001.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>CSS Highlight API Test: ::highlight dynamic change text-decoration</title>
+<link rel="help" href="https://drafts.csswg.org/css-highlight-api-1/">
+<link rel="match" href="custom-highlight-painting-text-decoration-dynamic-001-ref.html">
+<meta name="assert" content="This test checks that it's possible to modify dynamically the text-decoration of a custom highlight through ::highlight pseudo-element.">
+<script src="/common/reftest-wait.js"></script>
+<style>
+  ::highlight(example) {
+    text-decoration: solid underline red;
+  }
+</style>
+<p>The test passes if it line below has a green underline.</p>
+<div id="target">target</div>
+<script>
+  let range = new Range();
+  range.setStart(target, 0);
+  range.setEnd(target, 1);
+  CSS.highlights.set(`example`, new Highlight(range));
+
+  requestAnimationFrame(() => requestAnimationFrame(() => {
+    document.styleSheets[0].cssRules[0].style.textDecorationColor = "green";
+    takeScreenshot();
+  }));
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/triply-nested-with-fixedpos-in-abspos-crash.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/triply-nested-with-fixedpos-in-abspos-crash.html
new file mode 100644
index 0000000..8bed96f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/triply-nested-with-fixedpos-in-abspos-crash.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1278921">
+<div style="columns:2;">
+  <div style="columns:2; position:absolute;">
+    <div style="columns:2; position:absolute;">
+      <div style="columns:2; position:fixed;"></div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties-expected.txt
index f99b2ca33..1d6999f2 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 61 tests; 55 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 61 tests; 58 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Property font value 'italic small-caps 900 expanded 25px / 50px Ahem' in ::marker
 PASS Property font-family value 'Ahem' in ::marker
 PASS Property font-feature-settings value '"smcp"' in ::marker
@@ -49,10 +49,10 @@
 PASS Property word-break value 'break-word' in ::marker
 PASS Property word-spacing value '10px' in ::marker
 PASS Property text-decoration-skip-ink value 'none' in ::marker
-FAIL Property text-emphasis value 'dot rgb(0, 255, 0)' in ::marker assert_true: text-emphasis doesn't seem to be supported in the computed style expected true got false
-FAIL Property text-emphasis-color value 'rgb(0, 255, 0)' in ::marker assert_true: text-emphasis-color doesn't seem to be supported in the computed style expected true got false
-FAIL Property text-emphasis-position value 'under left' in ::marker assert_true: text-emphasis-position doesn't seem to be supported in the computed style expected true got false
-FAIL Property text-emphasis-style value 'dot' in ::marker assert_true: text-emphasis-style doesn't seem to be supported in the computed style expected true got false
+FAIL Property text-emphasis value 'filled dot rgb(0, 255, 0)' in ::marker assert_equals: expected "filled dot rgb(0, 255, 0)" but got ""
+PASS Property text-emphasis-color value 'rgb(0, 255, 0)' in ::marker
+PASS Property text-emphasis-position value 'under left' in ::marker
+PASS Property text-emphasis-style value 'filled dot' in ::marker
 PASS Property text-shadow value 'rgb(0, 255, 0) 1px 2px 3px' in ::marker
 PASS Property display value 'none' in ::marker
 PASS Property position value 'absolute' in ::marker
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties-in-animation-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties-in-animation-expected.txt
index 6226c7d..a389ccd 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties-in-animation-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties-in-animation-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 94 tests; 82 PASS, 12 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 94 tests; 86 PASS, 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Animation of font in ::marker
 PASS Animation of font-family in ::marker
 PASS Animation of font-feature-settings in ::marker
@@ -35,10 +35,10 @@
 PASS Animation of word-break in ::marker
 PASS Animation of word-spacing in ::marker
 PASS Animation of text-decoration-skip-ink in ::marker
-FAIL Animation of text-emphasis in ::marker assert_true: text-emphasis doesn't seem to be supported in the computed style expected true got false
-FAIL Animation of text-emphasis-color in ::marker assert_true: text-emphasis-color doesn't seem to be supported in the computed style expected true got false
-FAIL Animation of text-emphasis-position in ::marker assert_true: text-emphasis-position doesn't seem to be supported in the computed style expected true got false
-FAIL Animation of text-emphasis-style in ::marker assert_true: text-emphasis-style doesn't seem to be supported in the computed style expected true got false
+FAIL Animation of text-emphasis in ::marker assert_equals: expected "triangle rgb(50, 100, 100)" but got ""
+FAIL Animation of text-emphasis-color in ::marker assert_equals: expected "rgb(50, 100, 100)" but got "rgb(100, 0, 200)"
+PASS Animation of text-emphasis-position in ::marker
+PASS Animation of text-emphasis-style in ::marker
 PASS Animation of text-shadow in ::marker
 PASS Animation of display in ::marker
 PASS Animation of position in ::marker
@@ -82,10 +82,10 @@
 PASS Transition of word-break in ::marker
 PASS Transition of word-spacing in ::marker
 PASS Transition of text-decoration-skip-ink in ::marker
-FAIL Transition of text-emphasis in ::marker assert_true: text-emphasis doesn't seem to be supported in the computed style expected true got false
-FAIL Transition of text-emphasis-color in ::marker assert_true: text-emphasis-color doesn't seem to be supported in the computed style expected true got false
-FAIL Transition of text-emphasis-position in ::marker assert_true: text-emphasis-position doesn't seem to be supported in the computed style expected true got false
-FAIL Transition of text-emphasis-style in ::marker assert_true: text-emphasis-style doesn't seem to be supported in the computed style expected true got false
+FAIL Transition of text-emphasis in ::marker assert_equals: expected "triangle rgb(50, 100, 100)" but got ""
+FAIL Transition of text-emphasis-color in ::marker assert_equals: expected "rgb(50, 100, 100)" but got "rgb(100, 0, 200)"
+PASS Transition of text-emphasis-position in ::marker
+PASS Transition of text-emphasis-style in ::marker
 PASS Transition of text-shadow in ::marker
 PASS Transition of display in ::marker
 PASS Transition of position in ::marker
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties-in-animation.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties-in-animation.html
index 349c56d..d2b9961 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties-in-animation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties-in-animation.html
@@ -261,9 +261,9 @@
   },
   {
     property: "text-emphasis-style",
-    from: "dot",
-    to: "triangle",
-    midPoint: "triangle",
+    from: "filled dot",
+    to: "filled triangle",
+    midPoint: "filled triangle",
   },
   {
     property: "text-shadow",
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties.html
index ab03b98..ddcf98bf 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/marker-supported-properties.html
@@ -81,10 +81,10 @@
 
 // ::marker supports inherited text decoration properties.
 test_pseudo_computed_value("::marker", "text-decoration-skip-ink", "none");
-test_pseudo_computed_value("::marker", "text-emphasis", "dot rgb(0, 255, 0)");
+test_pseudo_computed_value("::marker", "text-emphasis", "filled dot rgb(0, 255, 0)");
 test_pseudo_computed_value("::marker", "text-emphasis-color", "rgb(0, 255, 0)");
 test_pseudo_computed_value("::marker", "text-emphasis-position", "under left");
-test_pseudo_computed_value("::marker", "text-emphasis-style", "dot");
+test_pseudo_computed_value("::marker", "text-emphasis-style", "filled dot");
 test_pseudo_computed_value("::marker", "text-shadow", "rgb(0, 255, 0) 1px 2px 3px");
 
 // ::marker does NOT support layout properties
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001-ref.html
new file mode 100644
index 0000000..b15f74c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Pseudo-Elements Test: Reference ::target-text text-decoration-color</title>
+<style>
+  div {
+    text-decoration: solid underline magenta;
+  }
+</style>
+<p>The test passes if the following line has a magenta underline.</p>
+<div>Example</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001.html
new file mode 100644
index 0000000..1709ce70
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Pseudo-Elements Test: ::target-text text-decoration</title>
+<link rel="help" href="https://drafts.csswg.org/css-pseudo/#selectordef-target-text">
+<link rel="match" href="target-text-text-decoration-001-ref.html">
+<meta name="assert" content="This test checks that ::target-text pseudo-element paints text-decorations defined by the pseudo-element.">
+<style>
+  ::target-text {
+    text-decoration: solid underline magenta;
+  }
+</style>
+<p>The test passes if the following line has a magenta underline.</p>
+<div>Example</div>
+<script>
+  location.href = "#:~:text=Example";
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/inheritance-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-text-decor/inheritance-expected.txt
deleted file mode 100644
index 1f27a3e..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-text-decor/inheritance-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-This is a testharness.js-based test.
-PASS Property text-decoration-color has initial value rgba(2, 3, 4, 0.5)
-PASS Property text-decoration-color does not inherit
-PASS Property text-decoration-line has initial value none
-PASS Property text-decoration-line does not inherit
-PASS Property text-decoration-style has initial value solid
-PASS Property text-decoration-style does not inherit
-FAIL Property text-emphasis-color has initial value rgba(2, 3, 4, 0.5) assert_true: text-emphasis-color doesn't seem to be supported in the computed style expected true got false
-FAIL Property text-emphasis-color inherits assert_true: text-emphasis-color doesn't seem to be supported in the computed style expected true got false
-FAIL Property text-emphasis-position has initial value over right assert_true: text-emphasis-position doesn't seem to be supported in the computed style expected true got false
-FAIL Property text-emphasis-position inherits assert_true: text-emphasis-position doesn't seem to be supported in the computed style expected true got false
-FAIL Property text-emphasis-style has initial value none assert_true: text-emphasis-style doesn't seem to be supported in the computed style expected true got false
-FAIL Property text-emphasis-style inherits assert_true: text-emphasis-style doesn't seem to be supported in the computed style expected true got false
-PASS Property text-shadow has initial value none
-PASS Property text-shadow inherits
-PASS Property text-underline-position has initial value auto
-PASS Property text-underline-position inherits
-PASS Property text-decoration-skip-ink has initial value auto
-PASS Property text-decoration-skip-ink inherits
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/inheritance.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/inheritance.html
index 9ee65b4..b106343 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text-decor/inheritance.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/inheritance.html
@@ -26,7 +26,7 @@
 assert_not_inherited('text-decoration-style', 'solid', 'dashed');
 assert_inherited('text-emphasis-color', 'rgba(2, 3, 4, 0.5)', 'rgba(42, 53, 64, 0.75)');
 assert_inherited('text-emphasis-position', 'over right', 'under left');
-assert_inherited('text-emphasis-style', 'none', 'triangle');
+assert_inherited('text-emphasis-style', 'none', 'filled triangle');
 assert_inherited('text-shadow', 'none', 'rgba(42, 53, 64, 0.75) 10px 20px 0px');
 assert_inherited('text-underline-position', 'auto', 'under');
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-emphasis-position-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-emphasis-position-computed-expected.txt
new file mode 100644
index 0000000..c242701
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-emphasis-position-computed-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+FAIL Property text-emphasis-position value 'over' assert_equals: expected "over" but got "over right"
+FAIL Property text-emphasis-position value 'under' assert_equals: expected "under" but got "under right"
+PASS Property text-emphasis-position value 'over right'
+PASS Property text-emphasis-position value 'over left'
+PASS Property text-emphasis-position value 'under right'
+PASS Property text-emphasis-position value 'under left'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-emphasis-position-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-emphasis-position-computed.html
new file mode 100644
index 0000000..f288fdf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-emphasis-position-computed.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<div id="target"></div>
+<script>
+// Computed value: specified keyword(s)
+test_computed_value('text-emphasis-position', 'over');
+test_computed_value('text-emphasis-position', 'under');
+test_computed_value('text-emphasis-position', 'over right');
+test_computed_value('text-emphasis-position', 'over left');
+test_computed_value('text-emphasis-position', 'under right');
+test_computed_value('text-emphasis-position', 'under left');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-emphasis-style-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-emphasis-style-computed.html
new file mode 100644
index 0000000..5aa84ab0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-emphasis-style-computed.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<div id="target"></div>
+<script>
+// Computed value: the keyword none, a pair of keywords representing the shape
+// and fill, or a string
+test_computed_value('text-emphasis-style', 'none');
+test_computed_value('text-emphasis-style', 'dot', 'filled dot');
+test_computed_value('text-emphasis-style', 'filled circle');
+test_computed_value('text-emphasis-style', 'double-circle', 'filled double-circle');
+test_computed_value('text-emphasis-style', 'triangle', 'filled triangle');
+test_computed_value('text-emphasis-style', 'open sesame');
+test_computed_value('text-emphasis-style', '"*"');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/3d-scene-with-iframe-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/3d-scene-with-iframe-001-ref.html
new file mode 100644
index 0000000..d984f8a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/3d-scene-with-iframe-001-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+
+<style>
+
+#container {
+    perspective: 400px;
+    perspective-origin: 0 0;
+}
+#ref {
+    background-color: green;
+    width: 150px;
+    height: 100px;
+    transform: translateZ(200px);
+}
+
+</style>
+
+<div id="container">
+    <div id="ref"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/3d-scene-with-iframe-001.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/3d-scene-with-iframe-001.html
new file mode 100644
index 0000000..075e4ad
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/3d-scene-with-iframe-001.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="UTF-8">
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1264783">
+<link rel="help" href="https://www.w3.org/TR/css-transforms-2/#3d-rendering-contexts">
+<link rel="match" href="3d-scene-with-iframe-001-ref.html">
+<meta name="assert" content="The iframe should be covered by the green div.">
+
+<style>
+
+#container {
+    perspective: 400px;
+    perspective-origin: 0 0;
+}
+#scene {
+    transform-style: preserve-3d;
+    transform: translate(100px, 100px);
+    width: 300px;
+    height: 300px;
+}
+.transform1, .transform2 {
+    position: absolute;
+    transform-style: preserve-3d;
+    top: 0;
+    left: 0;
+}
+.transform1 {
+    transform: translateZ(200px);
+}
+.transform1 > div {
+    background-color: green;
+    width: 150px;
+    height: 100px;
+    transform: translate(-100px, -100px)
+}
+.transform2 {
+    transform: translateZ(100px);
+}
+.transform2 > iframe {
+    display: block;
+    transform: translate(-50px, -75px);
+}
+
+</style>
+
+<div id="container">
+    <div id="scene">
+        <div class="transform1">
+            <div></div>
+        </div>
+        <div class="transform2">
+            <iframe width="150" height="100"></iframe>
+        </div>
+    </div>
+</div>
+
+<script>
+
+let iframe = document.getElementsByTagName("iframe")[0];
+iframe.addEventListener("load", function() {
+    let root = iframe.contentDocument.documentElement;
+    root.style.height = "10000px";
+    root.style.background = "red";
+    document.documentElement.classList.remove("reftest-wait");
+});
+iframe.src = "/resources/blank.html";
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/text-emphasis-color-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/text-emphasis-color-expected.txt
index a101fe8f..772c71bb 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/text-emphasis-color-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/text-emphasis-color-expected.txt
@@ -1,7 +1,7 @@
 This is a testharness.js-based test.
-FAIL Can set 'text-emphasis-color' to CSS-wide keywords Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: text-emphasis-color
-FAIL Can set 'text-emphasis-color' to var() references Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: text-emphasis-color
-FAIL Can set 'text-emphasis-color' to the 'currentcolor' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: text-emphasis-color
+PASS Can set 'text-emphasis-color' to CSS-wide keywords
+PASS Can set 'text-emphasis-color' to var() references
+FAIL Can set 'text-emphasis-color' to the 'currentcolor' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property
 PASS Setting 'text-emphasis-color' to a length throws TypeError
 PASS Setting 'text-emphasis-color' to a percent throws TypeError
 PASS Setting 'text-emphasis-color' to a time throws TypeError
@@ -11,10 +11,10 @@
 PASS Setting 'text-emphasis-color' to a position throws TypeError
 PASS Setting 'text-emphasis-color' to a URL throws TypeError
 PASS Setting 'text-emphasis-color' to a transform throws TypeError
-FAIL 'text-emphasis-color' does not supported 'red' Failed to execute 'get' on 'StylePropertyMapReadOnly': Invalid propertyName: text-emphasis-color
-FAIL 'text-emphasis-color' does not supported '#bbff00' Failed to execute 'get' on 'StylePropertyMapReadOnly': Invalid propertyName: text-emphasis-color
-FAIL 'text-emphasis-color' does not supported 'rgb(255, 255, 128)' Failed to execute 'get' on 'StylePropertyMapReadOnly': Invalid propertyName: text-emphasis-color
-FAIL 'text-emphasis-color' does not supported 'hsl(50, 33%, 25%)' Failed to execute 'get' on 'StylePropertyMapReadOnly': Invalid propertyName: text-emphasis-color
-FAIL 'text-emphasis-color' does not supported 'transparent' Failed to execute 'get' on 'StylePropertyMapReadOnly': Invalid propertyName: text-emphasis-color
+PASS 'text-emphasis-color' does not supported 'red'
+PASS 'text-emphasis-color' does not supported '#bbff00'
+PASS 'text-emphasis-color' does not supported 'rgb(255, 255, 128)'
+PASS 'text-emphasis-color' does not supported 'hsl(50, 33%, 25%)'
+PASS 'text-emphasis-color' does not supported 'transparent'
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/padding-percent-orthogonal-dynamic-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/padding-percent-orthogonal-dynamic-expected.txt
new file mode 100644
index 0000000..f96daef
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/padding-percent-orthogonal-dynamic-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+PASS 400px minus 100px padding based on #child height
+FAIL 200px minus 100px padding based on #child height assert_equals: expected 100 but got 300
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/padding-percent-orthogonal-dynamic.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/padding-percent-orthogonal-dynamic.html
new file mode 100644
index 0000000..402991b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/padding-percent-orthogonal-dynamic.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<title>CSS Writing Modes Test: Re-layout of orthogonal layouts with percentage padding</title>
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes/#dimension-mapping">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  #vertical {
+    width: 400px;
+    writing-mode: vertical-lr;
+  }
+
+  #padded {
+    width: 100%;
+    box-sizing: border-box;
+    padding-right: 100%;
+  }
+
+  #horizontal {
+    writing-mode: horizontal-tb;
+    width: 100%;
+  }
+
+  #container {
+    width: 100%;
+    background-color: green;
+  }
+
+  #child { height: 100px; }
+</style>
+<div id="vertical">
+  <div id="padded">
+    <div id="horizontal">
+      <div id="container">
+        <div id="child"></div>
+      </div>
+    </div>
+  </div>
+</div>
+<script>
+  test(() => assert_equals(container.offsetWidth, 300),
+       "400px minus 100px padding based on #child height");
+
+  vertical.style.width = "200px";
+
+  test(() => assert_equals(container.offsetWidth, 100),
+       "200px minus 100px padding based on #child height");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-002-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-002-expected.txt
index d9a60f3..b508e5ab 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-002-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-002-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 260 tests; 248 PASS, 12 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 273 tests; 255 PASS, 18 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Setup
 PASS isolation (type: discrete) has testAccumulation function
 PASS isolation: "isolate" onto "auto"
@@ -186,6 +186,19 @@
 PASS text-decoration-style (type: discrete) has testAccumulation function
 PASS text-decoration-style: "dotted" onto "solid"
 PASS text-decoration-style: "solid" onto "dotted"
+PASS text-emphasis-color (type: color) has testAccumulation function
+FAIL text-emphasis-color supports animating as color of rgb() with overflowed  from and to values assert_equals: The value should be rgb(255, 128, 128) at 0ms expected "rgb(255, 128, 128)" but got "rgb(255, 0, 0)"
+FAIL text-emphasis-color supports animating as color of #RGB assert_equals: The value should be rgb(255, 128, 128) at 0ms expected "rgb(255, 128, 128)" but got "rgb(255, 0, 0)"
+FAIL text-emphasis-color supports animating as color of hsl() assert_equals: The value should be rgb(255, 128, 128) at 0ms expected "rgb(255, 128, 128)" but got "rgb(255, 0, 0)"
+FAIL text-emphasis-color supports animating as color of #RGBa assert_equals: The value should be rgb(230, 128, 128) at 0ms expected "rgb(230, 128, 128)" but got "rgba(255, 0, 0, 0.4)"
+FAIL text-emphasis-color supports animating as color of rgba() assert_equals: The value should be rgb(230, 128, 128) at 0ms expected "rgb(230, 128, 128)" but got "rgba(255, 0, 0, 0.4)"
+FAIL text-emphasis-color supports animating as color of hsla() assert_equals: The value should be rgb(230, 128, 128) at 0ms expected "rgb(230, 128, 128)" but got "rgba(255, 0, 0, 0.4)"
+PASS text-emphasis-position (type: discrete) has testAccumulation function
+PASS text-emphasis-position: "under left" onto "over right"
+PASS text-emphasis-position: "over right" onto "under left"
+PASS text-emphasis-style (type: discrete) has testAccumulation function
+PASS text-emphasis-style: "open dot" onto "filled circle"
+PASS text-emphasis-style: "filled circle" onto "open dot"
 PASS text-overflow (type: discrete) has testAccumulation function
 PASS text-overflow: "ellipsis" onto "clip"
 PASS text-overflow: "clip" onto "ellipsis"
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-002-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-002-expected.txt
index 09a6e1c..2047aeaa 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-002-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-002-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 256 tests; 247 PASS, 9 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 269 tests; 254 PASS, 15 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Setup
 PASS isolation (type: discrete) has testAddition function
 PASS isolation: "isolate" onto "auto"
@@ -186,6 +186,19 @@
 PASS text-decoration-style (type: discrete) has testAddition function
 PASS text-decoration-style: "dotted" onto "solid"
 PASS text-decoration-style: "solid" onto "dotted"
+PASS text-emphasis-color (type: color) has testAddition function
+FAIL text-emphasis-color supports animating as color of rgb() with overflowed  from and to values assert_equals: The value should be rgb(255, 128, 128) at 0ms expected "rgb(255, 128, 128)" but got "rgb(255, 0, 0)"
+FAIL text-emphasis-color supports animating as color of #RGB assert_equals: The value should be rgb(255, 128, 128) at 0ms expected "rgb(255, 128, 128)" but got "rgb(255, 0, 0)"
+FAIL text-emphasis-color supports animating as color of hsl() assert_equals: The value should be rgb(255, 128, 128) at 0ms expected "rgb(255, 128, 128)" but got "rgb(255, 0, 0)"
+FAIL text-emphasis-color supports animating as color of #RGBa assert_equals: The value should be rgb(230, 128, 128) at 0ms expected "rgb(230, 128, 128)" but got "rgba(255, 0, 0, 0.4)"
+FAIL text-emphasis-color supports animating as color of rgba() assert_equals: The value should be rgb(230, 128, 128) at 0ms expected "rgb(230, 128, 128)" but got "rgba(255, 0, 0, 0.4)"
+FAIL text-emphasis-color supports animating as color of hsla() assert_equals: The value should be rgb(230, 128, 128) at 0ms expected "rgb(230, 128, 128)" but got "rgba(255, 0, 0, 0.4)"
+PASS text-emphasis-position (type: discrete) has testAddition function
+PASS text-emphasis-position: "under left" onto "over right"
+PASS text-emphasis-position: "over right" onto "under left"
+PASS text-emphasis-style (type: discrete) has testAddition function
+PASS text-emphasis-style: "open dot" onto "filled circle"
+PASS text-emphasis-style: "filled circle" onto "open dot"
 PASS text-overflow (type: discrete) has testAddition function
 PASS text-overflow: "ellipsis" onto "clip"
 PASS text-overflow: "clip" onto "ellipsis"
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-002-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-002-expected.txt
index ecf94a6..9e3c434 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-002-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-002-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 314 tests; 302 PASS, 12 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 329 tests; 311 PASS, 18 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Setup
 PASS isolation (type: discrete) has testInterpolation function
 PASS isolation uses discrete animation when animating between "auto" and "isolate" with linear easing
@@ -223,6 +223,21 @@
 PASS text-decoration-style uses discrete animation when animating between "solid" and "dotted" with linear easing
 PASS text-decoration-style uses discrete animation when animating between "solid" and "dotted" with effect easing
 PASS text-decoration-style uses discrete animation when animating between "solid" and "dotted" with keyframe easing
+PASS text-emphasis-color (type: color) has testInterpolation function
+FAIL text-emphasis-color supports animating as color of rgb() assert_equals: The value should be rgb(128, 0, 128) at 500ms expected "rgb(128, 0, 128)" but got "rgb(0, 0, 255)"
+FAIL text-emphasis-color supports animating as color of #RGB assert_equals: The value should be rgb(128, 0, 128) at 500ms expected "rgb(128, 0, 128)" but got "rgb(0, 0, 255)"
+FAIL text-emphasis-color supports animating as color of hsl() assert_equals: The value should be rgb(128, 0, 128) at 500ms expected "rgb(128, 0, 128)" but got "rgb(0, 0, 255)"
+FAIL text-emphasis-color supports animating as color of #RGBa assert_equals: The value should be rgba(85, 0, 170, 0.6) at 500ms expected "rgba(85, 0, 170, 0.6)" but got "rgba(0, 0, 255, 0.8)"
+FAIL text-emphasis-color supports animating as color of rgba() assert_equals: The value should be rgba(85, 0, 170, 0.6) at 500ms expected "rgba(85, 0, 170, 0.6)" but got "rgba(0, 0, 255, 0.8)"
+FAIL text-emphasis-color supports animating as color of hsla() assert_equals: The value should be rgba(85, 0, 170, 0.6) at 500ms expected "rgba(85, 0, 170, 0.6)" but got "rgba(0, 0, 255, 0.8)"
+PASS text-emphasis-position (type: discrete) has testInterpolation function
+PASS text-emphasis-position uses discrete animation when animating between "over right" and "under left" with linear easing
+PASS text-emphasis-position uses discrete animation when animating between "over right" and "under left" with effect easing
+PASS text-emphasis-position uses discrete animation when animating between "over right" and "under left" with keyframe easing
+PASS text-emphasis-style (type: discrete) has testInterpolation function
+PASS text-emphasis-style uses discrete animation when animating between "filled circle" and "open dot" with linear easing
+PASS text-emphasis-style uses discrete animation when animating between "filled circle" and "open dot" with effect easing
+PASS text-emphasis-style uses discrete animation when animating between "filled circle" and "open dot" with keyframe easing
 PASS text-overflow (type: discrete) has testInterpolation function
 PASS text-overflow uses discrete animation when animating between "clip" and "ellipsis" with linear easing
 PASS text-overflow uses discrete animation when animating between "clip" and "ellipsis" with effect easing
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js
index e9b7c52..60b0b595 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js
@@ -1290,7 +1290,7 @@
   'text-emphasis-style': {
     // http://dev.w3.org/csswg/css-text-decor-3/#propdef-text-emphasis-style
     types: [
-      { type: 'discrete', options: [ [ 'circle', 'open dot' ] ] }
+      { type: 'discrete', options: [ [ 'filled circle', 'open dot' ] ] }
     ]
   },
   'text-indent': {
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/log_entry_added/event_buffer.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/log_entry_added/event_buffer.py
deleted file mode 100644
index 8d0bbac..0000000
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/log_entry_added/event_buffer.py
+++ /dev/null
@@ -1,108 +0,0 @@
-import pytest
-import time
-
-from . import assert_base_entry, create_log
-
-@pytest.mark.asyncio
-@pytest.mark.parametrize("log_type", ["console_api_log", "javascript_error"])
-async def test_console_log_cached_messages(bidi_session,
-                                           current_session,
-                                           wait_for_event,
-                                           inline,
-                                           log_type):
-    # Unsubscribe in case previous tests subscribed to log.entryAdded
-    await bidi_session.session.unsubscribe(events=["log.entryAdded"])
-
-    # Refresh to make sure no events are cached for the current window global
-    # from previous tests.
-    current_session.refresh()
-
-    # Log a message before subscribing
-    expected_text = create_log(current_session, inline, log_type, "cached_message")
-
-    # Track all received log.entryAdded events in the events array
-    events = []
-    async def on_event(method, data):
-        events.append(data)
-    remove_listener = bidi_session.add_event_listener("log.entryAdded", on_event)
-
-    # Subscribe
-    on_entry_added = wait_for_event("log.entryAdded")
-    await bidi_session.session.subscribe(events=["log.entryAdded"])
-    await on_entry_added
-    assert len(events) == 1;
-
-    # Check the log.entryAdded event received has the expected properties.
-    assert_base_entry(events[0], text=expected_text)
-
-    # Unsubscribe and re-subscribe
-    await bidi_session.session.unsubscribe(events=["log.entryAdded"])
-    await bidi_session.session.subscribe(events=["log.entryAdded"])
-
-    # Wait for some time to catch all messages.
-    time.sleep(0.5)
-
-    # Check that the cached event was not re-emitted.
-    assert len(events) == 1;
-
-    on_entry_added = wait_for_event("log.entryAdded")
-    expected_text = create_log(current_session, inline, log_type, "live_message")
-    await on_entry_added
-
-    # Check that we only received the live message.
-    assert len(events) == 2;
-    assert_base_entry(events[1], text=expected_text)
-
-    # Unsubscribe, log a message and re-subscribe
-    await bidi_session.session.unsubscribe(events=["log.entryAdded"])
-    expected_text = create_log(current_session, inline, log_type, "cached_message_2")
-
-    on_entry_added = wait_for_event("log.entryAdded")
-    await bidi_session.session.subscribe(events=["log.entryAdded"])
-    await on_entry_added
-
-    # Check that only the newly cached event was emitted
-    assert len(events) == 3;
-    assert_base_entry(events[2], text=expected_text)
-
-    remove_listener()
-
-
-@pytest.mark.asyncio
-@pytest.mark.parametrize("log_type", ["console_api_log", "javascript_error"])
-async def test_console_log_cached_message_after_refresh(bidi_session,
-                                                        current_session,
-                                                        wait_for_event,
-                                                        inline,
-                                                        log_type):
-    # Unsubscribe in case previous tests subscribed to log.entryAdded
-    await bidi_session.session.unsubscribe(events=["log.entryAdded"])
-
-    # Refresh to make sure no events are cached for the current window global
-    # from previous tests.
-    current_session.refresh()
-
-    # Track all received log.entryAdded events in the events array
-    events = []
-    async def on_event(method, data):
-        events.append(data)
-    remove_listener = bidi_session.add_event_listener("log.entryAdded", on_event)
-
-    # Log a message, refresh, log another message and subscribe
-    create_log(current_session, inline, log_type, "missed_message")
-    current_session.refresh();
-    expected_text = create_log(current_session, inline, log_type, "cached_message")
-
-    on_entry_added = wait_for_event("log.entryAdded")
-    await bidi_session.session.subscribe(events=["log.entryAdded"])
-    await on_entry_added
-
-    # Wait for some time to catch all messages.
-    time.sleep(0.5)
-
-    # Check that only the cached message was retrieved.
-    assert len(events) == 1;
-    assert_base_entry(events[0], text=expected_text)
-
-    remove_listener()
-
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/log_entry_added/stacktrace.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/log_entry_added/stacktrace.py
deleted file mode 100644
index c1aec53b..0000000
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/log_entry_added/stacktrace.py
+++ /dev/null
@@ -1,106 +0,0 @@
-import pytest
-
-from . import assert_console_entry, assert_javascript_entry
-
-
-@pytest.mark.asyncio
-@pytest.mark.parametrize("log_method, expect_stack", [
-    ("assert", True),
-    ("debug", False),
-    ("error", True),
-    ("info", False),
-    ("log", False),
-    ("table", False),
-    ("trace", True),
-    ("warn", True),
-])
-async def test_console_entry_sync_callstack(bidi_session,
-                                            current_session,
-                                            inline,
-                                            wait_for_event,
-                                            log_method,
-                                            expect_stack):
-    if log_method == 'assert':
-        # assert has to be called with a first falsy argument to trigger a log.
-        url = inline(f"""
-            <script>
-                function foo() {{ console.{log_method}(false, "cheese"); }}
-                function bar() {{ foo(); }}
-                bar();
-            </script>
-            """)
-    else:
-        url = inline(f"""
-            <script>
-                function foo() {{ console.{log_method}("cheese"); }}
-                function bar() {{ foo(); }}
-                bar();
-            </script>
-            """)
-
-    await bidi_session.session.subscribe(events=["log.entryAdded"])
-
-    on_entry_added = wait_for_event("log.entryAdded")
-
-    if (expect_stack):
-        expected_stack = [
-            {"columnNumber": 42, "functionName": "foo", "lineNumber": 4, "url": url},
-            {"columnNumber": 34, "functionName": "bar", "lineNumber": 5, "url": url},
-            {"columnNumber": 17, "functionName": "", "lineNumber": 6, "url": url},
-        ]
-    else:
-        expected_stack = None
-
-    current_session.url = url
-
-    event_data = await on_entry_added
-
-    assert_console_entry(
-        event_data,
-        method=log_method,
-        text="cheese",
-        stacktrace=expected_stack,
-    )
-
-    # Navigate to a page with no error to avoid polluting the next tests with
-    # JavaScript errors.
-    current_session.url = inline("<p>foo")
-
-
-@pytest.mark.asyncio
-async def test_javascript_entry_sync_callstack(bidi_session,
-                                               current_session,
-                                               inline,
-                                               wait_for_event):
-    url = inline("""
-        <script>
-            function foo() { throw new Error("cheese"); }
-            function bar() { foo(); }
-            bar();
-        </script>
-        """)
-
-    await bidi_session.session.subscribe(events=["log.entryAdded"])
-
-    on_entry_added = wait_for_event("log.entryAdded")
-
-    expected_stack = [
-        {"columnNumber": 36, "functionName": "foo", "lineNumber": 4, "url": url},
-        {"columnNumber": 30, "functionName": "bar", "lineNumber": 5, "url": url},
-        {"columnNumber": 13, "functionName": "", "lineNumber": 6, "url": url},
-    ]
-
-    current_session.url = url
-
-    event_data = await on_entry_added
-
-    assert_javascript_entry(
-        event_data,
-        level="error",
-        text="Error: cheese",
-        stacktrace=expected_stack,
-    )
-
-    # Navigate to a page with no error to avoid polluting the next tests with
-    # JavaScript errors.
-    current_session.url = inline("<p>foo")
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/new_session/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/new_session/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/new_session/__init__.py
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session_status/status.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session_status/status.py
deleted file mode 100644
index eee102f..0000000
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session_status/status.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import pytest
-
-
-# Check that session.status can be used. The actual values for the "ready" and
-# "message" properties are implementation specific.
-@pytest.mark.asyncio
-async def test_bidi_session_status(bidi_session, send_blocking_command):
-    response = await send_blocking_command("session.status", {})
-    assert isinstance(response["ready"], bool)
-    assert isinstance(response["message"], str)
-
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session_subscribe/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session_subscribe/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session_subscribe/__init__.py
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session_unsubscribe/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session_unsubscribe/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session_unsubscribe/__init__.py
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webusb/usbDevice_reset-manual.https.html b/third_party/blink/web_tests/external/wpt/webusb/usbDevice_reset-manual.https.html
new file mode 100644
index 0000000..63a0c35
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webusb/usbDevice_reset-manual.https.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title></title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/manual.js"></script>
+  </head>
+  <body>
+    <p>
+      These tests require a USB device to be connected.
+    </p>
+    <script>
+      const kGetDescriptorRequest = 0x06;
+      const kDeviceDescriptorType = 0x01;
+      const kDeviceDescriptorLength = 18;
+
+      manual_usb_test(async (t, device) => {
+        await device.open();
+        t.add_cleanup(async () => {
+          await device.close();
+        });
+
+        // This test exercises the behavior that the device remains open when it
+        // is reset. If the device changes its properties too drastically when
+        // reset it may appear to disconnect instead.
+        await device.reset();
+
+        // Read the device descriptor in order to validate that communication
+        // with the device is still possible after a reset.
+        const result = await device.controlTransferIn({
+          requestType: 'standard',
+          recipient: 'device',
+          request: kGetDescriptorRequest,
+          value: kDeviceDescriptorType << 8,
+          index: 0,
+        }, kDeviceDescriptorLength);
+
+        assert_equals(result.status, 'ok', 'transfer status');
+        assert_equals(
+            result.data.byteLength, kDeviceDescriptorLength, 'transfer length');
+      }, 'reset() does not disconnect the device');
+    </script>
+  </body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
index 30e8911d..8f27e66 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
@@ -38,9 +38,6 @@
 -webkit-rtl-ordering: logical
 -webkit-text-combine: none
 -webkit-text-decorations-in-effect: none
--webkit-text-emphasis-color: rgb(0, 0, 0)
--webkit-text-emphasis-position: over right
--webkit-text-emphasis-style: none
 -webkit-text-fill-color: rgb(0, 0, 0)
 -webkit-text-orientation: vertical-right
 -webkit-text-security: none
@@ -324,6 +321,9 @@
 text-decoration-line: none
 text-decoration-skip-ink: auto
 text-decoration-style: solid
+text-emphasis-color: rgb(0, 0, 0)
+text-emphasis-position: over right
+text-emphasis-style: none
 text-indent: 0px
 text-justify: auto
 text-overflow: clip
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
index 15c9cc7..9a0a14a1 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
@@ -38,9 +38,6 @@
 -webkit-rtl-ordering: logical
 -webkit-text-combine: none
 -webkit-text-decorations-in-effect: none
--webkit-text-emphasis-color: rgb(0, 0, 0)
--webkit-text-emphasis-position: over right
--webkit-text-emphasis-style: none
 -webkit-text-fill-color: rgb(0, 0, 0)
 -webkit-text-orientation: vertical-right
 -webkit-text-security: none
@@ -324,6 +321,9 @@
 text-decoration-line: none
 text-decoration-skip-ink: auto
 text-decoration-style: solid
+text-emphasis-color: rgb(0, 0, 0)
+text-emphasis-position: over right
+text-emphasis-style: none
 text-indent: 0px
 text-justify: auto
 text-overflow: clip
diff --git a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
index 3503a31..5449a26 100644
--- a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
+++ b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
@@ -38,9 +38,6 @@
 -webkit-rtl-ordering: logical
 -webkit-text-combine: none
 -webkit-text-decorations-in-effect: none
--webkit-text-emphasis-color: rgb(0, 0, 0)
--webkit-text-emphasis-position: over right
--webkit-text-emphasis-style: none
 -webkit-text-fill-color: rgb(0, 0, 0)
 -webkit-text-orientation: vertical-right
 -webkit-text-security: none
@@ -324,6 +321,9 @@
 text-decoration-line: none
 text-decoration-skip-ink: auto
 text-decoration-style: solid
+text-emphasis-color: rgb(0, 0, 0)
+text-emphasis-position: over right
+text-emphasis-style: none
 text-indent: 0px
 text-justify: auto
 text-overflow: clip
diff --git a/third_party/blink/web_tests/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/resize-lock-expected.txt b/third_party/blink/web_tests/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/resize-lock-expected.txt
new file mode 100644
index 0000000..34398b2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/resize-lock-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Test Resize Lock assert_equals: fenced frame dimensions should not be updated by parent page expected "300x150" but got "444x444"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/README.md b/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/README.md
new file mode 100644
index 0000000..5585380
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/README.md
@@ -0,0 +1,8 @@
+# Fenced Frames + ShadowDOM
+
+This directory contains `wpt_internal/fenced_frame/` test expectations that are
+specific to the ShadowDOM implementation of fenced frames. The tests are run with
+the flag --enable-features=FencedFrames:implementation\_type/shadow\_dom.
+
+See crbug.com/1123606.
+
diff --git a/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/fenced_frame/basic-expected.html b/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/fenced_frame/basic-expected.html
new file mode 100644
index 0000000..1df34f7b
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/fenced_frame/basic-expected.html
@@ -0,0 +1,5 @@
+<body>
+  <iframe
+      style="width: 100%; height: 300px; box-sizing: border-box"
+      srcdoc="<body style='margin: 0px'><div style='background: red; width: 20px; height: 20px'></div></body>"></iframe>
+</body>
diff --git a/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/fenced_frame/visibility-changed-expected.html b/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/fenced_frame/visibility-changed-expected.html
new file mode 100644
index 0000000..90f5cff
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/fenced_frame/visibility-changed-expected.html
@@ -0,0 +1,5 @@
+<body>
+  <iframe
+      style="width: 100%; height: 300px; box-sizing: border-box"
+      srcdoc="<body style='margin: 0px'><div style='background: green; width: 20px; height: 20px'></div></body>"></iframe>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/navigate-by-name-expected.txt b/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/navigate-by-name-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/fenced_frame/navigate-by-name-expected.txt
rename to third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/navigate-by-name-expected.txt
diff --git a/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/prerender.https-expected.txt b/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/prerender.https-expected.txt
new file mode 100644
index 0000000..41b28d9
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/prerender.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+PASS Fenced Frame must not load prerendered page.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
index 6f9a8083..296b968 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
@@ -98,7 +98,9 @@
 PASS oldChildWindow.onchange is newChildWindow.onchange
 PASS oldChildWindow.onclick is newChildWindow.onclick
 PASS oldChildWindow.onclose is newChildWindow.onclose
+PASS oldChildWindow.oncontextlost is newChildWindow.oncontextlost
 PASS oldChildWindow.oncontextmenu is newChildWindow.oncontextmenu
+PASS oldChildWindow.oncontextrestored is newChildWindow.oncontextrestored
 PASS oldChildWindow.oncuechange is newChildWindow.oncuechange
 PASS oldChildWindow.ondblclick is newChildWindow.ondblclick
 PASS oldChildWindow.ondevicemotion is newChildWindow.ondevicemotion
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
index 8ca358f..5b5da5e 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
@@ -52,7 +52,9 @@
 PASS childWindow.onchange is null
 PASS childWindow.onclick is null
 PASS childWindow.onclose is null
+PASS childWindow.oncontextlost is null
 PASS childWindow.oncontextmenu is null
+PASS childWindow.oncontextrestored is null
 PASS childWindow.oncuechange is null
 PASS childWindow.ondblclick is null
 PASS childWindow.ondevicemotion is null
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
index 704abb1d..158521f3 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
@@ -52,7 +52,9 @@
 PASS childWindow.onchange is null
 PASS childWindow.onclick is null
 PASS childWindow.onclose is null
+PASS childWindow.oncontextlost is null
 PASS childWindow.oncontextmenu is null
+PASS childWindow.oncontextrestored is null
 PASS childWindow.oncuechange is null
 PASS childWindow.ondblclick is null
 PASS childWindow.ondevicemotion is null
diff --git a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 4948853..c1e359c9 100644
--- a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -111,6 +111,9 @@
     getter topOrigin
     method constructor
     method respondWith
+interface CanvasFilter
+    attribute @@toStringTag
+    method constructor
 interface CanvasGradient
     attribute @@toStringTag
     method addColorStop
@@ -852,12 +855,16 @@
 interface OffscreenCanvas : EventTarget
     attribute @@toStringTag
     getter height
+    getter oncontextlost
+    getter oncontextrestored
     getter width
     method constructor
     method convertToBlob
     method getContext
     method transferToImageBitmap
     setter height
+    setter oncontextlost
+    setter oncontextrestored
     setter width
 interface OffscreenCanvasRenderingContext2D
     attribute @@toStringTag
@@ -866,10 +873,14 @@
     getter fillStyle
     getter filter
     getter font
+    getter fontKerning
+    getter fontStretch
+    getter fontVariantCaps
     getter globalAlpha
     getter globalCompositeOperation
     getter imageSmoothingEnabled
     getter imageSmoothingQuality
+    getter letterSpacing
     getter lineCap
     getter lineDashOffset
     getter lineJoin
@@ -882,6 +893,8 @@
     getter strokeStyle
     getter textAlign
     getter textBaseline
+    getter textRendering
+    getter wordSpacing
     method arc
     method arcTo
     method beginPath
@@ -890,6 +903,7 @@
     method clip
     method closePath
     method constructor
+    method createConicGradient
     method createImageData
     method createLinearGradient
     method createPattern
@@ -902,6 +916,7 @@
     method getImageData
     method getLineDash
     method getTransform
+    method isContextLost
     method isPointInPath
     method isPointInStroke
     method lineTo
@@ -910,9 +925,11 @@
     method putImageData
     method quadraticCurveTo
     method rect
+    method reset
     method resetTransform
     method restore
     method rotate
+    method roundRect
     method save
     method scale
     method setLineDash
@@ -926,10 +943,14 @@
     setter fillStyle
     setter filter
     setter font
+    setter fontKerning
+    setter fontStretch
+    setter fontVariantCaps
     setter globalAlpha
     setter globalCompositeOperation
     setter imageSmoothingEnabled
     setter imageSmoothingQuality
+    setter letterSpacing
     setter lineCap
     setter lineDashOffset
     setter lineJoin
@@ -942,6 +963,8 @@
     setter strokeStyle
     setter textAlign
     setter textBaseline
+    setter textRendering
+    setter wordSpacing
 interface Path2D
     attribute @@toStringTag
     method addPath
@@ -955,6 +978,7 @@
     method moveTo
     method quadraticCurveTo
     method rect
+    method roundRect
 interface PaymentInstruments
     attribute @@toStringTag
     method clear
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
index 8d7beaaa2..3d05c96 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
@@ -392,6 +392,10 @@
 textDecorationSkipInk
 textDecorationStyle
 textDecorationThickness
+textEmphasis
+textEmphasisColor
+textEmphasisPosition
+textEmphasisStyle
 textIndent
 textOrientation
 textOverflow
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
index 05f5d53f..9cb505c 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
@@ -41,9 +41,6 @@
     -webkit-tap-highlight-color
     -webkit-text-combine
     -webkit-text-decorations-in-effect
-    -webkit-text-emphasis-color
-    -webkit-text-emphasis-position
-    -webkit-text-emphasis-style
     -webkit-text-fill-color
     -webkit-text-orientation
     -webkit-text-security
@@ -343,6 +340,9 @@
     text-decoration-skip-ink
     text-decoration-style
     text-decoration-thickness
+    text-emphasis-color
+    text-emphasis-position
+    text-emphasis-style
     text-indent
     text-orientation
     text-overflow
@@ -407,9 +407,6 @@
     -webkit-mask-repeat
         -webkit-mask-repeat-x
         -webkit-mask-repeat-y
-    -webkit-text-emphasis
-        -webkit-text-emphasis-color
-        -webkit-text-emphasis-style
     -webkit-text-stroke
         -webkit-text-stroke-color
         -webkit-text-stroke-width
@@ -716,6 +713,9 @@
         text-decoration-line
         text-decoration-style
         text-decoration-thickness
+    text-emphasis
+        text-emphasis-color
+        text-emphasis-style
     transition
         transition-delay
         transition-duration
@@ -728,11 +728,11 @@
     -epub-text-combine
         -webkit-text-combine
     -epub-text-emphasis
-        -webkit-text-emphasis
+        text-emphasis
     -epub-text-emphasis-color
-        -webkit-text-emphasis-color
+        text-emphasis-color
     -epub-text-emphasis-style
-        -webkit-text-emphasis-style
+        text-emphasis-style
     -epub-text-orientation
         -webkit-text-orientation
     -epub-text-transform
@@ -905,6 +905,14 @@
         shape-margin
     -webkit-shape-outside
         shape-outside
+    -webkit-text-emphasis
+        text-emphasis
+    -webkit-text-emphasis-color
+        text-emphasis-color
+    -webkit-text-emphasis-position
+        text-emphasis-position
+    -webkit-text-emphasis-style
+        text-emphasis-style
     -webkit-text-size-adjust
         text-size-adjust
     -webkit-transform
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
index bb64a4d1..b4f8119 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
@@ -170,7 +170,9 @@
     property onchange
     property onclick
     property onclose
+    property oncontextlost
     property oncontextmenu
+    property oncontextrestored
     property oncopy
     property oncuechange
     property oncut
@@ -1324,7 +1326,9 @@
     property onchange
     property onclick
     property onclose
+    property oncontextlost
     property oncontextmenu
+    property oncontextrestored
     property oncopy
     property oncuechange
     property oncut
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt
index 67ff614..80edce1 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt
@@ -32,6 +32,7 @@
 gyroscope
 hid
 idle-detection
+keyboard-map
 magnetometer
 microphone
 midi
@@ -41,6 +42,7 @@
 publickey-credentials-get
 screen-wake-lock
 serial
+shared-autofill
 sync-xhr
 usb
 xr-spatial-tracking
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
index afc3f4f3..ebdc3131 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -131,6 +131,9 @@
 [Worker]     method keys
 [Worker]     method match
 [Worker]     method open
+[Worker] interface CanvasFilter
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
 [Worker] interface CanvasGradient
 [Worker]     attribute @@toStringTag
 [Worker]     method addColorStop
@@ -871,12 +874,16 @@
 [Worker] interface OffscreenCanvas : EventTarget
 [Worker]     attribute @@toStringTag
 [Worker]     getter height
+[Worker]     getter oncontextlost
+[Worker]     getter oncontextrestored
 [Worker]     getter width
 [Worker]     method constructor
 [Worker]     method convertToBlob
 [Worker]     method getContext
 [Worker]     method transferToImageBitmap
 [Worker]     setter height
+[Worker]     setter oncontextlost
+[Worker]     setter oncontextrestored
 [Worker]     setter width
 [Worker] interface OffscreenCanvasRenderingContext2D
 [Worker]     attribute @@toStringTag
@@ -885,10 +892,14 @@
 [Worker]     getter fillStyle
 [Worker]     getter filter
 [Worker]     getter font
+[Worker]     getter fontKerning
+[Worker]     getter fontStretch
+[Worker]     getter fontVariantCaps
 [Worker]     getter globalAlpha
 [Worker]     getter globalCompositeOperation
 [Worker]     getter imageSmoothingEnabled
 [Worker]     getter imageSmoothingQuality
+[Worker]     getter letterSpacing
 [Worker]     getter lineCap
 [Worker]     getter lineDashOffset
 [Worker]     getter lineJoin
@@ -901,6 +912,8 @@
 [Worker]     getter strokeStyle
 [Worker]     getter textAlign
 [Worker]     getter textBaseline
+[Worker]     getter textRendering
+[Worker]     getter wordSpacing
 [Worker]     method arc
 [Worker]     method arcTo
 [Worker]     method beginPath
@@ -909,6 +922,7 @@
 [Worker]     method clip
 [Worker]     method closePath
 [Worker]     method constructor
+[Worker]     method createConicGradient
 [Worker]     method createImageData
 [Worker]     method createLinearGradient
 [Worker]     method createPattern
@@ -921,6 +935,7 @@
 [Worker]     method getImageData
 [Worker]     method getLineDash
 [Worker]     method getTransform
+[Worker]     method isContextLost
 [Worker]     method isPointInPath
 [Worker]     method isPointInStroke
 [Worker]     method lineTo
@@ -929,9 +944,11 @@
 [Worker]     method putImageData
 [Worker]     method quadraticCurveTo
 [Worker]     method rect
+[Worker]     method reset
 [Worker]     method resetTransform
 [Worker]     method restore
 [Worker]     method rotate
+[Worker]     method roundRect
 [Worker]     method save
 [Worker]     method scale
 [Worker]     method setLineDash
@@ -945,10 +962,14 @@
 [Worker]     setter fillStyle
 [Worker]     setter filter
 [Worker]     setter font
+[Worker]     setter fontKerning
+[Worker]     setter fontStretch
+[Worker]     setter fontVariantCaps
 [Worker]     setter globalAlpha
 [Worker]     setter globalCompositeOperation
 [Worker]     setter imageSmoothingEnabled
 [Worker]     setter imageSmoothingQuality
+[Worker]     setter letterSpacing
 [Worker]     setter lineCap
 [Worker]     setter lineDashOffset
 [Worker]     setter lineJoin
@@ -961,6 +982,8 @@
 [Worker]     setter strokeStyle
 [Worker]     setter textAlign
 [Worker]     setter textBaseline
+[Worker]     setter textRendering
+[Worker]     setter wordSpacing
 [Worker] interface Path2D
 [Worker]     attribute @@toStringTag
 [Worker]     method addPath
@@ -974,6 +997,7 @@
 [Worker]     method moveTo
 [Worker]     method quadraticCurveTo
 [Worker]     method rect
+[Worker]     method roundRect
 [Worker] interface PaymentInstruments
 [Worker]     attribute @@toStringTag
 [Worker]     method clear
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 6d1cddc..ad75653 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -774,6 +774,9 @@
     getter canvas
     method constructor
     method requestFrame
+interface CanvasFilter
+    attribute @@toStringTag
+    method constructor
 interface CanvasGradient
     attribute @@toStringTag
     method addColorStop
@@ -789,10 +792,14 @@
     getter fillStyle
     getter filter
     getter font
+    getter fontKerning
+    getter fontStretch
+    getter fontVariantCaps
     getter globalAlpha
     getter globalCompositeOperation
     getter imageSmoothingEnabled
     getter imageSmoothingQuality
+    getter letterSpacing
     getter lineCap
     getter lineDashOffset
     getter lineJoin
@@ -805,6 +812,8 @@
     getter strokeStyle
     getter textAlign
     getter textBaseline
+    getter textRendering
+    getter wordSpacing
     method arc
     method arcTo
     method beginPath
@@ -813,6 +822,7 @@
     method clip
     method closePath
     method constructor
+    method createConicGradient
     method createImageData
     method createLinearGradient
     method createPattern
@@ -827,6 +837,7 @@
     method getImageData
     method getLineDash
     method getTransform
+    method isContextLost
     method isPointInPath
     method isPointInStroke
     method lineTo
@@ -835,9 +846,11 @@
     method putImageData
     method quadraticCurveTo
     method rect
+    method reset
     method resetTransform
     method restore
     method rotate
+    method roundRect
     method save
     method scale
     method setLineDash
@@ -851,10 +864,14 @@
     setter fillStyle
     setter filter
     setter font
+    setter fontKerning
+    setter fontStretch
+    setter fontVariantCaps
     setter globalAlpha
     setter globalCompositeOperation
     setter imageSmoothingEnabled
     setter imageSmoothingQuality
+    setter letterSpacing
     setter lineCap
     setter lineDashOffset
     setter lineJoin
@@ -867,6 +884,8 @@
     setter strokeStyle
     setter textAlign
     setter textBaseline
+    setter textRendering
+    setter wordSpacing
 interface ChannelMergerNode : AudioNode
     attribute @@toStringTag
     method constructor
@@ -1391,7 +1410,9 @@
     getter onchange
     getter onclick
     getter onclose
+    getter oncontextlost
     getter oncontextmenu
+    getter oncontextrestored
     getter oncopy
     getter oncuechange
     getter oncut
@@ -1590,7 +1611,9 @@
     setter onchange
     setter onclick
     setter onclose
+    setter oncontextlost
     setter oncontextmenu
+    setter oncontextrestored
     setter oncopy
     setter oncuechange
     setter oncut
@@ -2650,7 +2673,9 @@
     getter onchange
     getter onclick
     getter onclose
+    getter oncontextlost
     getter oncontextmenu
+    getter oncontextrestored
     getter oncopy
     getter oncuechange
     getter oncut
@@ -2769,7 +2794,9 @@
     setter onchange
     setter onclick
     setter onclose
+    setter oncontextlost
     setter oncontextmenu
+    setter oncontextrestored
     setter oncopy
     setter oncuechange
     setter oncut
@@ -4975,12 +5002,16 @@
 interface OffscreenCanvas : EventTarget
     attribute @@toStringTag
     getter height
+    getter oncontextlost
+    getter oncontextrestored
     getter width
     method constructor
     method convertToBlob
     method getContext
     method transferToImageBitmap
     setter height
+    setter oncontextlost
+    setter oncontextrestored
     setter width
 interface OffscreenCanvasRenderingContext2D
     attribute @@toStringTag
@@ -4989,10 +5020,14 @@
     getter fillStyle
     getter filter
     getter font
+    getter fontKerning
+    getter fontStretch
+    getter fontVariantCaps
     getter globalAlpha
     getter globalCompositeOperation
     getter imageSmoothingEnabled
     getter imageSmoothingQuality
+    getter letterSpacing
     getter lineCap
     getter lineDashOffset
     getter lineJoin
@@ -5005,6 +5040,8 @@
     getter strokeStyle
     getter textAlign
     getter textBaseline
+    getter textRendering
+    getter wordSpacing
     method arc
     method arcTo
     method beginPath
@@ -5013,6 +5050,7 @@
     method clip
     method closePath
     method constructor
+    method createConicGradient
     method createImageData
     method createLinearGradient
     method createPattern
@@ -5025,6 +5063,7 @@
     method getImageData
     method getLineDash
     method getTransform
+    method isContextLost
     method isPointInPath
     method isPointInStroke
     method lineTo
@@ -5033,9 +5072,11 @@
     method putImageData
     method quadraticCurveTo
     method rect
+    method reset
     method resetTransform
     method restore
     method rotate
+    method roundRect
     method save
     method scale
     method setLineDash
@@ -5049,10 +5090,14 @@
     setter fillStyle
     setter filter
     setter font
+    setter fontKerning
+    setter fontStretch
+    setter fontVariantCaps
     setter globalAlpha
     setter globalCompositeOperation
     setter imageSmoothingEnabled
     setter imageSmoothingQuality
+    setter letterSpacing
     setter lineCap
     setter lineDashOffset
     setter lineJoin
@@ -5065,6 +5110,8 @@
     setter strokeStyle
     setter textAlign
     setter textBaseline
+    setter textRendering
+    setter wordSpacing
 interface Option
     attribute @@toStringTag
     getter defaultSelected
@@ -5151,6 +5198,7 @@
     method moveTo
     method quadraticCurveTo
     method rect
+    method roundRect
 interface PaymentAddress
     attribute @@toStringTag
     getter addressLine
@@ -6169,7 +6217,9 @@
     getter onchange
     getter onclick
     getter onclose
+    getter oncontextlost
     getter oncontextmenu
+    getter oncontextrestored
     getter oncopy
     getter oncuechange
     getter oncut
@@ -6273,7 +6323,9 @@
     setter onchange
     setter onclick
     setter onclose
+    setter oncontextlost
     setter oncontextmenu
+    setter oncontextrestored
     setter oncopy
     setter oncuechange
     setter oncut
@@ -10102,7 +10154,9 @@
     getter onchange
     getter onclick
     getter onclose
+    getter oncontextlost
     getter oncontextmenu
+    getter oncontextrestored
     getter oncuechange
     getter ondblclick
     getter ondevicemotion
@@ -10310,7 +10364,9 @@
     setter onchange
     setter onclick
     setter onclose
+    setter oncontextlost
     setter oncontextmenu
+    setter oncontextrestored
     setter oncuechange
     setter ondblclick
     setter ondevicemotion
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
index 40607b6..f8597ee 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -96,6 +96,9 @@
 [Worker]     method keys
 [Worker]     method match
 [Worker]     method open
+[Worker] interface CanvasFilter
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
 [Worker] interface CanvasGradient
 [Worker]     attribute @@toStringTag
 [Worker]     method addColorStop
@@ -780,12 +783,16 @@
 [Worker] interface OffscreenCanvas : EventTarget
 [Worker]     attribute @@toStringTag
 [Worker]     getter height
+[Worker]     getter oncontextlost
+[Worker]     getter oncontextrestored
 [Worker]     getter width
 [Worker]     method constructor
 [Worker]     method convertToBlob
 [Worker]     method getContext
 [Worker]     method transferToImageBitmap
 [Worker]     setter height
+[Worker]     setter oncontextlost
+[Worker]     setter oncontextrestored
 [Worker]     setter width
 [Worker] interface OffscreenCanvasRenderingContext2D
 [Worker]     attribute @@toStringTag
@@ -794,10 +801,14 @@
 [Worker]     getter fillStyle
 [Worker]     getter filter
 [Worker]     getter font
+[Worker]     getter fontKerning
+[Worker]     getter fontStretch
+[Worker]     getter fontVariantCaps
 [Worker]     getter globalAlpha
 [Worker]     getter globalCompositeOperation
 [Worker]     getter imageSmoothingEnabled
 [Worker]     getter imageSmoothingQuality
+[Worker]     getter letterSpacing
 [Worker]     getter lineCap
 [Worker]     getter lineDashOffset
 [Worker]     getter lineJoin
@@ -810,6 +821,8 @@
 [Worker]     getter strokeStyle
 [Worker]     getter textAlign
 [Worker]     getter textBaseline
+[Worker]     getter textRendering
+[Worker]     getter wordSpacing
 [Worker]     method arc
 [Worker]     method arcTo
 [Worker]     method beginPath
@@ -818,6 +831,7 @@
 [Worker]     method clip
 [Worker]     method closePath
 [Worker]     method constructor
+[Worker]     method createConicGradient
 [Worker]     method createImageData
 [Worker]     method createLinearGradient
 [Worker]     method createPattern
@@ -830,6 +844,7 @@
 [Worker]     method getImageData
 [Worker]     method getLineDash
 [Worker]     method getTransform
+[Worker]     method isContextLost
 [Worker]     method isPointInPath
 [Worker]     method isPointInStroke
 [Worker]     method lineTo
@@ -838,9 +853,11 @@
 [Worker]     method putImageData
 [Worker]     method quadraticCurveTo
 [Worker]     method rect
+[Worker]     method reset
 [Worker]     method resetTransform
 [Worker]     method restore
 [Worker]     method rotate
+[Worker]     method roundRect
 [Worker]     method save
 [Worker]     method scale
 [Worker]     method setLineDash
@@ -854,10 +871,14 @@
 [Worker]     setter fillStyle
 [Worker]     setter filter
 [Worker]     setter font
+[Worker]     setter fontKerning
+[Worker]     setter fontStretch
+[Worker]     setter fontVariantCaps
 [Worker]     setter globalAlpha
 [Worker]     setter globalCompositeOperation
 [Worker]     setter imageSmoothingEnabled
 [Worker]     setter imageSmoothingQuality
+[Worker]     setter letterSpacing
 [Worker]     setter lineCap
 [Worker]     setter lineDashOffset
 [Worker]     setter lineJoin
@@ -870,6 +891,8 @@
 [Worker]     setter strokeStyle
 [Worker]     setter textAlign
 [Worker]     setter textBaseline
+[Worker]     setter textRendering
+[Worker]     setter wordSpacing
 [Worker] interface Path2D
 [Worker]     attribute @@toStringTag
 [Worker]     method addPath
@@ -883,6 +906,7 @@
 [Worker]     method moveTo
 [Worker]     method quadraticCurveTo
 [Worker]     method rect
+[Worker]     method roundRect
 [Worker] interface PaymentInstruments
 [Worker]     attribute @@toStringTag
 [Worker]     method clear
diff --git a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
index bd80bb8..589e977 100644
--- a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
@@ -411,6 +411,10 @@
 textDecorationSkipInk
 textDecorationStyle
 textDecorationThickness
+textEmphasis
+textEmphasisColor
+textEmphasisPosition
+textEmphasisStyle
 textIndent
 textJustify
 textOrientation
diff --git a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
index 77d35d60..3690532 100644
--- a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
@@ -41,9 +41,6 @@
     -webkit-tap-highlight-color
     -webkit-text-combine
     -webkit-text-decorations-in-effect
-    -webkit-text-emphasis-color
-    -webkit-text-emphasis-position
-    -webkit-text-emphasis-style
     -webkit-text-fill-color
     -webkit-text-orientation
     -webkit-text-security
@@ -358,6 +355,9 @@
     text-decoration-skip-ink
     text-decoration-style
     text-decoration-thickness
+    text-emphasis-color
+    text-emphasis-position
+    text-emphasis-style
     text-indent
     text-justify
     text-orientation
@@ -424,9 +424,6 @@
     -webkit-mask-repeat
         -webkit-mask-repeat-x
         -webkit-mask-repeat-y
-    -webkit-text-emphasis
-        -webkit-text-emphasis-color
-        -webkit-text-emphasis-style
     -webkit-text-stroke
         -webkit-text-stroke-color
         -webkit-text-stroke-width
@@ -739,6 +736,9 @@
         text-decoration-line
         text-decoration-style
         text-decoration-thickness
+    text-emphasis
+        text-emphasis-color
+        text-emphasis-style
     transition
         transition-delay
         transition-duration
@@ -751,11 +751,11 @@
     -epub-text-combine
         -webkit-text-combine
     -epub-text-emphasis
-        -webkit-text-emphasis
+        text-emphasis
     -epub-text-emphasis-color
-        -webkit-text-emphasis-color
+        text-emphasis-color
     -epub-text-emphasis-style
-        -webkit-text-emphasis-style
+        text-emphasis-style
     -epub-text-orientation
         -webkit-text-orientation
     -epub-text-transform
@@ -928,6 +928,14 @@
         shape-margin
     -webkit-shape-outside
         shape-outside
+    -webkit-text-emphasis
+        text-emphasis
+    -webkit-text-emphasis-color
+        text-emphasis-color
+    -webkit-text-emphasis-position
+        text-emphasis-position
+    -webkit-text-emphasis-style
+        text-emphasis-style
     -webkit-text-size-adjust
         text-size-adjust
     -webkit-transform
diff --git a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
index 0f26e75..8b82ab6 100644
--- a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
@@ -562,10 +562,6 @@
     property src
     property type
     property width
-html element fencedframe
-    property height
-    property src
-    property width
 html element fieldset
     property checkValidity
     property disabled
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index dd49f32..44148f0 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -3450,15 +3450,6 @@
     setter src
     setter type
     setter width
-interface HTMLFencedFrameElement : HTMLElement
-    attribute @@toStringTag
-    getter height
-    getter src
-    getter width
-    method constructor
-    setter height
-    setter src
-    setter width
 interface HTMLFieldSetElement : HTMLElement
     attribute @@toStringTag
     getter disabled
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/history-back-and-forward-should-not-work-in-fenced-tree.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/history-back-and-forward-should-not-work-in-fenced-tree.html
new file mode 100644
index 0000000..0fec176
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/history-back-and-forward-should-not-work-in-fenced-tree.html
@@ -0,0 +1,61 @@
+!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+<title>history-back-and-forward-should-not-work-in-fenced-tree</title>
+
+<body>
+  <script>
+    const test_desc = "history.back() and history.forward() should be " +
+                      "restricted within a fenced tree in shadowDOM thus have" +
+                      " no effect when called within a fenced tree scope.";
+    const history_navigation_performed_key =
+      KEYS["history_navigation_performed"];
+    const outer_page_ready_key = KEYS["outer_page_ready"];
+
+    // attach a fenced frame to exeute a series of back and forward history
+    // navigations.
+    attachFencedFrame("resources/history-back-and-forward-should-not-work-in-" +
+      "fenced-tree-inner.html");
+
+    promise_test(async function () {
+      await nextValueFromServer(history_navigation_performed_key);
+
+      // Perform a series of history.pushState() to help observe any popstate due
+      // to back and forward history navigations.
+      window.history.pushState(1, document.title, '#tag1');
+      window.history.pushState(2, document.title, '#tag2');
+      window.history.pushState(3, document.title, '#tag3');
+
+      writeValueToServer(outer_page_ready_key, "yes");
+
+      // Assert restricted history.back() within fenced frame.
+      await nextValueFromServer(history_navigation_performed_key);
+      assert_equals(window.history.state, 3, "history.back() should be " +
+        "restricted and will not work when called from a fenced frame.");
+
+      writeValueToServer(outer_page_ready_key, "yes");
+
+      // Assert restricted history.forward() within fenced frame.
+      await nextValueFromServer(history_navigation_performed_key);
+      assert_equals(window.history.state, 3, "history.forward() should be " +
+        "restricted and will not work when called from a fenced frame.");
+
+      writeValueToServer(outer_page_ready_key, "yes");
+
+      // Assert restricted history.back() within iframe in fenced frame.
+      await nextValueFromServer(history_navigation_performed_key);
+      assert_equals(window.history.state, 3, "history.back() should be " +
+        "restricted and will not work within an iframe embeeded in a fenced " +
+        "frame.");
+
+      writeValueToServer(outer_page_ready_key, "yes");
+
+      // Assert restricted history.forward() within iframe in fenced frame.
+      await nextValueFromServer(history_navigation_performed_key);
+      assert_equals(window.history.state, 3, "history.forward() should be " +
+        "restricted and will not work within an iframe embeeded in a fenced " +
+        "frame.");
+    }, test_desc);
+  </script>
+</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resize-lock.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/resize-lock.html
new file mode 100644
index 0000000..fd264a0b
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/resize-lock.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+  <title>Test FencedFrames Resize Lock</title>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="resources/utils.js"></script>
+
+  <body>
+
+    <script>
+      promise_test(async () => {
+        const resize_lock_inner_page_is_ready_key =
+          KEYS["resize_lock_inner_page_is_ready"];
+        const resize_lock_resize_is_done_key =
+          KEYS["resize_lock_resize_is_done"];
+        const resize_lock_report_inner_dimensions_key =
+          KEYS["resize_lock_report_inner_dimensions"];
+
+        const frame = attachFencedFrame("resources/resize-lock-inner.html");
+
+        await nextValueFromServer(resize_lock_inner_page_is_ready_key);
+
+        frame.width = "444";
+        frame.height = "444";
+
+        writeValueToServer(resize_lock_resize_is_done_key,
+                           "outer_page_attempted_resize");
+
+        let result =
+          await nextValueFromServer(resize_lock_report_inner_dimensions_key);
+        assert_equals(result, "300x150",
+                      "fenced frame dimensions should not be updated by " +
+                      "parent page");
+
+      }, "Test Resize Lock");
+    </script>
+
+  </body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html
new file mode 100644
index 0000000..df0195c
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html
@@ -0,0 +1,43 @@
+!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="utils.js"></script>
+<title>history-back-and-forward-should-not-work-in-fenced-tree-inner</title>
+
+<body>
+  <script>
+    // This is a helper file that will serve as the document loaded inside
+    // a fenced frame by 'history-back-and-forward-should-not-work-in-fenced
+    // -tree' Once loaded, it will sequentially perform the back and forward
+    // history navigations to observe whether these methods were successfuly
+    // restricted for the fenced tree.
+
+    const history_navigation_performed_key =
+      KEYS["history_navigation_performed"];
+    const outer_page_ready_key = KEYS["outer_page_ready"];
+
+    (async function () {
+      const url = new URL(location.href);
+      const test = url.searchParams.get("test");
+      const embed_scope = url.searchParams.get("embed_scope");
+
+      writeValueToServer(history_navigation_performed_key, "yes");
+
+      // Execute history.back() within fenced frame and iframe.
+      await nextValueFromServer(outer_page_ready_key);
+      window.history.back();
+      writeValueToServer(history_navigation_performed_key, "yes");
+
+      // Execute history.forward() within fenced frame and iframe.
+      await nextValueFromServer(outer_page_ready_key);
+      window.history.forward();
+      writeValueToServer(history_navigation_performed_key, "yes");
+
+      if (embed_scope === "outerPage::fencedFrame::iframe") return;
+
+      const iframe = document.createElement('iframe');
+      iframe.src = "history-back-and-forward-should-not-work-in-fenced-tree-" +
+        "inner.html?embed_scope=outerPage::fencedFrame::iframe";
+      document.body.append(iframe);
+    })();
+  </script>
+</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html.headers b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html.headers
new file mode 100644
index 0000000..1b63235
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html.headers
@@ -0,0 +1 @@
+Supports-Loading-Mode: fenced-frame
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/resize-lock-inner.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/resize-lock-inner.html
new file mode 100644
index 0000000..75c396209
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/resize-lock-inner.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+  <script src="utils.js"></script>
+  <title>Fenced frame content to report any changes in inner dimensions</title>
+
+  <body>
+    <script>
+      async function init() {
+        const resize_lock_inner_page_is_ready_key =
+          KEYS["resize_lock_inner_page_is_ready"];
+        const resize_lock_resize_is_done_key =
+          KEYS["resize_lock_resize_is_done"];
+        const resize_lock_report_inner_dimensions_key =
+          KEYS["resize_lock_report_inner_dimensions"];
+
+        writeValueToServer(resize_lock_inner_page_is_ready_key, "ready");
+
+        await nextValueFromServer(resize_lock_resize_is_done_key);
+
+        const response = window.innerWidth + "x" + window.innerHeight;
+        writeValueToServer(resize_lock_report_inner_dimensions_key, response);
+      }
+
+      init();
+    </script>
+  </body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/resize-lock-inner.html.headers b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/resize-lock-inner.html.headers
new file mode 100644
index 0000000..6247f6d
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/resize-lock-inner.html.headers
@@ -0,0 +1 @@
+Supports-Loading-Mode: fenced-frame
\ No newline at end of file
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/utils.js b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/utils.js
index 602cd6a..a0b28f58 100644
--- a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/utils.js
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/utils.js
@@ -100,6 +100,13 @@
 
   "ndef.write"                                  : "00000000-0000-0000-0000-000000000034",
   "ndef.scan"                                   : "00000000-0000-0000-0000-000000000035",
+
+  "history_navigation_performed"                : "00000000-0000-0000-0000-000000000036",
+  "outer_page_ready"                            : "00000000-0000-0000-0000-000000000037",
+
+  "resize_lock_inner_page_is_ready"             : "00000000-0000-0000-0000-000000000038",
+  "resize_lock_resize_is_done"                  : "00000000-0000-0000-0000-000000000039",
+  "resize_lock_report_inner_dimensions"         : "00000000-0000-0000-0000-00000000004A",
   // Add keys above this list, incrementing the key UUID in hexadecimal
 }
 
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index fe056c5..d6cb8e3 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: 5cc0d543d0e5dadba03a6bf7dd8161fb78ab07b5
+Revision: 0ea32e0c7bc2569e597e412a20151ad8a7e64623
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_linux_test.cc b/third_party/crashpad/crashpad/client/crashpad_client_linux_test.cc
index ddf1539..1ab974e 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client_linux_test.cc
+++ b/third_party/crashpad/crashpad/client/crashpad_client_linux_test.cc
@@ -90,6 +90,9 @@
   ~StartHandlerForSelfTest() = default;
 
   void SetUp() override {
+    // MSAN requires that padding bytes have been initialized for structs that
+    // are written to files.
+    memset(&options_, 0, sizeof(options_));
     std::tie(options_.start_handler_at_crash,
              options_.set_first_chance_handler,
              options_.crash_non_main_thread,
diff --git a/third_party/grpc/BUILD.gn b/third_party/grpc/BUILD.gn
index eae94dc..d08397c 100644
--- a/third_party/grpc/BUILD.gn
+++ b/third_party/grpc/BUILD.gn
@@ -420,6 +420,8 @@
     "src/src/core/ext/filters/http/server/http_server_filter.h",
     "src/src/core/ext/filters/max_age/max_age_filter.h",
     "src/src/core/ext/filters/message_size/message_size_filter.h",
+    "src/src/core/ext/filters/server_config_selector/server_config_selector.h",
+    "src/src/core/ext/filters/server_config_selector/server_config_selector_filter.h",
     "src/src/core/ext/service_config/service_config.h",
     "src/src/core/ext/service_config/service_config_call_data.h",
     "src/src/core/ext/service_config/service_config_parser.h",
@@ -546,7 +548,10 @@
     "src/src/core/ext/upb-generated/envoy/type/v3/semantic_version.upb.h",
     "src/src/core/ext/upb-generated/google/api/annotations.upb.h",
     "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/checked.upb.h",
+    "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/eval.upb.h",
+    "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/explain.upb.h",
     "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/syntax.upb.h",
+    "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/value.upb.h",
     "src/src/core/ext/upb-generated/google/api/http.upb.h",
     "src/src/core/ext/upb-generated/google/protobuf/any.upb.h",
     "src/src/core/ext/upb-generated/google/protobuf/duration.upb.h",
@@ -676,6 +681,7 @@
     "src/src/core/lib/backoff/backoff.h",
     "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_stack.h",
     "src/src/core/lib/channel/channel_stack_builder.h",
     "src/src/core/lib/channel/channel_trace.h",
@@ -711,7 +717,6 @@
     "src/src/core/lib/gpr/tls.h",
     "src/src/core/lib/gpr/tmpfile.h",
     "src/src/core/lib/gpr/useful.h",
-    "src/src/core/lib/gprpp/arena.h",
     "src/src/core/lib/gprpp/atomic_utils.h",
     "src/src/core/lib/gprpp/bitset.h",
     "src/src/core/lib/gprpp/chunked_vector.h",
@@ -727,11 +732,9 @@
     "src/src/core/lib/gprpp/global_config_generic.h",
     "src/src/core/lib/gprpp/host_port.h",
     "src/src/core/lib/gprpp/manual_constructor.h",
-    "src/src/core/lib/gprpp/match.h",
     "src/src/core/lib/gprpp/memory.h",
     "src/src/core/lib/gprpp/mpscq.h",
     "src/src/core/lib/gprpp/orphanable.h",
-    "src/src/core/lib/gprpp/overload.h",
     "src/src/core/lib/gprpp/ref_counted.h",
     "src/src/core/lib/gprpp/ref_counted_ptr.h",
     "src/src/core/lib/gprpp/stat.h",
@@ -837,6 +840,7 @@
     "src/src/core/lib/promise/race.h",
     "src/src/core/lib/promise/seq.h",
     "src/src/core/lib/resource_quota/api.h",
+    "src/src/core/lib/resource_quota/arena.h",
     "src/src/core/lib/resource_quota/memory_quota.h",
     "src/src/core/lib/resource_quota/resource_quota.h",
     "src/src/core/lib/resource_quota/thread_quota.h",
@@ -892,6 +896,7 @@
     "src/src/core/lib/security/util/json_util.h",
     "src/src/core/lib/slice/b64.h",
     "src/src/core/lib/slice/percent_encoding.h",
+    "src/src/core/lib/slice/slice.h",
     "src/src/core/lib/slice/slice_internal.h",
     "src/src/core/lib/slice/slice_refcount.h",
     "src/src/core/lib/slice/slice_refcount_base.h",
@@ -924,7 +929,6 @@
     "src/src/core/lib/transport/pid_controller.h",
     "src/src/core/lib/transport/static_metadata.h",
     "src/src/core/lib/transport/status_conversion.h",
-    "src/src/core/lib/transport/status_metadata.h",
     "src/src/core/lib/transport/timeout_encoding.h",
     "src/src/core/lib/transport/transport.h",
     "src/src/core/lib/transport/transport_impl.h",
@@ -1047,7 +1051,6 @@
     "src/src/core/ext/filters/client_idle/idle_filter_state.cc",
     "src/src/core/ext/filters/deadline/deadline_filter.cc",
     "src/src/core/ext/filters/fault_injection/fault_injection_filter.cc",
-    "src/src/core/ext/filters/fault_injection/service_config_parser.cc",
     "src/src/core/ext/filters/http/client/http_client_filter.cc",
     "src/src/core/ext/filters/http/client_authority_filter.cc",
     "src/src/core/ext/filters/http/http_filters_plugin.cc",
@@ -1056,8 +1059,12 @@
     "src/src/core/ext/filters/http/server/http_server_filter.cc",
     "src/src/core/ext/filters/max_age/max_age_filter.cc",
     "src/src/core/ext/filters/message_size/message_size_filter.cc",
+    "src/src/core/ext/filters/server_config_selector/server_config_selector.cc",
+    "src/src/core/ext/filters/server_config_selector/server_config_selector_filter.cc",
     "src/src/core/ext/service_config/service_config.cc",
+    "src/src/core/ext/service_config/service_config_parser.cc",
     "src/src/core/ext/transport/binder/client/binder_connector.cc",
+    "src/src/core/ext/transport/binder/client/channel_create.cc",
     "src/src/core/ext/transport/binder/client/channel_create_impl.cc",
     "src/src/core/ext/transport/binder/client/connection_id_generator.cc",
     "src/src/core/ext/transport/binder/client/endpoint_binder_pool.cc",
@@ -1076,7 +1083,6 @@
     "src/src/core/ext/transport/binder/wire_format/wire_writer.cc",
     "src/src/core/ext/transport/chttp2/alpn/alpn.cc",
     "src/src/core/ext/transport/chttp2/client/chttp2_connector.cc",
-    "src/src/core/ext/transport/chttp2/client/insecure/channel_create.cc",
     "src/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc",
     "src/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc",
     "src/src/core/ext/transport/chttp2/server/chttp2_server.cc",
@@ -1111,11 +1117,9 @@
     "src/src/core/ext/transport/inproc/inproc_transport.cc",
     "src/src/core/ext/upb-generated/envoy/admin/v3/config_dump.upb.c",
     "src/src/core/ext/upb-generated/envoy/annotations/deprecation.upb.c",
-    "src/src/core/ext/upb-generated/envoy/annotations/resource.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/accesslog/v3/accesslog.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/bootstrap/v3/bootstrap.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/cluster/v3/circuit_breaker.upb.c",
-    "src/src/core/ext/upb-generated/envoy/config/cluster/v3/cluster.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/cluster/v3/filter.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/cluster/v3/outlier_detection.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/core/v3/address.upb.c",
@@ -1148,7 +1152,8 @@
     "src/src/core/ext/upb-generated/envoy/config/route/v3/route_components.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/route/v3/scoped_route.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/trace/v3/http_tracer.upb.c",
-    "src/src/core/ext/upb-generated/envoy/extensions/filters/common/fault/v3/fault.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/clusters/aggregate/v3/cluster.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/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/transport_sockets/tls/v3/cert.upb.c",
@@ -1165,19 +1170,21 @@
     "src/src/core/ext/upb-generated/envoy/service/route/v3/srds.upb.c",
     "src/src/core/ext/upb-generated/envoy/service/status/v3/csds.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/http/v3/path_transformation.upb.c",
-    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/metadata.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/node.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/number.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/path.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/regex.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/string.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/value.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/metadata/v3/metadata.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/tracing/v3/custom_tag.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/v3/percent.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/v3/range.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/v3/semantic_version.upb.c",
     "src/src/core/ext/upb-generated/google/api/annotations.upb.c",
     "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/checked.upb.c",
+    "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/eval.upb.c",
+    "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/explain.upb.c",
     "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/syntax.upb.c",
     "src/src/core/ext/upb-generated/google/api/http.upb.c",
     "src/src/core/ext/upb-generated/google/protobuf/any.upb.c",
@@ -1186,7 +1193,6 @@
     "src/src/core/ext/upb-generated/google/protobuf/struct.upb.c",
     "src/src/core/ext/upb-generated/google/protobuf/timestamp.upb.c",
     "src/src/core/ext/upb-generated/google/protobuf/wrappers.upb.c",
-    "src/src/core/ext/upb-generated/google/rpc/status.upb.c",
     "src/src/core/ext/upb-generated/src/proto/grpc/gcp/altscontext.upb.c",
     "src/src/core/ext/upb-generated/src/proto/grpc/gcp/handshaker.upb.c",
     "src/src/core/ext/upb-generated/src/proto/grpc/gcp/transport_security_common.upb.c",
@@ -1198,16 +1204,17 @@
     "src/src/core/ext/upb-generated/udpa/annotations/sensitive.upb.c",
     "src/src/core/ext/upb-generated/udpa/annotations/versioning.upb.c",
     "src/src/core/ext/upb-generated/validate/validate.upb.c",
+    "src/src/core/ext/upb-generated/xds/annotations/v3/status.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/authority.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/collection_entry.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/context_params.upb.c",
+    "src/src/core/ext/upb-generated/xds/core/v3/resource.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/resource_locator.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/resource_name.upb.c",
     "src/src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.c",
     "src/src/core/ext/upb-generated/xds/type/v3/typed_struct.upb.c",
     "src/src/core/ext/upbdefs-generated/envoy/admin/v3/config_dump.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/annotations/deprecation.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/envoy/annotations/resource.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/accesslog/v3/accesslog.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/bootstrap/v3/bootstrap.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/circuit_breaker.upbdefs.c",
@@ -1260,7 +1267,6 @@
     "src/src/core/ext/upbdefs-generated/envoy/service/route/v3/srds.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/service/status/v3/csds.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/http/v3/path_transformation.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/metadata.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/node.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/number.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/path.upbdefs.c",
@@ -1268,6 +1274,7 @@
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/string.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/struct.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/value.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/metadata/v3/metadata.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/tracing/v3/custom_tag.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/v3/http.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/v3/percent.upbdefs.c",
@@ -1279,15 +1286,16 @@
     "src/src/core/ext/upbdefs-generated/google/protobuf/empty.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/protobuf/timestamp.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/protobuf/wrappers.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/google/rpc/status.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/udpa/annotations/migrate.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/udpa/annotations/security.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/udpa/annotations/sensitive.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/udpa/annotations/versioning.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/validate/validate.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/annotations/v3/status.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/authority.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/collection_entry.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/context_params.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/resource.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/resource_locator.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/resource_name.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/type/v3/typed_struct.upbdefs.c",
@@ -1295,6 +1303,7 @@
     "src/src/core/lib/address_utils/sockaddr_utils.cc",
     "src/src/core/lib/backoff/backoff.cc",
     "src/src/core/lib/channel/channel_args.cc",
+    "src/src/core/lib/channel/channel_args_preconditioning.cc",
     "src/src/core/lib/channel/channel_stack.cc",
     "src/src/core/lib/channel/channel_stack_builder.cc",
     "src/src/core/lib/channel/channel_trace.cc",
@@ -1314,7 +1323,6 @@
     "src/src/core/lib/config/core_configuration.cc",
     "src/src/core/lib/debug/stats.cc",
     "src/src/core/lib/debug/stats_data.cc",
-    "src/src/core/lib/debug/trace.cc",
     "src/src/core/lib/event_engine/channel_args_endpoint_config.cc",
     "src/src/core/lib/event_engine/event_engine.cc",
     "src/src/core/lib/event_engine/event_engine_factory.cc",
@@ -1354,7 +1362,6 @@
     # gRPC memcpy wrapping logic isn't useful here.
     # See https://crbug.com/661171
     # "src/src/core/lib/gpr/wrap_memcpy.cc",
-    "src/src/core/lib/gprpp/arena.cc",
     "src/src/core/lib/gprpp/examine_stack.cc",
     "src/src/core/lib/gprpp/fork.cc",
     "src/src/core/lib/gprpp/global_config_env.cc",
@@ -1394,7 +1401,6 @@
     "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/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",
@@ -1449,6 +1455,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_custom.cc",
     "src/src/core/lib/iomgr/timer_generic.cc",
     "src/src/core/lib/iomgr/timer_heap.cc",
@@ -1469,9 +1476,11 @@
     "src/src/core/lib/profiling/stap_timers.cc",
     "src/src/core/lib/promise/activity.cc",
     "src/src/core/lib/resource_quota/api.cc",
+    "src/src/core/lib/resource_quota/arena.cc",
     "src/src/core/lib/resource_quota/memory_quota.cc",
     "src/src/core/lib/resource_quota/resource_quota.cc",
     "src/src/core/lib/resource_quota/thread_quota.cc",
+    "src/src/core/lib/resource_quota/trace.cc",
     "src/src/core/lib/security/authorization/authorization_policy_provider_vtable.cc",
     "src/src/core/lib/security/authorization/evaluate_args.cc",
     "src/src/core/lib/security/authorization/sdk_server_authz_filter.cc",
@@ -1496,7 +1505,6 @@
     "src/src/core/lib/security/credentials/google_default/credentials_generic.cc",
     "src/src/core/lib/security/credentials/google_default/google_default_credentials.cc",
     "src/src/core/lib/security/credentials/iam/iam_credentials.cc",
-    "src/src/core/lib/security/credentials/insecure/insecure_credentials.cc",
     "src/src/core/lib/security/credentials/jwt/json_token.cc",
     "src/src/core/lib/security/credentials/jwt/jwt_credentials.cc",
     "src/src/core/lib/security/credentials/jwt/jwt_verifier.cc",
@@ -1565,10 +1573,10 @@
     "src/src/core/lib/transport/error_utils.cc",
     "src/src/core/lib/transport/metadata.cc",
     "src/src/core/lib/transport/metadata_batch.cc",
+    "src/src/core/lib/transport/parsed_metadata.cc",
     "src/src/core/lib/transport/pid_controller.cc",
     "src/src/core/lib/transport/static_metadata.cc",
     "src/src/core/lib/transport/status_conversion.cc",
-    "src/src/core/lib/transport/status_metadata.cc",
     "src/src/core/lib/transport/timeout_encoding.cc",
     "src/src/core/lib/transport/transport.cc",
     "src/src/core/lib/transport/transport_op_string.cc",
@@ -1608,6 +1616,7 @@
     "src/src/cpp/client/create_channel_internal.cc",
     "src/src/cpp/client/create_channel_posix.cc",
     "src/src/cpp/client/credentials_cc.cc",
+    "src/src/cpp/client/insecure_credentials.cc",
     "src/src/cpp/client/secure_credentials.cc",
     "src/src/cpp/codegen/codegen_init.cc",
     "src/src/cpp/common/alarm.cc",
@@ -1676,30 +1685,31 @@
 
 source_set("grpc++_repeated1") {
   sources = [
-    "src/src/core/ext/service_config/service_config_parser.cc",
-    "src/src/core/ext/transport/binder/client/channel_create.cc",
-    "src/src/core/ext/upb-generated/envoy/extensions/clusters/aggregate/v3/cluster.upb.c",
-    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/fault/v3/fault.upb.c",
+    "src/src/core/ext/filters/fault_injection/service_config_parser.cc",
+    "src/src/core/ext/transport/chttp2/client/insecure/channel_create.cc",
+    "src/src/core/ext/upb-generated/envoy/annotations/resource.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/cluster/v3/cluster.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/common/fault/v3/fault.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/metadata.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/struct.upb.c",
-    "src/src/core/ext/upb-generated/envoy/type/metadata/v3/metadata.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/v3/http.upb.c",
-    "src/src/core/ext/upb-generated/udpa/annotations/status.upb.c",
-    "src/src/core/ext/upb-generated/xds/core/v3/resource.upb.c",
+    "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/value.upb.c",
+    "src/src/core/ext/upb-generated/google/rpc/status.upb.c",
+    "src/src/core/ext/upbdefs-generated/envoy/annotations/resource.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/clusters/aggregate/v3/cluster.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/fault/v3/fault.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/envoy/type/metadata/v3/metadata.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/metadata.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/api/http.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/protobuf/struct.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/udpa/annotations/status.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/xds/core/v3/resource.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/rpc/status.upbdefs.c",
+    "src/src/core/lib/debug/trace.cc",
     "src/src/core/lib/iomgr/event_engine/endpoint.cc",
     "src/src/core/lib/iomgr/event_engine/resolver.cc",
+    "src/src/core/lib/iomgr/event_engine/timer.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/resource_quota/trace.cc",
+    "src/src/core/lib/security/credentials/insecure/insecure_credentials.cc",
     "src/src/core/lib/security/util/json_util.cc",
-    "src/src/cpp/client/insecure_credentials.cc",
 
     # "src/src/cpp/client/xds_credentials.cc",
   ]
@@ -1724,8 +1734,8 @@
 }
 source_set("grpc++_repeated2") {
   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/ext/upb-generated/udpa/annotations/status.upb.c",
+    "src/src/core/ext/upbdefs-generated/udpa/annotations/status.upbdefs.c",
   ]
 
   deps = [
diff --git a/third_party/grpc/README.chromium b/third_party/grpc/README.chromium
index 95cd8b8..9d999782 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.41.1+
-Revision: da47e8823754d12f1dd643bc911e80703b557ade
+Version: v1.42.0+
+Revision: 47f58f5b8d338a6861febc8ee1a602c23a5af70a
 Security Critical: yes
 
 Please note that that the use of gRPC is not generally allowed within Chromium.
@@ -27,3 +27,7 @@
 7. Run: gn format --in-place BUILD.gn
 
 We have a helper script `generate_gn.sh` does step (4), (5), (6), and (7).
+
+Note: Please also remember to check if `plugin_registry/grpc_plugin_registry.cc`
+needs update! Upstream sometimes introduces new plugins and we may need to update
+our custom `grpc_plugin_registry.cc`.
diff --git a/third_party/grpc/plugin_registry/grpc_plugin_registry.cc b/third_party/grpc/plugin_registry/grpc_plugin_registry.cc
index a204ae6..2061555 100644
--- a/third_party/grpc/plugin_registry/grpc_plugin_registry.cc
+++ b/third_party/grpc/plugin_registry/grpc_plugin_registry.cc
@@ -173,6 +173,7 @@
 extern void RegisterSecurityFilters(CoreConfiguration::Builder* builder);
 extern void RegisterServiceConfigChannelArgFilter(
     CoreConfiguration::Builder* builder);
+extern void RegisterResourceQuota(CoreConfiguration::Builder* builder);
 #ifndef GRPC_NO_XDS
 extern void RegisterXdsChannelStackModifier(
     CoreConfiguration::Builder* builder);
@@ -189,6 +190,7 @@
   RegisterDeadlineFilter(builder);
   RegisterMessageSizeFilter(builder);
   RegisterServiceConfigChannelArgFilter(builder);
+  RegisterResourceQuota(builder);
 #ifndef GRPC_NO_XDS
   RegisterXdsChannelStackModifier(builder);
 #endif
diff --git a/third_party/grpc/template/BUILD.chromium.gn.template b/third_party/grpc/template/BUILD.chromium.gn.template
index 91d88d6..0ece963 100644
--- a/third_party/grpc/template/BUILD.chromium.gn.template
+++ b/third_party/grpc/template/BUILD.chromium.gn.template
@@ -137,6 +137,9 @@
   # Split the sources into 3 set of sources so that
   # sources with repeated basenames are in different sets
   def find_repeated(sources):
+    # Deduplicate the sources files in the input so the same source file
+    # don't appear in multiple sets
+    sources = set(sources)
     out_sources = []
     repeated1 = []
     repeated2 = []
diff --git a/third_party/libaddressinput/chromium/address_input_strings.grd b/third_party/libaddressinput/chromium/address_input_strings.grd
index 1b56377c..16656ac 100644
--- a/third_party/libaddressinput/chromium/address_input_strings.grd
+++ b/third_party/libaddressinput/chromium/address_input_strings.grd
@@ -53,7 +53,7 @@
       <output filename="address_input_strings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="address_input_strings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="address_input_strings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="address_input_strings_am.pak" type="data_package" lang="am" />
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index fa3364f..420f569 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -421,6 +421,10 @@
     "META": {"align": 100},
     "messages": [2900],
   },
+  "<(SHARED_INTERMEDIATE_DIR)/ash/ambient/resources/lottie_resources.grd": {
+    "META": {"sizes": {"includes": [100],}},
+    "includes": [2910],
+  },
   "<(SHARED_INTERMEDIATE_DIR)/ash/webui/camera_app_ui/ash_camera_app_resources.grd": {
     "META": {"sizes": {"includes": [300],}},
     "includes": [2920],
@@ -490,11 +494,11 @@
     "includes": [3140],
     "structures": [3160],
   },
-  "<(SHARED_INTERMEDIATE_DIR)/ash/webui/sample_system_web_app_ui/ash_sample_system_web_app_resources.grd": {
+  "<(SHARED_INTERMEDIATE_DIR)/ash/webui/sample_system_web_app_ui/resources/trusted/ash_sample_system_web_app_resources.grd": {
     "META": {"sizes": {"includes": [50],}},
     "includes": [3180],
   },
-  "<(SHARED_INTERMEDIATE_DIR)/ash/webui/sample_system_web_app_ui/ash_sample_system_web_app_untrusted_resources.grd": {
+  "<(SHARED_INTERMEDIATE_DIR)/ash/webui/sample_system_web_app_ui/resources/untrusted/ash_sample_system_web_app_untrusted_resources.grd": {
     "META": {"sizes": {"includes": [50],}},
     "includes": [3200],
   },
diff --git a/tools/mac/power/driver.py b/tools/mac/power/driver.py
index ced1b58..65dd6b8 100644
--- a/tools/mac/power/driver.py
+++ b/tools/mac/power/driver.py
@@ -37,17 +37,24 @@
     os.makedirs(f"{self._output_dir}", exist_ok=True)
 
   def __enter__(self):
+
     self._caffeinate_process = subprocess.Popen([
         "caffeinate",
         "-d",  # Prevent the display from sleeping.
-        "-i",  # Prevent the system from idle sleeping. This doesn't really take
-        # effect since the display is forced on.
-        "-u"  # Force user_idle_level to active.
+    ])
+    # Force user_idle_level to stay active by poking a key code. caffeinate -u
+    # declares that a user is active but this doesn't seem to have a lasting
+    # effect.
+    self._poke_user_process = subprocess.Popen([
+        "osascript",
+        os.path.join(os.path.dirname(__file__), "driver_scripts_templates",
+                     "poke_user.scpt")
     ])
     return self
 
   def __exit__(self, exc_type, exc_val, exc_tb):
     utils.TerminateProcess(self._caffeinate_process)
+    utils.TerminateProcess(self._poke_user_process)
 
   def SetMainDisplayBrightness(self, brightness_level: int):
     # This function imitates the open-source "brightness" tool at
diff --git a/tools/mac/power/driver_scripts_templates/poke_user.scpt b/tools/mac/power/driver_scripts_templates/poke_user.scpt
new file mode 100755
index 0000000..af969e0
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/poke_user.scpt
@@ -0,0 +1,17 @@
+#!/usr/bin/osascript
+
+-- 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.
+
+-- This script repeatedly sends a key to ensure the system considers
+-- the user active.
+
+tell application "System Events"
+  repeat
+    -- Send "shift" keycode.
+	  key code 57
+    -- user_idle_level is reset after 5min.
+    delay 120.0
+  end repeat
+end tell
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 4714112..2091e503 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -9782,8 +9782,8 @@
 </action>
 
 <action name="IOS.DefaultBrowserPromo.NonModal.Accepted">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@google.com</owner>
+  <owner>djean@chromium.org</owner>
   <description>
     The user accepted a non modal promo. This will either take them to the
     settings instructions for default browser ot directly into settings. To
@@ -9793,32 +9793,32 @@
 </action>
 
 <action name="IOS.DefaultBrowserPromo.NonModal.Appear">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@google.com</owner>
+  <owner>djean@chromium.org</owner>
   <description>
     The user triggered a non modal default promo, and it appeared. iOS only.
   </description>
 </action>
 
 <action name="IOS.DefaultBrowserPromo.NonModal.Dismiss">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@google.com</owner>
+  <owner>djean@chromium.org</owner>
   <description>
     The user dismissed explicitly a non modal promo. iOS only.
   </description>
 </action>
 
 <action name="IOS.DefaultBrowserPromo.NonModal.Timeout">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@google.com</owner>
+  <owner>djean@chromium.org</owner>
   <description>
     A non modal promo timed out and was dismissed. iOS only.
   </description>
 </action>
 
 <action name="IOS.DefaultBrowserPromo.TailoredFullscreen.Accepted">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@google.com</owner>
+  <owner>djean@chromium.org</owner>
   <description>
     The user accepted a tailored browser fullscreen promo modal, and was taken
     to the Settings app. iOS only.
@@ -9826,14 +9826,14 @@
 </action>
 
 <action name="IOS.DefaultBrowserPromo.TailoredFullscreen.Appear">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@google.com</owner>
+  <owner>djean@chromium.org</owner>
   <description>A tailored fullscren promo appeared. iOS only.</description>
 </action>
 
 <action name="IOS.DefaultBrowserPromo.TailoredFullscreen.Dismiss">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@google.com</owner>
+  <owner>djean@chromium.org</owner>
   <description>
     The user dismissed a tailored browser fullscreen promo modal. iOS only.
   </description>
@@ -10145,7 +10145,6 @@
 </action>
 
 <action name="IOSOpenByViewIntent">
-  <owner>javierrobles@chromium.org</owner>
   <owner>olivierrobin@chromium.org</owner>
   <description>
     User opened Chrome through a view intent, basically anything that opens
@@ -10155,7 +10154,6 @@
 
 <action name="IOSPasswordsSettingsCloseWithSwipe">
   <owner>djean@chromium.org</owner>
-  <owner>javierrobles@chromium.org</owner>
   <description>
     Reported when Passwords Settings UI was dismissed using swipe down gesture.
     iOS only.
@@ -14758,14 +14756,16 @@
 </action>
 
 <action name="ManualFallback_Close">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user closed the Manual Fallback view by tapping the keyboard icon.
   </description>
 </action>
 
 <action name="ManualFallback_ClosePopover">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user closed the Manual Fallback Popover view. This happens when the user
     taps outside the popover.
@@ -14773,7 +14773,8 @@
 </action>
 
 <action name="ManualFallback_ClosePull">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user closed the Manual Fallback view by pulling. This happens when the
     user pulls enough to the right, similar to &quot;pull to refresh&quot;.
@@ -14781,7 +14782,8 @@
 </action>
 
 <action name="ManualFallback_CreditCard_OpenAddPaymentMethod">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on &quot;Add Payment Method...&quot; on the Password Manual
     Fallback view.
@@ -14789,7 +14791,8 @@
 </action>
 
 <action name="ManualFallback_CreditCard_OpenManageCreditCard">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on &quot;Manage credit cards&quot; on the Password Manual
     Fallback view.
@@ -14797,50 +14800,58 @@
 </action>
 
 <action name="ManualFallback_CreditCard_SelectCardholderName">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on a cardholder name in the Credit Card Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_CreditCard_SelectCardNumber">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on a credit card number in the Credit Card Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_CreditCard_SelectExpirationMonth">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on an expiration month in the Credit Card Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_CreditCard_SelectExpirationYear">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on an expiration year in the Credit Card Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_OpenCreditCard">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>The user opened Credit Card Manual Fallback view.</description>
 </action>
 
 <action name="ManualFallback_OpenPassword">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>The user opened the Password Manual Fallback view.</description>
 </action>
 
 <action name="ManualFallback_OpenProfile">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>The user opened the Profile Manual Fallback view.</description>
 </action>
 
 <action name="ManualFallback_Password_OpenManagePassword">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on &quot;Manage passwords&quot; on the Password Manual
     Fallback view.
@@ -14848,7 +14859,8 @@
 </action>
 
 <action name="ManualFallback_Password_OpenOtherPassword">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on &quot;Use other password&quot; on the Password Manual
     Fallback view.
@@ -14856,63 +14868,72 @@
 </action>
 
 <action name="ManualFallback_Password_SelectPassword">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on a password in the Password Manual Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_Password_SelectUsername">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on a username in the Password Manual Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_Profiles_Address1">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on line 1 in the Address Manual Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_Profiles_Address2">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on line 2 in the Address Manual Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_Profiles_City">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on a city in the Address Manual Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_Profiles_Company">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on a company name in the Address Manual Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_Profiles_Country">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on a country in the Address Manual Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_Profiles_EmailAddress">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on an email address in the Address Manual Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_Profiles_OpenManageProfiles">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on &quot;Manage addresses&quot; on the Password Manual
     Fallback view.
@@ -14920,42 +14941,48 @@
 </action>
 
 <action name="ManualFallback_Profiles_PhoneNumber">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on a phone number in the Address Manual Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_Profiles_SelectFirstName">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on a first name in the Address Manual Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_Profiles_SelectLastName">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on a last name in the Address Manual Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_Profiles_SelectMiddleName">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on a middle name in the Address Manual Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_Profiles_State">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on a state / province in the Address Manual Fallback view.
   </description>
 </action>
 
 <action name="ManualFallback_Profiles_Zip">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <owner>olivierrobin@chromium.org</owner>
   <description>
     The user tapped on a zip code in the Address Manual Fallback view.
   </description>
@@ -16657,7 +16684,7 @@
 
 <action name="MobileLocationBarTapped">
   <owner>stkhapugin@chromium.org</owner>
-  <owner>rkgibson@chromium.org</owner>
+  <owner>rkgibson@google.com</owner>
   <description>
     The user has tapped location bar, which will focus the omnibox. iOS only.
   </description>
@@ -16856,7 +16883,7 @@
 </action>
 
 <action name="MobileMenuPasswords">
-  <owner>rkgibson@chromium.org</owner>
+  <owner>rkgibson@google.com</owner>
   <description>User pressed 'Passwords' in the app menu.</description>
 </action>
 
@@ -17505,7 +17532,8 @@
 
 <action name="MobilePasswordsSettingsClose">
   <owner>djean@chromium.org</owner>
-  <owner>javierrobles@chromium.org</owner>
+  <owner>kazinova@google.com</owner>
+  <owner>sczs@chromium.org</owner>
   <description>
     Reported when Passwords Settings UI was dismissed. iOS only.
   </description>
@@ -17568,7 +17596,7 @@
 </action>
 
 <action name="MobilePullGestureCloseTab">
-  <owner>rkgibson@chromium.org</owner>
+  <owner>rkgibson@google.com</owner>
   <owner>michaeldo@chromium.org</owner>
   <description>
     Emitted on iOS when over-scroll action touch gesture closes current tab.
@@ -17578,7 +17606,7 @@
 </action>
 
 <action name="MobilePullGestureNewTab">
-  <owner>rkgibson@chromium.org</owner>
+  <owner>rkgibson@google.com</owner>
   <owner>michaeldo@chromium.org</owner>
   <description>
     Emitted on iOS when over-scroll action touch gesture opens a new tab. This
@@ -17588,7 +17616,7 @@
 </action>
 
 <action name="MobilePullGestureReload">
-  <owner>rkgibson@chromium.org</owner>
+  <owner>rkgibson@google.com</owner>
   <owner>tedchoc@chromium.org</owner>
   <description>
     Emitted on Android and iOS when a pull-to-refresh touch gesture initiates a
@@ -18611,7 +18639,7 @@
 <action name="MobileToolbarSearchButtonTapped">
   <obsolete>Deprecate as of 3/2020</obsolete>
   <owner>stkhapugin@chromium.org</owner>
-  <owner>rkgibson@chromium.org</owner>
+  <owner>rkgibson@google.com</owner>
   <description>
     The user has tapped on search bottom toolbar, which will focus the omnibox.
     iOS only.
@@ -22506,7 +22534,7 @@
 </action>
 
 <action name="OverscrollActionCloseTab">
-  <owner>rkgibson@chromium.org</owner>
+  <owner>rkgibson@google.com</owner>
   <owner>kkhorimoto@chromium.org</owner>
   <description>
     Called when the user closes the tab using overscroll action.
@@ -28281,6 +28309,11 @@
   </description>
 </action>
 
+<action name="StatusArea_Cast_Detailed_Launch_AccesCastDialog">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <description>Please enter the description of the metric.</description>
+</action>
+
 <action name="StatusArea_Cast_Detailed_Launch_Cast">
   <owner>jdufault@chromium.org</owner>
   <description>
@@ -31500,6 +31533,20 @@
       label="For DownloadPageScreenshot feature."/>
   <suffix name="EphemeralTab" label="For Ephemeral Tab."/>
   <suffix name="ExploreSitesTile" label="For Explore Sites feature."/>
+  <suffix name="FeatureNotificationGuideDefaultBrowserNotificationShown"
+      label="For feature notification guide default browser notification
+             feature."/>
+  <suffix name="FeatureNotificationGuideIncognitoTabNotificationShown"
+      label="For feature notification guide incognito tab notification
+             feature."/>
+  <suffix name="FeatureNotificationGuideNTPSuggestionCardNotificationShown"
+      label="For feature notification guide NTP suggestion card notification
+             feature."/>
+  <suffix name="FeatureNotificationGuideSignInNotificationShown"
+      label="For feature notification guide sign in notification feature."/>
+  <suffix name="FeatureNotificationGuideVoiceSearchNotificationShown"
+      label="For feature notification guide voice search notification
+             feature."/>
   <suffix name="FeedCardMenu" label="For FeedCardMenu feature."/>
   <suffix name="FeedHeaderMenu" label="For FeedHeaderMenu feature."/>
   <suffix name="GlobalMediaControls" label="For GlobalMediaControls feature."/>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 3de8f20..7390f8a3 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2587,7 +2587,10 @@
   <int value="0" label="Java Exception thrown inside onRenderProcessGone()"/>
   <int value="1" label="Renderer crash not handled"/>
   <int value="2" label="Renderer kill (OOM or update) not handled"/>
-  <int value="3" label="Crash was handled for all WebViews"/>
+  <int value="3" label="Crash or kill was handled for all WebViews"/>
+  <int value="4" label="Renderer crash was handled for all WebViews"/>
+  <int value="5"
+      label="Renderer kill (OOM or update) was handled for all WebViews"/>
 </enum>
 
 <enum name="AndroidWebViewSafeModeResult">
@@ -50875,6 +50878,7 @@
   <int value="-1415244717" label="CrostiniVirtualKeyboardSupport:disabled"/>
   <int value="-1414531531" label="NtpModulesRedesignedLayout:disabled"/>
   <int value="-1412230070" label="query-tiles-instant-background-task"/>
+  <int value="-1411980923" label="LinkCapturingUiUpdate:enabled"/>
   <int value="-1411733990" label="OmniboxDedupeGoogleDriveURLs:disabled"/>
   <int value="-1411003295" label="disable-encrypted-media"/>
   <int value="-1410394131" label="EvDetailsInPageInfo:enabled"/>
@@ -51745,6 +51749,7 @@
   <int value="-787238455" label="OmniboxZeroSuggestionsOnSERP:disabled"/>
   <int value="-785528415" label="FedCm:disabled"/>
   <int value="-784199026" label="EnableFilesAppCopyImage:enabled"/>
+  <int value="-783890018" label="LacrosProfileMigrationForAnyUser:disabled"/>
   <int value="-780798969" label="disable-single-click-autofill"/>
   <int value="-778126349" label="DownloadsLocationChange:enabled"/>
   <int value="-778098896" label="EnableAggregatedMlSearchRanking:disabled"/>
@@ -52334,6 +52339,7 @@
   <int value="-336768659" label="NtpPhotosModule:enabled"/>
   <int value="-335331552" label="TabGroupsForTablets:disabled"/>
   <int value="-334873793" label="MediaSessionNotification:enabled"/>
+  <int value="-334846826" label="DesktopPWAsDefaultOfflinePage:enabled"/>
   <int value="-333216449" label="MouseSubframeNoImplicitCapture:enabled"/>
   <int value="-332010491" label="RetailCoupons:enabled"/>
   <int value="-331952590" label="OmniboxTriggerForPrerender2:enabled"/>
@@ -52958,6 +52964,7 @@
   <int value="132907995" label="DownloadProgressMessage:enabled"/>
   <int value="133482330" label="AppNotificationStatusMessaging:enabled"/>
   <int value="136924140" label="OfflineIndicatorV2:enabled"/>
+  <int value="138020994" label="LacrosProfileMigrationForAnyUser:enabled"/>
   <int value="138345654" label="enable-ambient-authentication-in-incognito"/>
   <int value="138598687"
       label="HappinessTrackingSurveysForDesktopDevToolsIssuesCookiesSameSite:enabled"/>
@@ -53140,6 +53147,7 @@
   <int value="288755982"
       label="AutofillEnableLocalCardMigrationForNonSyncUser:enabled"/>
   <int value="289465857" label="ClipboardHistorySimpleRender:disabled"/>
+  <int value="290559997" label="LinkCapturingUiUpdate:disabled"/>
   <int value="291389947" label="PercentBasedScrolling:disabled"/>
   <int value="291482671" label="ExperimentalFlingAnimation:disabled"/>
   <int value="291866104" label="WebAuthenticationPhoneSupport:enabled"/>
@@ -54820,6 +54828,7 @@
   <int value="1514158607" label="NearbySharingDeviceContacts:enabled"/>
   <int value="1515196403" label="fast-user-switching"/>
   <int value="1515334367" label="ToolbarMicIphAndroid:disabled"/>
+  <int value="1516258676" label="DesktopPWAsDefaultOfflinePage:disabled"/>
   <int value="1517863401" label="history-entry-requires-user-gesture"/>
   <int value="1517988103" label="FilesBannerFramework:disabled"/>
   <int value="1518833340" label="MediaAppDisplayExif:enabled"/>
@@ -56859,10 +56868,10 @@
   <int value="314" label="webkit-ruby-position"/>
   <int value="315" label="webkit-text-combine"/>
   <int value="316" label="webkit-text-decorations-in-effect"/>
-  <int value="317" label="webkit-text-emphasis"/>
-  <int value="318" label="webkit-text-emphasis-color"/>
-  <int value="319" label="webkit-text-emphasis-position"/>
-  <int value="320" label="webkit-text-emphasis-style"/>
+  <int value="317" label="alias-webkit-text-emphasis"/>
+  <int value="318" label="alias-webkit-text-emphasis-color"/>
+  <int value="319" label="alias-webkit-text-emphasis-position"/>
+  <int value="320" label="alias-webkit-text-emphasis-style"/>
   <int value="321" label="webkit-text-fill-color"/>
   <int value="322" label="webkit-text-security"/>
   <int value="323" label="webkit-text-stroke"/>
@@ -57247,6 +57256,10 @@
   <int value="702" label="app-region"/>
   <int value="703" label="font-synthesis-small-caps"/>
   <int value="704" label="font-synthesis"/>
+  <int value="705" label="text-emphasis"/>
+  <int value="706" label="text-emphasis-color"/>
+  <int value="707" label="text-emphasis-position"/>
+  <int value="708" label="text-emphasis-style"/>
 </enum>
 
 <enum name="MappedEditingCommands">
@@ -69058,6 +69071,7 @@
   <int value="4" label="Alt-mode fallback in guest session"/>
   <int value="5" label="Peripheral blocked"/>
   <int value="6" label="Billboard device"/>
+  <int value="7" label="Invalid DP alternate mode cable"/>
 </enum>
 
 <enum name="PermissionAction">
@@ -69251,6 +69265,12 @@
   <int value="511" label="Click on download in context menu media &gt;=5"/>
 </enum>
 
+<enum name="PhoneHubCameraRollOptInEntryPoint">
+  <int value="0" label="New user opted in via multidevice setup flow"/>
+  <int value="1" label="User opted in via onboarding dialog in phonehub view"/>
+  <int value="2" label="User opted in via Settings page"/>
+</enum>
+
 <enum name="PhoneHubFeatureStatus">
   <int value="0" label="Not eligible for feature"/>
   <int value="1" label="Eligible phone but not set up"/>
@@ -74899,7 +74919,8 @@
              tag"/>
   <int value="13" label="Unsupported permanent folder server tag"/>
   <int value="14"
-      label="Node is descendant of root node, without permanent folder"/>
+      label="Node is descendant of root node, without permanent folder
+             (deprecated)"/>
 </enum>
 
 <enum name="RemoteCommandExecutionStatus">
diff --git a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
index 58e7452c..d97fa82 100644
--- a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
+++ b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
@@ -13,6 +13,7 @@
 csharrison@chromium.org
 cthomp@chromium.org
 curranmax@chromium.org
+danielng@google.com
 davidmunro@google.com
 dewittj@chromium.org
 dmurph@chromium.org
@@ -31,7 +32,6 @@
 iby@chromium.org
 iclelland@chromium.org
 ioanap@chromium.org
-javierrobles@chromium.org
 jihanli@chromium.org
 johnidel@chromium.org
 jonross@chromium.org
@@ -60,6 +60,7 @@
 ravjit@chromium.org
 rayankans@chromium.org
 reillyg@chromium.org
+rkgibson@google.com
 rouslan@chromium.org
 rsorokin@chromium.org
 rushans@google.com
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index f6d0bb3..fe96adc2d 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -140,7 +140,7 @@
 </variants>
 
 <histogram name="Accessibility.ActiveTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>aleventhal@chromium.org</owner>
   <owner>janewman@microsoft.com</owner>
   <summary>
@@ -278,7 +278,7 @@
 </histogram>
 
 <histogram name="Accessibility.AutoDisabled.DisabledTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>abigailbklein@google.com</owner>
   <owner>chrome-a11y-core@google.com</owner>
   <summary>
@@ -298,7 +298,7 @@
 </histogram>
 
 <histogram name="Accessibility.AutoDisabled.EventCount" units="count"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>abigailbklein@google.com</owner>
   <owner>chrome-a11y-core@google.com</owner>
   <summary>
@@ -978,7 +978,7 @@
 </histogram>
 
 <histogram name="Accessibility.InactiveTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>aleventhal@chromium.org</owner>
   <owner>abigailbklein@chromium.org</owner>
   <owner>janewman@microsoft.com</owner>
@@ -1313,7 +1313,7 @@
 </histogram>
 
 <histogram name="Accessibility.ManuallyEnabled" enum="BooleanEnabled"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>aleventhal@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
   <owner>chrome-a11y-core@google.com</owner>
@@ -1932,7 +1932,7 @@
 </histogram>
 
 <histogram name="DomDistiller.Time.ViewingReaderModePage" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>mdjones@chromium.org</owner>
   <summary>
     Records the amount of time a user spent on a Reader Mode Page.
@@ -2008,7 +2008,7 @@
 </histogram>
 
 <histogram name="TextToSpeech.Utterance.HasVoiceName"
-    enum="TextToSpeechHasVoiceName" expires_after="2022-04-17">
+    enum="TextToSpeechHasVoiceName" expires_after="2022-06-19">
   <owner>katie@chromium.org</owner>
   <summary>
     True if an utterance spoken via synthesized text-to-speech requested a
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index f21ece6..746df0f 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -117,7 +117,7 @@
 </variants>
 
 <histogram name="Android.AdaptiveToolbarButton.Clicked"
-    enum="AdaptiveToolbarButtonVariant" expires_after="2022-04-17">
+    enum="AdaptiveToolbarButtonVariant" expires_after="2022-06-19">
   <owner>bttk@chromium.org</owner>
   <owner>chrome-segmentation-platform@google.com</owner>
   <summary>
@@ -127,7 +127,7 @@
 </histogram>
 
 <histogram name="Android.AdaptiveToolbarButton.SessionVariant"
-    enum="AdaptiveToolbarButtonVariant" expires_after="2022-04-17">
+    enum="AdaptiveToolbarButtonVariant" expires_after="2022-06-19">
   <owner>bttk@chromium.org</owner>
   <owner>chrome-segmentation-platform@google.com</owner>
   <summary>
@@ -171,7 +171,7 @@
 </histogram>
 
 <histogram name="Android.AdaptiveToolbarButton.SettingsToggle.Startup"
-    enum="BooleanEnabled" expires_after="2022-04-17">
+    enum="BooleanEnabled" expires_after="2022-06-19">
   <owner>shaktisahu@chromium.org</owner>
   <owner>chrome-segmentation-platform@google.com</owner>
   <summary>
@@ -194,7 +194,7 @@
 </histogram>
 
 <histogram name="Android.AppLaunch.DurationDrawWasBlocked" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>sinansahin@google.com</owner>
   <owner>twellington@chromium.org</owner>
   <owner>skym@chromium.org</owner>
@@ -629,7 +629,7 @@
 </histogram>
 
 <histogram name="Android.ChromeStartupDelegate.FailureReason"
-    enum="ChromeStartupDelegateFailureType" expires_after="2022-04-17">
+    enum="ChromeStartupDelegateFailureType" expires_after="2022-06-19">
   <owner>gangwu@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -808,7 +808,7 @@
 </histogram>
 
 <histogram name="Android.DarkTheme.EnabledReason" enum="DarkThemeEnabledReason"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>twellington@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
   <summary>
@@ -818,7 +818,7 @@
 </histogram>
 
 <histogram name="Android.DarkTheme.EnabledState" enum="BooleanEnabled"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>twellington@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
   <summary>
@@ -828,7 +828,7 @@
 </histogram>
 
 <histogram name="Android.DarkTheme.Preference.State"
-    enum="DarkThemePreferences" expires_after="2022-04-17">
+    enum="DarkThemePreferences" expires_after="2022-06-19">
   <owner>twellington@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
   <summary>
@@ -2300,7 +2300,7 @@
 </histogram>
 
 <histogram name="Android.Omnibox.SuggestionList.LayoutTime"
-    units="microseconds" expires_after="2022-04-17">
+    units="microseconds" expires_after="2022-06-19">
   <owner>ender@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
@@ -2327,7 +2327,7 @@
 </histogram>
 
 <histogram name="Android.Omnibox.SuggestionList.MeasureTime"
-    units="microseconds" expires_after="2022-04-17">
+    units="microseconds" expires_after="2022-06-19">
   <owner>ender@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
@@ -2355,7 +2355,7 @@
 </histogram>
 
 <histogram name="Android.Omnibox.SuggestionView.CreateTime"
-    units="microseconds" expires_after="2022-04-17">
+    units="microseconds" expires_after="2022-06-19">
   <owner>ender@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
@@ -2378,7 +2378,7 @@
 </histogram>
 
 <histogram name="Android.Omnibox.SuggestionView.Reused" enum="BooleanReused"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>ender@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
@@ -2724,7 +2724,7 @@
 </histogram>
 
 <histogram name="Android.PlayServices.Installed" enum="BooleanInstalled"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>twellington@chromium.org</owner>
   <owner>tedchoc@chromium.org</owner>
   <summary>
@@ -2734,7 +2734,7 @@
 </histogram>
 
 <histogram name="Android.PlayServices.Version" units="versioncode"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>twellington@chromium.org</owner>
   <owner>tedchoc@chromium.org</owner>
   <summary>
@@ -2807,7 +2807,7 @@
 </histogram>
 
 <histogram name="Android.Rotation.BeginToRendererFrameActivation" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jonross@chromium.org</owner>
   <owner>sadrul@chromium.org</owner>
   <summary>
@@ -3302,7 +3302,7 @@
 </histogram>
 
 <histogram name="Android.ThemeColor" enum="Android.ThemeColor"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>sinansahin@google.com</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -3436,13 +3436,32 @@
 </histogram>
 
 <histogram name="Android.WebView.AndroidX.ApiCall" enum="AndroidXWebkitApiCall"
-    expires_after="2022-01-31">
+    expires_after="2022-12-20">
   <owner>ntfschr@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
     Records calls to WebView APIs from AndroidX. Some AndroidX-originating calls
-    will plumb through the framework, for example, if methods that are available
-    in the framework are called from the equivalent method in AndroidX.
+    may plumb through the corresponding platform (framework) API if the device
+    OS version supports the platform API. In this case the API call will be
+    logged by the &quot;Android.WebView.ApiCall&quot; histogram instead. This is
+    logged each time an AndroidX API is called through the &quot;boundary
+    interface&quot; code path (see //android_webview/support_library/README.md
+    for more details).
+  </summary>
+</histogram>
+
+<histogram name="Android.WebView.ApiCall" enum="WebViewApiCall"
+    expires_after="2022-12-20">
+  <owner>ntfschr@chromium.org</owner>
+  <owner>src/android_webview/OWNERS</owner>
+  <summary>
+    Records calls to WebView APIs through the platform (framework) APIs. This
+    may include AndroidX API calls on OS versions where the AndroidX library
+    just calls through the platform API (see
+    &quot;Android.WebView.AndroidX.ApiCall&quot; for details). This is logged
+    each time an API is called.
+
+    For historical data (before M99), see &quot;WebView.ApiCall&quot;.
   </summary>
 </histogram>
 
@@ -4333,10 +4352,13 @@
   <owner>src/android_webview/OWNERS</owner>
   <summary>
     This captures two pieces of information about renderer crashes: did the app
-    developer handle the renderer crash to recover, and if not, what type of
-    crash was this? This is recorded once each time the renderer process dies
+    developer handle the renderer crash to recover and what type of crash was
+    this? This is recorded once each time the renderer process dies
     unexpectedly, and is logged after we have notified the app via the
     onRenderProcessGone() callback.
+
+    Prior to M99, this histogram did not distinguish between crashes vs. kills
+    if the renderer crash was handled by the app.
   </summary>
 </histogram>
 
@@ -4661,7 +4683,7 @@
 </histogram>
 
 <histogram name="Android.WebView.Startup.InitType"
-    enum="AndroidWebViewInitType" expires_after="2021-12-21">
+    enum="AndroidWebViewInitType" expires_after="2022-06-21">
   <owner>torne@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index da8bb12..8f8bf67e 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -155,7 +155,12 @@
       Base histogram. Use suffixes of this histogram instead.
     </obsolete>
   </variant>
-  <variant name=".AtFillTime" summary="Recorded at filling time."/>
+  <variant name=".AtFillTimeAfterSecurityPolicy"
+      summary="Recorded at filling time after applying restrictions due to
+               the cross-frame security policy."/>
+  <variant name=".AtFillTimeBeforeSecurityPolicy"
+      summary="Recorded at filling time before applying restrictions due to
+               the cross-frame security policy."/>
   <variant name=".AtSubmissionTime"
       summary="Recorded at submission time. May be missed due to submission
                detection problems."/>
@@ -2418,7 +2423,7 @@
 </histogram>
 
 <histogram name="Autofill.ProfileImport.NewProfileDecision"
-    enum="AutofillProfileImportDecision" expires_after="2022-04-17">
+    enum="AutofillProfileImportDecision" expires_after="2022-06-19">
   <owner>koerber@google.com</owner>
   <owner>src/components/autofill/OWNERS</owner>
   <summary>
@@ -2451,7 +2456,7 @@
 </histogram>
 
 <histogram name="Autofill.ProfileImport.ProfileImportType"
-    enum="AutofillProfileImportType" expires_after="2022-04-17">
+    enum="AutofillProfileImportType" expires_after="2022-06-19">
   <owner>koerber@google.com</owner>
   <owner>src/components/autofill/OWNERS</owner>
   <summary>
@@ -2473,7 +2478,7 @@
 </histogram>
 
 <histogram name="Autofill.ProfileImport.UpdateProfileAffectedType"
-    enum="AutofillSettingsVisibleTypes" expires_after="2022-04-17">
+    enum="AutofillSettingsVisibleTypes" expires_after="2022-06-19">
   <owner>koerber@google.com</owner>
   <owner>src/components/autofill/OWNERS</owner>
   <summary>
@@ -2528,7 +2533,7 @@
 </histogram>
 
 <histogram name="Autofill.ProfileImport.UpdateProfileDecision"
-    enum="AutofillProfileImportDecision" expires_after="2022-04-17">
+    enum="AutofillProfileImportDecision" expires_after="2022-06-19">
   <owner>koerber@google.com</owner>
   <owner>src/components/autofill/OWNERS</owner>
   <summary>
@@ -2552,7 +2557,7 @@
 </histogram>
 
 <histogram name="Autofill.ProfileImport.UpdateProfileNumberOfAffectedFields"
-    units="fields" expires_after="2022-04-17">
+    units="fields" expires_after="2022-06-19">
   <owner>koerber@google.com</owner>
   <owner>src/components/autofill/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 77ee2043..ec5e050 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -990,7 +990,7 @@
 </histogram>
 
 <histogram name="Blink.ContextMenu.ImageSelection.RetrievalOutcome"
-    enum="ImageSelectionRetrievalOutcome" expires_after="2022-04-17">
+    enum="ImageSelectionRetrievalOutcome" expires_after="2022-06-19">
   <owner>benwgold@google.com</owner>
   <owner>flackr@chromium.org</owner>
   <summary>
@@ -1143,7 +1143,7 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.EffectiveZoom" units="%" expires_after="2022-04-17">
+<histogram name="Blink.EffectiveZoom" units="%" expires_after="2022-06-19">
   <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
@@ -3084,7 +3084,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.TimeSmsReceive" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>yigu@chromium.org</owner>
   <owner>goto@chromium.org</owner>
   <owner>web-identity@google.com</owner>
@@ -3325,7 +3325,7 @@
 </histogram>
 
 <histogram name="Blink.UseCounter.FeaturePolicy.Header"
-    enum="FeaturePolicyFeature" expires_after="2022-04-17">
+    enum="FeaturePolicyFeature" expires_after="2022-06-19">
   <owner>iclelland@chromium.org</owner>
   <owner>feature-control@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/browser/histograms.xml b/tools/metrics/histograms/metadata/browser/histograms.xml
index 2c9fce6..b8d8fe8a 100644
--- a/tools/metrics/histograms/metadata/browser/histograms.xml
+++ b/tools/metrics/histograms/metadata/browser/histograms.xml
@@ -498,7 +498,7 @@
 </histogram>
 
 <histogram name="Browser.PaintPreview.TabbedPlayer.HadCapture" units="Boolean"
-    expires_after="2022-04-10">
+    expires_after="2022-06-19">
   <owner>ckitagawa@chromium.org</owner>
   <owner>yashard@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
@@ -576,7 +576,7 @@
 </histogram>
 
 <histogram name="Browser.Responsiveness.IOJanksTotalPerMinute" units="janks"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>gab@chromium.org</owner>
   <owner>olivierli@chromium.org</owner>
   <summary>
@@ -910,7 +910,7 @@
 </histogram>
 
 <histogram name="Browser.WindowCount.Guest" units="units"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
   <summary>
@@ -922,7 +922,7 @@
 </histogram>
 
 <histogram name="Browser.WindowCount.Incognito" units="units"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/compositing/histograms.xml b/tools/metrics/histograms/metadata/compositing/histograms.xml
index c815f65e..ecc4bc5 100644
--- a/tools/metrics/histograms/metadata/compositing/histograms.xml
+++ b/tools/metrics/histograms/metadata/compositing/histograms.xml
@@ -101,7 +101,7 @@
 </histogram>
 
 <histogram name="Compositing.Browser.NumActivePictureLayers" units="layers"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
@@ -112,7 +112,7 @@
 </histogram>
 
 <histogram name="Compositing.Browser.NumRenderSurfaces" units="surfaces"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
@@ -198,7 +198,7 @@
 </histogram>
 
 <histogram name="Compositing.DirectRenderer.PartialSwap.FrameBufferDamage"
-    units="%" expires_after="2022-04-17">
+    units="%" expires_after="2022-06-19">
   <owner>vasilyt@chromium.org</owner>
   <owner>backer@chromium.org</owner>
   <summary>
@@ -212,7 +212,7 @@
 </histogram>
 
 <histogram name="Compositing.DirectRenderer.PartialSwap.RootDamage" units="%"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>vasilyt@chromium.org</owner>
   <owner>backer@chromium.org</owner>
   <summary>
@@ -224,7 +224,7 @@
 </histogram>
 
 <histogram name="Compositing.DirectRenderer.PartialSwap.TotalDamage" units="%"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>vasilyt@chromium.org</owner>
   <owner>backer@chromium.org</owner>
   <summary>
@@ -730,7 +730,7 @@
 </histogram>
 
 <histogram name="Compositing.SurfaceAggregator.CopyUs" units="microseconds"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>kylechar@chromium.org</owner>
   <owner>jonross@chromium.org</owner>
   <summary>
@@ -744,7 +744,7 @@
 </histogram>
 
 <histogram name="Compositing.SurfaceAggregator.DeclareResourceCount"
-    units="resources" expires_after="2022-04-17">
+    units="resources" expires_after="2022-06-19">
   <owner>kylechar@chromium.org</owner>
   <owner>jonross@chromium.org</owner>
   <summary>
@@ -847,7 +847,7 @@
 </histogram>
 
 <histogram base="true" name="CompositorLatency.CompositorOnlyFrame"
-    units="microseconds" expires_after="2022-04-17">
+    units="microseconds" expires_after="2022-06-19">
   <owner>sadrul@chromium.org</owner>
   <owner>graphics-dev@chromium.org</owner>
   <summary>
@@ -963,7 +963,7 @@
 </histogram>
 
 <histogram name="Graphics.Smoothness.95pctPercentDroppedFrames_1sWindow"
-    units="%" expires_after="2022-04-17">
+    units="%" expires_after="2022-06-19">
   <owner>behdadb@chromium.org</owner>
   <owner>sadrul@chromium.org</owner>
   <summary>
@@ -1001,7 +1001,7 @@
 </histogram>
 
 <histogram name="Graphics.Smoothness.Diagnostic.DiscardedDependentCount"
-    units="dependent reporters" expires_after="2022-04-17">
+    units="dependent reporters" expires_after="2022-06-19">
   <owner>sadrul@chromium.org</owner>
   <owner>behdadb@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/content/histograms.xml b/tools/metrics/histograms/metadata/content/histograms.xml
index 7626f40..2ded457 100644
--- a/tools/metrics/histograms/metadata/content/histograms.xml
+++ b/tools/metrics/histograms/metadata/content/histograms.xml
@@ -296,7 +296,7 @@
 </histogram>
 
 <histogram name="ContentSettings.DefaultRequestDesktopSiteSetting"
-    enum="ContentSetting" expires_after="2022-04-01">
+    enum="ContentSetting" expires_after="2022-06-19">
   <owner>shuyng@google.com</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -453,7 +453,7 @@
 </histogram>
 
 <histogram name="ContentSettings.PermissionRequested" enum="PermissionType"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>engedy@chromium.org</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
   <summary>
@@ -961,7 +961,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.Network.Duration" units="ms"
-    expires_after="2022-06-12">
+    expires_after="2022-06-19">
   <owner>carlosk@chromium.org</owner>
   <owner>harringtond@chromium.org</owner>
   <owner>feed@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/content_creation/histograms.xml b/tools/metrics/histograms/metadata/content_creation/histograms.xml
index 11f5b7c1..55618af 100644
--- a/tools/metrics/histograms/metadata/content_creation/histograms.xml
+++ b/tools/metrics/histograms/metadata/content_creation/histograms.xml
@@ -366,7 +366,7 @@
 </histogram>
 
 <histogram name="SharedHighlights.LinkGenerated.Iterations" units="iterations"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>gayane@chromium.org</owner>
   <owner>chrome-shared-highlighting@google.com</owner>
   <summary>
@@ -398,7 +398,7 @@
 </histogram>
 
 <histogram name="SharedHighlights.LinkGenerated.SelectorParameters"
-    enum="TextFragmentAnchorParameters" expires_after="2022-04-17">
+    enum="TextFragmentAnchorParameters" expires_after="2022-06-19">
   <owner>gayane@chromium.org</owner>
   <owner>chrome-shared-highlighting@google.com</owner>
   <summary>
@@ -447,7 +447,7 @@
 </histogram>
 
 <histogram name="SharedHighlights.LinkToTextDiagnoseStatus"
-    enum="LinkToTextDiagnoseStatus" expires_after="2022-04-17">
+    enum="LinkToTextDiagnoseStatus" expires_after="2022-06-19">
   <owner>gayane@chromium.org</owner>
   <owner>chrome-shared-highlighting@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cookie/histograms.xml b/tools/metrics/histograms/metadata/cookie/histograms.xml
index aa743a6..43e86d4 100644
--- a/tools/metrics/histograms/metadata/cookie/histograms.xml
+++ b/tools/metrics/histograms/metadata/cookie/histograms.xml
@@ -114,7 +114,7 @@
 </histogram>
 
 <histogram name="Cookie.CookieSourceSchemeName" enum="CookieSourceSchemeName"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>bingler@chromium.org</owner>
   <owner>miketaylr@chromium.org</owner>
   <summary>
@@ -195,7 +195,7 @@
   </token>
 </histogram>
 
-<histogram name="Cookie.DomainSet" enum="Boolean" expires_after="2022-04-17">
+<histogram name="Cookie.DomainSet" enum="Boolean" expires_after="2022-06-19">
   <owner>bingler@chromium.org</owner>
   <owner>miketaylr@chromium.org</owner>
   <summary>
@@ -477,7 +477,7 @@
   </summary>
 </histogram>
 
-<histogram name="Cookie.NumKeys" units="keys" expires_after="2022-04-17">
+<histogram name="Cookie.NumKeys" units="keys" expires_after="2022-06-19">
   <owner>cfredric@chromium.org</owner>
   <owner>kaustubhag@chromium.org</owner>
   <summary>
@@ -521,7 +521,7 @@
 </histogram>
 
 <histogram name="Cookie.PerFirstPartySetCount" units="cookies"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>cfredric@chromium.org</owner>
   <owner>bingler@chromium.org</owner>
   <summary>
@@ -670,7 +670,7 @@
 </histogram>
 
 <histogram name="Cookie.RequestSameSiteContext" enum="SameSiteCookieContext"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>bingler@chromium.org</owner>
   <owner>morlovich@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cras/histograms.xml b/tools/metrics/histograms/metadata/cras/histograms.xml
index 417f346..d8c6a29f 100644
--- a/tools/metrics/histograms/metadata/cras/histograms.xml
+++ b/tools/metrics/histograms/metadata/cras/histograms.xml
@@ -23,7 +23,7 @@
 <histograms>
 
 <histogram name="Cras.A2dp100msFailureOverStream" units="units"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>hychao@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cross_device/histograms.xml b/tools/metrics/histograms/metadata/cross_device/histograms.xml
index 7659ddf5..3de0d6d 100644
--- a/tools/metrics/histograms/metadata/cross_device/histograms.xml
+++ b/tools/metrics/histograms/metadata/cross_device/histograms.xml
@@ -1593,7 +1593,7 @@
 </histogram>
 
 <histogram name="MultiDevice.BetterTogetherSuite.MultiDeviceFeatureState"
-    enum="MultiDevice_FeatureState" expires_after="2022-04-17">
+    enum="MultiDevice_FeatureState" expires_after="2022-06-19">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1748,7 +1748,7 @@
 
 <histogram
     name="MultiDevice.SecureChannel.BLE.Performance.StartScanToAuthenticationDuration.Background"
-    units="ms" expires_after="2022-04-17">
+    units="ms" expires_after="2022-06-19">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1781,7 +1781,7 @@
 
 <histogram
     name="MultiDevice.SecureChannel.BLE.Performance.StartScanToReceiveAdvertisementDuration.Background"
-    units="ms" expires_after="2022-04-17">
+    units="ms" expires_after="2022-06-19">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1969,7 +1969,7 @@
 </histogram>
 
 <histogram name="MultiDevice.Setup.HostStatus"
-    enum="MultiDevice_Setup_HostStatus" expires_after="2022-04-17">
+    enum="MultiDevice_Setup_HostStatus" expires_after="2022-06-19">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/crostini/OWNERS b/tools/metrics/histograms/metadata/crostini/OWNERS
index 33d577b..e5d00c7 100644
--- a/tools/metrics/histograms/metadata/crostini/OWNERS
+++ b/tools/metrics/histograms/metadata/crostini/OWNERS
@@ -4,3 +4,4 @@
 # Use chromium-metrics-reviews@google.com as a backup.
 davidmunro@google.com
 hollingum@google.com
+danielng@google.com
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
index 1c7f816..422df07 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -122,7 +122,7 @@
 </histogram>
 
 <histogram name="CustomTabs.DetachedResourceRequest.FinalStatus"
-    enum="NetErrorCodes" expires_after="2022-04-17">
+    enum="NetErrorCodes" expires_after="2022-06-19">
   <owner>lizeb@chromium.org</owner>
   <owner>cct-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/data/histograms.xml b/tools/metrics/histograms/metadata/data/histograms.xml
index 1dbab2e..a648159e 100644
--- a/tools/metrics/histograms/metadata/data/histograms.xml
+++ b/tools/metrics/histograms/metadata/data/histograms.xml
@@ -582,7 +582,7 @@
 </histogram>
 
 <histogram name="DataUse.BytesReceived2" units="bytes"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/download/histograms.xml b/tools/metrics/histograms/metadata/download/histograms.xml
index a3db467f..094365b 100644
--- a/tools/metrics/histograms/metadata/download/histograms.xml
+++ b/tools/metrics/histograms/metadata/download/histograms.xml
@@ -136,7 +136,7 @@
 </histogram>
 
 <histogram name="Download.DangerousDialog.Events"
-    enum="DangerousDownloadDialogEvent" expires_after="2022-04-17">
+    enum="DangerousDownloadDialogEvent" expires_after="2022-06-19">
   <owner>qinmin@chromium.org</owner>
   <owner>xingliu@chromium.org</owner>
   <summary>
@@ -1175,7 +1175,7 @@
 </histogram>
 
 <histogram name="Download.Shelf.WebUI.LoadDocumentTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>kerenzhu@chromium.org</owner>
   <owner>robliao@chromium.org</owner>
   <owner>romanarora@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml
index 2030bccc..993c3e73 100644
--- a/tools/metrics/histograms/metadata/enterprise/histograms.xml
+++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -513,7 +513,7 @@
 </histogram>
 
 <histogram name="Enterprise.CloudReportingBasicRequestSize" units="KB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>zmin@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
@@ -544,7 +544,7 @@
 </histogram>
 
 <histogram name="Enterprise.CloudReportingResponse"
-    enum="EnterpriseCloudReportingResponse" expires_after="2022-04-17">
+    enum="EnterpriseCloudReportingResponse" expires_after="2022-06-19">
   <owner>zmin@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
@@ -1391,8 +1391,8 @@
 
 <histogram name="Enterprise.EnterCriticalPolicySectionDelay.Total" units="ms"
     expires_after="2022-06-12">
-  <owner>grt@chromium.org</owner>
   <owner>zmin@chromium.org</owner>
+  <owner>grt@chromium.org</owner>
   <summary>
     Elapsed time taken to acquire each lock to access policies in the Windows
     registry (at user scope and then at machine scope). This metric is recorded
@@ -1402,9 +1402,9 @@
 </histogram>
 
 <histogram name="Enterprise.EnterCriticalPolicySectionDelay.{Scope}.{Result}"
-    units="ms" expires_after="2021-12-31">
-  <owner>grt@chromium.org</owner>
+    units="ms" expires_after="2022-06-12">
   <owner>zmin@chromium.org</owner>
+  <owner>grt@chromium.org</owner>
   <summary>
     Elapsed time taken to acquire the lock to access policies in the Windows
     registry. The success or failure variant of this metric is recorded once for
@@ -1422,8 +1422,8 @@
 
 <histogram name="Enterprise.EnterCriticalPolicySectionError"
     enum="WinGetLastError" expires_after="2022-06-12">
-  <owner>grt@chromium.org</owner>
   <owner>zmin@chromium.org</owner>
+  <owner>grt@chromium.org</owner>
   <summary>
     Windows error code following a failure to acquire the lock to access
     policies in the Windows registry shortly after browser startup.
@@ -1990,7 +1990,7 @@
 </histogram>
 
 <histogram name="Enterprise.PolicyUserVerification"
-    enum="EnterprisePolicyUserVerification" expires_after="2022-04-17">
+    enum="EnterprisePolicyUserVerification" expires_after="2022-06-19">
   <owner>poromov@chromium.org</owner>
   <owner>chromeos-commercial-remote-management@google.com</owner>
   <summary>Tracking the results of policy user verification.</summary>
@@ -2595,7 +2595,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.IsDomainJoined" enum="BooleanEnabled"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>pastarmovj@chromium.org</owner>
   <owner>rogerta@chromium.org</owner>
   <owner>zmin@chromium.org</owner>
@@ -2606,7 +2606,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.IsEnterpriseUser" enum="BooleanEnabled"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>pastarmovj@chromium.org</owner>
   <owner>rogerta@chromium.org</owner>
   <owner>zmin@chromium.org</owner>
@@ -2618,7 +2618,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.IsFullyManaged2" enum="IsFullyManagedBoolean"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>twellington@google.com</owner>
   <owner>tedchcoc@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/event/histograms.xml b/tools/metrics/histograms/metadata/event/histograms.xml
index 2d3dc526..86e5be6 100644
--- a/tools/metrics/histograms/metadata/event/histograms.xml
+++ b/tools/metrics/histograms/metadata/event/histograms.xml
@@ -1271,7 +1271,7 @@
 
 <histogram
     name="Event.Latency.ScrollInertial.Touch.BrowserNotifiedToBeforeGpuSwap2"
-    units="microseconds" expires_after="2022-04-17">
+    units="microseconds" expires_after="2022-06-19">
   <owner>flackr@chromium.org</owner>
   <owner>input-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml
index 8aceccd5..1f7d415 100644
--- a/tools/metrics/histograms/metadata/extensions/histograms.xml
+++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -61,7 +61,7 @@
 </histogram>
 
 <histogram name="Extensions.ActiveScriptController.DeniedExtensions"
-    units="Extension Count" expires_after="2022-04-17">
+    units="Extension Count" expires_after="2022-06-19">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -71,7 +71,7 @@
 </histogram>
 
 <histogram name="Extensions.ActiveScriptController.PermittedExtensions"
-    units="Extension Count" expires_after="2022-04-17">
+    units="Extension Count" expires_after="2022-06-19">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -577,7 +577,7 @@
 </histogram>
 
 <histogram name="Extensions.CorruptExtensionDisabledReason"
-    enum="CorruptExtensionDisabledReason" expires_after="2021-09-21">
+    enum="CorruptExtensionDisabledReason" expires_after="2022-12-01">
   <owner>lazyboy@chromium.org</owner>
   <owner>rockot@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
@@ -665,6 +665,7 @@
     <variant name=".Scripts" summary="Scripts backing stores"/>
     <variant name=".Settings" summary="Settings backing stores"/>
     <variant name=".State" summary="State backing stores"/>
+    <variant name=".WebAppsLockScreen" summary="Web apps lock screen stores"/>
   </token>
 </histogram>
 
@@ -712,7 +713,7 @@
 </histogram>
 
 <histogram name="Extensions.DeclarativeNetRequest.CreateVerifiedMatcherTime"
-    units="ms" expires_after="2022-04-17">
+    units="ms" expires_after="2022-06-19">
   <owner>kelvinjiang@chromium.org</owner>
   <owner>src/extensions/OWNERS</owner>
   <summary>
@@ -1382,7 +1383,7 @@
 </histogram>
 
 <histogram name="Extensions.ExtensionReenabledRemotely" units="count"
-    expires_after="2022-04-10">
+    expires_after="2022-06-19">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -2075,7 +2076,7 @@
 </histogram>
 
 <histogram name="Extensions.Functions.FailedTotalExecutionTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -2135,7 +2136,7 @@
 </histogram>
 
 <histogram name="Extensions.Functions.SucceededTotalExecutionTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -2148,7 +2149,7 @@
 </histogram>
 
 <histogram name="Extensions.Functions.SynchronousExecutionTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
index cb109d2..142a998 100644
--- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
+++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -128,6 +128,17 @@
   <variant name="IPH_EphemeralTab"
       summary="new label on the context menu for Ephemeral Tab"/>
   <variant name="IPH_ExploreSitesTile" summary="Explore Sites feature"/>
+  <variant name="IPH_FeatureNotificationGuideDefaultBrowserNotificationShown"
+      summary="feature notification guide default browser notification"/>
+  <variant name="IPH_FeatureNotificationGuideIncognitoTabNotificationShown"
+      summary="feature notification guide incognito tab notification"/>
+  <variant
+      name="IPH_FeatureNotificationGuideNTPSuggestionCardNotificationShown"
+      summary="feature notification guide NTP suggestion card notification"/>
+  <variant name="IPH_FeatureNotificationGuideSignInNotificationShown"
+      summary="feature notification guide sign in notification"/>
+  <variant name="IPH_FeatureNotificationGuideVoiceSearchNotificationShown"
+      summary="feature notification guide voice search notification"/>
   <variant name="IPH_FeedCardMenu" summary="feed card menu on NTP"/>
   <variant name="IPH_FeedHeaderMenu" summary="feed header menu on NTP"/>
   <variant name="IPH_FeedSwipeRefresh" summary="swipe refreshing feeds on NTP"/>
diff --git a/tools/metrics/histograms/metadata/gpu/histograms.xml b/tools/metrics/histograms/metadata/gpu/histograms.xml
index 0b57d41..24670d8 100644
--- a/tools/metrics/histograms/metadata/gpu/histograms.xml
+++ b/tools/metrics/histograms/metadata/gpu/histograms.xml
@@ -308,7 +308,7 @@
   </summary>
 </histogram>
 
-<histogram name="GPU.ANGLE.D3DCompileMS" units="ms" expires_after="2022-04-17">
+<histogram name="GPU.ANGLE.D3DCompileMS" units="ms" expires_after="2022-06-19">
   <owner>jonahr@google.com</owner>
   <owner>angle-team@google.com</owner>
   <summary>
@@ -727,7 +727,7 @@
 </histogram>
 
 <histogram base="true" name="GPU.DirectComposition.DCLayerResult.Video"
-    enum="DCLayerResult" expires_after="2022-04-17">
+    enum="DCLayerResult" expires_after="2022-06-19">
 <!-- Name completed by histogram_suffixes name="GPU.ProtectedVideoType" -->
 
   <owner>magchen@chromium.org</owner>
@@ -1674,7 +1674,7 @@
 </histogram>
 
 <histogram name="GPU.SharedImage.ContentConsumed" enum="BooleanMatched"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>penghuang@chromium.org</owner>
   <owner>backer@chromium.org</owner>
   <summary>
@@ -1946,7 +1946,7 @@
 </histogram>
 
 <histogram name="GPU.WatchdogThread.Timeout" enum="GpuWatchdogTimeoutEvent"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
 <!-- Name completed by histogram_suffixes name="GPU.WatchdogStage" -->
 
   <owner>magchen@chromium.org</owner>
@@ -2106,7 +2106,7 @@
 </histogram>
 
 <histogram name="Viz.DisplayCompositor.OverlayNumProposedCandidates"
-    units="units" expires_after="2022-04-17">
+    units="units" expires_after="2022-06-19">
   <owner>petermcneeley@chromium.org</owner>
   <owner>dcastagna@chromium.org</owner>
   <summary>
@@ -2117,7 +2117,7 @@
 </histogram>
 
 <histogram name="Viz.DisplayCompositor.OverlayQuadMaterial"
-    enum="OverlayQuadMaterial" expires_after="2022-04-17">
+    enum="OverlayQuadMaterial" expires_after="2022-06-19">
   <owner>petermcneeley@chromium.org</owner>
   <owner>dcastagna@chromium.org</owner>
   <summary>
@@ -2147,7 +2147,7 @@
 </histogram>
 
 <histogram name="Viz.DisplayCompositor.RootDamageRect.Overlay"
-    enum="BooleanOverlayDamageRect" expires_after="2022-04-17">
+    enum="BooleanOverlayDamageRect" expires_after="2022-06-19">
   <owner>magchen@chromium.org</owner>
   <owner>zmo@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml
index 7c2307f..0743860a 100644
--- a/tools/metrics/histograms/metadata/history/histograms.xml
+++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -375,7 +375,7 @@
 </histogram>
 
 <histogram name="History.ClearBrowsingData.UserDeletedFromTab"
-    enum="ClearBrowsingDataTab" expires_after="2022-04-17">
+    enum="ClearBrowsingDataTab" expires_after="2022-06-19">
   <owner>dullweber@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
   <summary>
@@ -770,7 +770,7 @@
 </histogram>
 
 <histogram name="History.DomainCount1Day" units="domains"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>mpearson@chromium.org</owner>
   <owner>mjzhang@chromium.org</owner>
   <owner>chrome-analysis-team@google.com</owner>
@@ -808,7 +808,7 @@
 </histogram>
 
 <histogram name="History.DomainCount28Day" units="domains"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>mpearson@chromium.org</owner>
   <owner>mjzhang@chromium.org</owner>
   <owner>chrome-analysis-team@google.com</owner>
@@ -847,7 +847,7 @@
 </histogram>
 
 <histogram name="History.DomainCount7Day" units="domains"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>mpearson@chromium.org</owner>
   <owner>mjzhang@chromium.org</owner>
   <owner>chrome-analysis-team@google.com</owner>
@@ -1282,7 +1282,7 @@
 </histogram>
 
 <histogram name="History.WeeklyHostCount" units="hosts"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>mpearson@chromium.org</owner>
   <owner>sky@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/ios/OWNERS b/tools/metrics/histograms/metadata/ios/OWNERS
index fce30ef4..37ff0ca 100644
--- a/tools/metrics/histograms/metadata/ios/OWNERS
+++ b/tools/metrics/histograms/metadata/ios/OWNERS
@@ -3,5 +3,4 @@
 # Prefer sending CLs to the owners listed below.
 # Use chromium-metrics-reviews@google.com as a backup.
 ajuma@chromium.org
-javierrobles@chromium.org
 olivierrobin@chromium.org
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index 138dec2..c1536f3 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -347,7 +347,7 @@
 </histogram>
 
 <histogram name="IOS.DefaultBrowserFullscreenPromoRemindMeSecondPromo"
-    enum="IOSDefaultBrowserFullscreenPromoAction" expires_after="2022-04-10">
+    enum="IOSDefaultBrowserFullscreenPromoAction" expires_after="2022-06-19">
   <owner>thegreenfrog@chromium.org</owner>
   <owner>rohitrao@chromium.org</owner>
   <summary>
@@ -358,8 +358,8 @@
 
 <histogram name="IOS.DefaultBrowserFullscreenTailoredPromoAllTabs"
     enum="IOSDefaultBrowserFullscreenPromoAction" expires_after="2022-06-05">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
   <summary>
     The action taken by the user in response to the &quot;All tabs&quot; default
     browser tailored promo.
@@ -368,8 +368,8 @@
 
 <histogram name="IOS.DefaultBrowserFullscreenTailoredPromoMadeForIOS"
     enum="IOSDefaultBrowserFullscreenPromoAction" expires_after="2022-06-05">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
   <summary>
     The action taken by the user in response to the &quot;Made for iOS&quot;
     default browser tailored promo.
@@ -378,8 +378,8 @@
 
 <histogram name="IOS.DefaultBrowserFullscreenTailoredPromoStaySafe"
     enum="IOSDefaultBrowserFullscreenPromoAction" expires_after="2022-06-05">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
   <summary>
     The action taken by the user in response to the &quot;Stay Safe&quot;
     default browser tailored promo.
@@ -387,9 +387,9 @@
 </histogram>
 
 <histogram name="IOS.DefaultBrowserPromo.NonModal.OnScreenTime" units="ms"
-    expires_after="2022-04-20">
-  <owner>javierrobles@chromium.org</owner>
+    expires_after="2022-06-19">
   <owner>rkgibson@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
   <summary>
     Logs the time on screen for an impression of a non modal promo. iOS only.
   </summary>
@@ -397,8 +397,8 @@
 
 <histogram name="IOS.DefaultBrowserPromo.NonModal.{Impression}"
     enum="IOSDefaultBrowserPromoNonModalAction" expires_after="2022-04-20">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
   <summary>
     Logged when an action happens on the {Impression} impression of a non modal
     promo. Records the action of the promo. iOS only.
@@ -411,8 +411,8 @@
 
 <histogram name="IOS.DefaultBrowserPromo.NonModal.{PromoType}"
     enum="IOSDefaultBrowserPromoNonModalAction" expires_after="2022-04-20">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
   <summary>
     Logged when an action happens on a non modal promo triggered by {PromoType}.
     Records the action of the promo. iOS only.
@@ -426,8 +426,8 @@
 
 <histogram name="IOS.DefaultBrowserPromo.TailoredFullscreen.{Action}"
     enum="IOSDefaultBrowserTailoredPromoType" expires_after="2022-04-20">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
   <summary>
     Logged when the {Action} action happens in a tailored default browser promo.
     Records the type of promo.
@@ -513,9 +513,9 @@
 </histogram>
 
 <histogram name="IOS.FormInputAccessory.ExecuteFormAssistActionException"
-    enum="FormInputAccessoryAction" expires_after="2021-12-11">
-  <owner>javierrobles@chromium.org</owner>
+    enum="FormInputAccessoryAction" expires_after="2022-12-11">
   <owner>rohitrao@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
   <summary>
     Reports exceptions when trying to send a form input accessory action to the
     virtual keyboard.
@@ -681,7 +681,6 @@
 <histogram name="IOS.IsDefaultBrowser" enum="Boolean" expires_after="never">
 <!-- expires-never: used internally for filtering -->
 
-  <owner>javierrobles@chromium.org</owner>
   <owner>thegreenfrog@chromium.org</owner>
   <owner>rohitrao@chromium.org</owner>
   <owner>chrome-metrics-team@google.com</owner>
@@ -701,7 +700,6 @@
 <histogram name="IOS.IsDefaultBrowser21" enum="Boolean" expires_after="never">
 <!-- expires-never: used internally for filtering -->
 
-  <owner>javierrobles@chromium.org</owner>
   <owner>rohitrao@chromium.org</owner>
   <owner>chrome-metrics-team@google.com</owner>
   <summary>
@@ -980,7 +978,7 @@
 </histogram>
 
 <histogram name="IOS.MultiWindow.Configuration"
-    enum="IOSMultiWindowConfiguration" expires_after="2021-12-11">
+    enum="IOSMultiWindowConfiguration" expires_after="2022-12-11">
   <owner>marq@chromium.org</owner>
   <owner>djean@chromium.org</owner>
   <summary>MultiWindow configuration sampled once per minute.</summary>
@@ -997,9 +995,9 @@
 </histogram>
 
 <histogram name="IOS.NSString.stringByReplacingCharactersInRange.NilArgument"
-    enum="Boolean" expires_after="2021-12-11">
-  <owner>javierrobles@chromium.org</owner>
+    enum="Boolean" expires_after="2022-12-11">
   <owner>rohitrao@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
   <summary>
     NSString API: stringByReplacingCharactersInRange:withString: was invoked
     with a nil argument.
@@ -1149,8 +1147,8 @@
 
 <histogram name="IOS.Reauth.Password.Autofill" enum="ReauthenticationEvent"
     expires_after="2022-05-01">
-  <owner>javierrobles@chromium.org</owner>
   <owner>sarraf@google.com</owner>
+  <owner>djean@chromium.org</owner>
   <summary>
     Tracks the results and attempts of reauthentication when using password
     Autofill suggestions.
@@ -1159,8 +1157,8 @@
 
 <histogram name="IOS.Reauth.Password.ManualFallback"
     enum="ReauthenticationEvent" expires_after="2022-05-01">
-  <owner>javierrobles@chromium.org</owner>
   <owner>sarraf@google.com</owner>
+  <owner>djean@chromium.org</owner>
   <summary>
     Tracks the results and attempts of reauthentication when using a password in
     Manual Fallback.
@@ -1491,7 +1489,7 @@
 
 <histogram name="IOS.WidgetKit.Action" enum="IOSWidgetKitAction"
     expires_after="2022-06-05">
-  <owner>javierrobles@chromium.org</owner>
+  <owner>rkgibson@google.com</owner>
   <owner>muradyan@google.com</owner>
   <summary>
     Measures UI interactions in WidgetKit extension. Recorded after a user taps
@@ -1500,8 +1498,8 @@
 </histogram>
 
 <histogram name="IOS.WidgetKit.{Status}" enum="IOSWidgetKitExtensionKind"
-    expires_after="2021-12-11">
-  <owner>javierrobles@chromium.org</owner>
+    expires_after="2022-12-11">
+  <owner>rkgibson@google.com</owner>
   <owner>muradyan@chromium.org</owner>
   <summary>
     Indicates the {Status} of an iOS 14 widget. Logged when the app goes to
@@ -1519,7 +1517,7 @@
 </histogram>
 
 <histogram name="IOS.WindowIDInjection.ElapsedTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>michaeldo@chromium.org</owner>
   <owner>rohitrao@chromium.org</owner>
   <summary>
@@ -1639,7 +1637,6 @@
 
 <histogram name="UserInterfaceStyle.CurrentlyUsed" enum="IOSUserInterfaceStyle"
     expires_after="2021-12-11">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@google.com</owner>
   <owner>bling-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/leveldb_proto/histograms.xml b/tools/metrics/histograms/metadata/leveldb_proto/histograms.xml
index 0e57e73..b593689 100644
--- a/tools/metrics/histograms/metadata/leveldb_proto/histograms.xml
+++ b/tools/metrics/histograms/metadata/leveldb_proto/histograms.xml
@@ -203,7 +203,7 @@
 </histogram>
 
 <histogram name="ProtoDB.SharedDbInitStatus" enum="ProtoDatabaseInitState"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>ssid@chromium.org</owner>
   <owner>salg@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index 5278b94b..d55dfc2 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -55,7 +55,7 @@
 </variants>
 
 <histogram name="Media.AImageReaderGLOwner.AcquireImageResult"
-    enum="MediaStatus" expires_after="2022-04-17">
+    enum="MediaStatus" expires_after="2022-06-19">
   <owner>vikassoni@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -104,7 +104,7 @@
 </histogram>
 
 <histogram name="Media.Android.MediaPlayerSuccess" enum="MediaPlayerExitStatus"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>tguilbert@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>Android: Whether MediaPlayer exited without errors.</summary>
@@ -269,7 +269,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.LostFramesInMs" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -281,7 +281,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.LowLatencyCallbackError"
-    enum="BooleanError" expires_after="2022-04-17">
+    enum="BooleanError" expires_after="2022-06-19">
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -657,7 +657,7 @@
 </histogram>
 
 <histogram name="Media.Audio.OutputDeviceAuthorizationTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>armax@chromium.org</owner>
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
@@ -847,7 +847,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Processing.TotalDelayMs" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>armax@chromium.org</owner>
   <owner>guidou@chromium.org</owner>
   <summary>
@@ -912,7 +912,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Render.GetSourceDataTimeMax.WebRTC"
-    units="microseconds" expires_after="2022-04-17">
+    units="microseconds" expires_after="2022-06-19">
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -951,7 +951,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Render.LostFramesInMs" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -1000,7 +1000,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Render.OutputDeviceStatus"
-    enum="OutputDeviceStatus" expires_after="2022-04-17">
+    enum="OutputDeviceStatus" expires_after="2022-06-19">
   <owner>dalecurtis@chromium.org</owner>
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
@@ -1252,7 +1252,7 @@
 </histogram>
 
 <histogram name="Media.AudioInputControllerSessionSilenceReport"
-    enum="AudioInputSilenceReport" expires_after="2022-04-17">
+    enum="AudioInputSilenceReport" expires_after="2022-06-19">
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -3260,7 +3260,7 @@
 </histogram>
 
 <histogram name="Media.Midi.SendReceiveUsage" enum="MidiSendReceiveUsage"
-    expires_after="M95">
+    expires_after="2022-06-01">
   <owner>toyoshim@chromium.org</owner>
   <owner>midi-dev@chromium.org</owner>
   <summary>
@@ -3274,7 +3274,7 @@
 </histogram>
 
 <histogram name="Media.Midi.SysExMessageSizeUpTo1MB" units="bytes"
-    expires_after="M95">
+    expires_after="2022-06-01">
   <owner>toyoshim@chromium.org</owner>
   <owner>midi-dev@chromium.org</owner>
   <summary>
@@ -3284,7 +3284,7 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Midi.Usage" enum="MidiUsage" expires_after="M95">
+<histogram name="Media.Midi.Usage" enum="MidiUsage" expires_after="2022-06-01">
   <owner>toyoshim@chromium.org</owner>
   <owner>midi-dev@chromium.org</owner>
   <summary>
@@ -3298,7 +3298,7 @@
 </histogram>
 
 <histogram name="Media.MojoVideoDecoder.ActiveInstances" units="units"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>sandersd@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -3387,7 +3387,7 @@
 </histogram>
 
 <histogram base="true" name="Media.MSE.CodecChangeTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>wolenetz@chromium.org</owner>
   <owner>sandersd@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
@@ -3409,7 +3409,7 @@
 </histogram>
 
 <histogram name="Media.MSE.DetectedShakaPackagerInMp4" enum="BooleanDetected"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -3430,7 +3430,7 @@
 </histogram>
 
 <histogram name="Media.MSE.Mp4ConsecutiveEmptySamples" units="samples"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>wolenetz@chromium.org</owner>
   <owner>sandersd@chromium.org</owner>
   <summary>
@@ -3454,7 +3454,7 @@
 </histogram>
 
 <histogram name="Media.MSE.Mp4SampleSize" units="bytes"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>wolenetz@chromium.org</owner>
   <owner>sandersd@chromium.org</owner>
   <summary>
@@ -3465,7 +3465,7 @@
 </histogram>
 
 <histogram name="Media.MSE.Mp4TrunSampleCount" units="samples"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>wolenetz@chromium.org</owner>
   <owner>sandersd@chromium.org</owner>
   <summary>
@@ -3582,7 +3582,7 @@
 </histogram>
 
 <histogram name="Media.Notification.Cast.UserAction" enum="MediaSessionAction"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>takumif@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <owner>media-dev@chromium.org</owner>
@@ -3646,7 +3646,7 @@
 </histogram>
 
 <histogram name="Media.OutputStreamDuration" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -4802,7 +4802,7 @@
 </histogram>
 
 <histogram name="Media.VideoCapture.Error" enum="VideoCaptureError"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>mcasas@chromium.org</owner>
   <owner>guidou@chromium.org</owner>
   <owner>armax@chromium.org</owner>
@@ -4965,7 +4965,7 @@
 </histogram>
 
 <histogram name="Media.VideoCapture.Start" enum="Boolean"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>toprice@chromium.org</owner>
   <owner>guidou@chromium.org</owner>
   <summary>
@@ -4975,7 +4975,7 @@
 </histogram>
 
 <histogram name="Media.VideoCapture.StartOutcome"
-    enum="VideoCaptureStartOutcome" expires_after="2022-04-17">
+    enum="VideoCaptureStartOutcome" expires_after="2022-06-19">
   <owner>toprice@chromium.org</owner>
   <owner>guidou@chromium.org</owner>
   <summary>
@@ -5618,7 +5618,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Cast.Channel.ConnectResult" enum="BooleanSuccess"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>mfoltz@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -5628,7 +5628,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Cast.Channel.Error"
-    enum="MediaRouterCastChannelError" expires_after="2022-04-17">
+    enum="MediaRouterCastChannelError" expires_after="2022-06-19">
   <owner>mfoltz@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -5745,7 +5745,7 @@
 </histogram>
 
 <histogram name="MediaRouter.CastStreaming.Session.Launch" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>takumif@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>Total time to launch a Cast Streaming mirror session.</summary>
@@ -5860,7 +5860,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Dial.AvailableDevicesCount" units="devices"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>mfoltz@chromium.org</owner>
   <owner>takumif@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
@@ -5939,7 +5939,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Icon.Click.Location"
-    enum="MediaRouterDialogOpenOrigin" expires_after="2022-04-17">
+    enum="MediaRouterDialogOpenOrigin" expires_after="2022-06-19">
   <owner>takumif@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>Location the user clicked to open the Media Router dialog.</summary>
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index b358aa8e..20921bc4 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -368,7 +368,7 @@
 </histogram>
 
 <histogram name="Memory.Browser.MemoryFootprint.OnBackground" units="MiB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>justincohen@chromium.org</owner>
   <owner>olivierrobin@chromium.org</owner>
   <summary>
@@ -404,7 +404,7 @@
 </histogram>
 
 <histogram name="Memory.Browser.PrivateMemoryFootprint.HasZombieProfile"
-    units="MB" expires_after="2022-04-20">
+    units="MB" expires_after="2022-06-19">
   <owner>nicolaso@chromium.org</owner>
   <owner>cbe-eng@google.com</owner>
   <summary>
@@ -1993,7 +1993,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableImage.OnDiskSize.5min" units="KiB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>thiabaud@google.com</owner>
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -2003,7 +2003,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableImage.Read.Latency" units="microseconds"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>thiabaud@google.com</owner>
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -2016,7 +2016,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableImage.Read.Size" units="KiB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>thiabaud@google.com</owner>
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -2026,7 +2026,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableImage.Read.Throughput" units="MiBps"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>thiabaud@google.com</owner>
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -2036,7 +2036,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableImage.Read.TimeSinceFreeze" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>thiabaud@google.com</owner>
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -2046,7 +2046,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableImage.TotalReadTime.5min" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>thiabaud@google.com</owner>
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -2066,7 +2066,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableImage.TotalWriteTime.5min" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>thiabaud@google.com</owner>
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -2076,7 +2076,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableImage.UnparkedSize.5min" units="KiB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>thiabaud@google.com</owner>
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -2086,7 +2086,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableImage.Write.Latency" units="microseconds"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>thiabaud@google.com</owner>
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -2099,7 +2099,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableImage.Write.Size" units="KiB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>thiabaud@google.com</owner>
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -2109,7 +2109,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableImage.Write.Throughput" units="MiBps"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>thiabaud@google.com</owner>
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -2128,7 +2128,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.Compression.Latency"
-    units="microseconds" expires_after="2022-04-17">
+    units="microseconds" expires_after="2022-06-19">
   <owner>lizeb@chromium.org</owner>
   <owner>thiabaud@google.com</owner>
   <summary>
@@ -2158,7 +2158,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.CompressionRatio.5min" units="%"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>lizeb@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <summary>
@@ -2169,7 +2169,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.Decompression.Latency"
-    units="microseconds" expires_after="2022-04-17">
+    units="microseconds" expires_after="2022-06-19">
   <owner>lizeb@chromium.org</owner>
   <owner>thiabaud@google.com</owner>
   <summary>
@@ -2191,7 +2191,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.Decompression.ThroughputMBps"
-    units="MBps" expires_after="2022-04-17">
+    units="MBps" expires_after="2022-06-19">
   <owner>lizeb@chromium.org</owner>
   <owner>thiabaud@google.com</owner>
   <summary>
@@ -2235,7 +2235,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.MainThreadTime.5min" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>lizeb@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <summary>
@@ -2246,7 +2246,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.MemorySavingsKb.5min" units="KB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>lizeb@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <summary>
@@ -2281,7 +2281,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.ParkingThreadTime.5min" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>lizeb@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <summary>
@@ -2292,7 +2292,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.Read.Latency" units="microseconds"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>lizeb@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <summary>
@@ -2304,7 +2304,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.Read.SinceLastDiskWrite" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>lizeb@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <summary>
@@ -2313,7 +2313,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.Read.SizeKb" units="KB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>lizeb@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <summary>Size read from disk for a ParkableString.</summary>
@@ -2337,7 +2337,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.SavingsKb.5min" units="KB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>lizeb@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <summary>
@@ -2454,7 +2454,7 @@
 </histogram>
 
 <histogram base="true" name="Memory.PressureWindowDuration" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
 <!-- Name completed by histogram_suffixes name="Memory.Pressure.TransitionType" -->
 
   <owner>fdoray@chromium.org</owner>
@@ -2699,7 +2699,7 @@
 </histogram>
 
 <histogram name="Memory.Total.PrivateMemoryFootprint.HasZombieProfile"
-    units="MB" expires_after="2022-04-20">
+    units="MB" expires_after="2022-06-19">
   <owner>nicolaso@chromium.org</owner>
   <owner>cbe-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/mobile/histograms.xml b/tools/metrics/histograms/metadata/mobile/histograms.xml
index 7dc9d70..3aba02a5 100644
--- a/tools/metrics/histograms/metadata/mobile/histograms.xml
+++ b/tools/metrics/histograms/metadata/mobile/histograms.xml
@@ -806,7 +806,7 @@
 </histogram>
 
 <histogram base="true" name="MobileFre.FromLaunch" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>skym@chromium.org</owner>
   <owner>wenyufu@chromium.org</owner>
   <summary>
@@ -1153,7 +1153,7 @@
 </histogram>
 
 <histogram name="MobileStartup.DailyLaunchCount" units="units"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>tedchoc@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml
index eeafeee..13fe97f 100644
--- a/tools/metrics/histograms/metadata/navigation/histograms.xml
+++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -80,7 +80,7 @@
 
 <histogram
     name="BackForwardCache.AllSites.HistoryNavigationOutcome.BlocklistedFeature"
-    enum="WebSchedulerTrackedFeature" expires_after="2022-04-17">
+    enum="WebSchedulerTrackedFeature" expires_after="2022-06-19">
   <owner>hajimehoshi@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -159,7 +159,7 @@
 
 <histogram
     name="BackForwardCache.AllSites.HistoryNavigationOutcome.NotRestoredReason"
-    enum="BackForwardCacheNotRestoredReason" expires_after="2022-04-17">
+    enum="BackForwardCacheNotRestoredReason" expires_after="2022-06-19">
   <owner>hajimehoshi@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -205,7 +205,7 @@
 </histogram>
 
 <histogram name="BackForwardCache.Eviction.Renderer"
-    enum="BackForwardCacheRendererEvictionReason" expires_after="2022-04-17">
+    enum="BackForwardCacheRendererEvictionReason" expires_after="2022-06-19">
   <owner>rakina@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -524,7 +524,7 @@
 </histogram>
 
 <histogram name="BackForwardCache.Restore.NavigationToFirstPaint" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>sreejakshetty@chromium.org</owner>
   <owner>altimin@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
@@ -561,7 +561,7 @@
 
 <histogram
     name="BackForwardCache.UnexpectedRendererToBrowserMessage.InterfaceName"
-    enum="MojoInterfaceName" expires_after="2022-04-17">
+    enum="MojoInterfaceName" expires_after="2022-06-19">
   <owner>carlscab@google.com</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index 8717e74..0db43a3 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -842,7 +842,7 @@
 </histogram>
 
 <histogram name="Net.DNS.DnsConfig.Nsswitch.IncompatibleService"
-    enum="NsswitchService" expires_after="2022-04-17">
+    enum="NsswitchService" expires_after="2022-06-19">
   <owner>ericorth@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -866,7 +866,7 @@
 </histogram>
 
 <histogram name="Net.DNS.DnsConfig.Nsswitch.Read" enum="BooleanReceived"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>ericorth@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -878,7 +878,7 @@
 </histogram>
 
 <histogram name="Net.DNS.DnsConfig.Nsswitch.TooLarge" enum="BooleanExceeded"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>ericorth@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -911,7 +911,7 @@
 </histogram>
 
 <histogram name="Net.DNS.DnsConfig.Resolv.Read" enum="BooleanReceived"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>ericorth@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -1488,7 +1488,7 @@
 </histogram>
 
 <histogram name="Net.DNS.JobQueueTime.Success" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>dmcardle@chromium.org</owner>
   <owner>ericorth@chromium.org</owner>
   <summary>
@@ -2099,7 +2099,7 @@
 </histogram>
 
 <histogram name="Net.HttpAuthCount" enum="HttpAuthCount"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>asanka@chromium.org</owner>
   <owner>mmenke@chromium.org</owner>
   <summary>
@@ -2213,7 +2213,7 @@
 </histogram>
 
 <histogram name="Net.HttpJob.TotalTime.TLS13" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>davidben@chromium.org</owner>
   <owner>trusty-transport@chromium.org</owner>
   <summary>
@@ -2224,7 +2224,7 @@
 </histogram>
 
 <histogram name="Net.HttpJob.TotalTime.TLS13.Google" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>davidben@chromium.org</owner>
   <owner>trusty-transport@chromium.org</owner>
   <summary>
@@ -2632,7 +2632,7 @@
   </summary>
 </histogram>
 
-<histogram name="Net.Port.Alpaca" enum="AlpacaPort" expires_after="2022-04-17">
+<histogram name="Net.Port.Alpaca" enum="AlpacaPort" expires_after="2022-06-19">
   <owner>ricea@chromium.org</owner>
   <owner>yhirano@chromium.org</owner>
   <summary>
@@ -5607,7 +5607,7 @@
 </histogram>
 
 <histogram name="Net.SSLHandshakeDetails" enum="SSLHandshakeDetails"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>davidben@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -5654,7 +5654,7 @@
 </histogram>
 
 <histogram name="Net.SSLLegacyCryptoFallback" enum="SSLLegacyCryptoFallback"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>davidben@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index 0911c9f7..02a69e5 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -3446,7 +3446,7 @@
 </histogram>
 
 <histogram name="NetworkService.GrantSandboxResult"
-    enum="NetworkServiceSandboxGrantResult" expires_after="2022-04-17">
+    enum="NetworkServiceSandboxGrantResult" expires_after="2022-06-19">
   <owner>wfh@chromium.org</owner>
   <owner>mmenke@chromium.org</owner>
   <summary>
@@ -3562,7 +3562,7 @@
 </histogram>
 
 <histogram name="NetworkService.TimeToMigrateData" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>wfh@chromium.org</owner>
   <owner>mmenke@chromium.org</owner>
   <summary>
@@ -3605,7 +3605,7 @@
 </histogram>
 
 <histogram name="NetworkService.URLLoaderFactory.UpdateLoadInfo" units="ms"
-    expires_after="2022-03-30">
+    expires_after="2022-06-19">
   <owner>jam@chromium.org</owner>
   <owner>cduvall@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
index 79954e77..68b0e50ba 100644
--- a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
@@ -183,7 +183,7 @@
 </histogram>
 
 <histogram name="NewTabPage.Carts.DiscountCountAtLoad" units="count"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>meiliang@chromium.org</owner>
   <owner>yuezhanggg@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
@@ -965,7 +965,7 @@
 </histogram>
 
 <histogram name="NewTabPage.MainUi.ShownTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>tiborg@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
   <summary>
@@ -986,7 +986,7 @@
 </histogram>
 
 <histogram name="NewTabPage.Modules.DataRequest" enum="NtpModules"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>tiborg@chromium.org</owner>
   <owner>yyushkina@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -1265,7 +1265,7 @@
 </histogram>
 
 <histogram name="NewTabPage.OneGoogleBar.RequestLatency" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>tiborg@chromium.org</owner>
   <owner>yyushkina@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -2060,7 +2060,7 @@
 </histogram>
 
 <histogram name="NewTabPage.TimeSinceLastNTP" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>tiborg@chromium.org</owner>
   <owner>yyushkina@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -2087,7 +2087,7 @@
 </histogram>
 
 <histogram name="NewTabPage.TimeSpentBeforeDismissLens" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>yusuyoutube@google.com</owner>
   <owner>benwgold@google.com</owner>
   <owner>fgorski@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/notifications/histograms.xml b/tools/metrics/histograms/metadata/notifications/histograms.xml
index 6c3869a..1ecaeadd 100644
--- a/tools/metrics/histograms/metadata/notifications/histograms.xml
+++ b/tools/metrics/histograms/metadata/notifications/histograms.xml
@@ -197,7 +197,7 @@
 </histogram>
 
 <histogram name="Notifications.Blocker.ScreenCapture.SessionDuration"
-    units="ms" expires_after="2022-04-17">
+    units="ms" expires_after="2022-06-19">
   <owner>knollr@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -208,7 +208,7 @@
 </histogram>
 
 <histogram name="Notifications.Blocker.ScreenCapture.SnoozedCount"
-    units="notifications" expires_after="2022-04-17">
+    units="notifications" expires_after="2022-06-19">
   <owner>knollr@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -394,7 +394,7 @@
 </histogram>
 
 <histogram name="Notifications.Database.WriteResult"
-    enum="NotificationDatabaseStatus" expires_after="2022-04-17">
+    enum="NotificationDatabaseStatus" expires_after="2022-06-19">
   <owner>knollr@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -549,7 +549,7 @@
 </histogram>
 
 <histogram name="Notifications.NotifierType" enum="NotifierType"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>tbarzic@chromium.org</owner>
   <owner>tengs@chromium.org</owner>
   <owner>gzadina@google.com</owner>
@@ -625,7 +625,7 @@
 </histogram>
 
 <histogram name="Notifications.PersistentNotificationActionCount"
-    units="buttons" expires_after="2022-04-17">
+    units="buttons" expires_after="2022-06-19">
   <owner>peter@chromium.org</owner>
   <summary>
     The number of action buttons the developer provided for a persistent Web
@@ -1112,7 +1112,7 @@
 </histogram>
 
 <histogram name="Notifications.Windows.GetSettingPolicyStartup"
-    enum="WindowsNotificationGetSettingPolicy" expires_after="2022-04-17">
+    enum="WindowsNotificationGetSettingPolicy" expires_after="2022-06-19">
   <owner>finnur@chromium.org</owner>
   <owner>knollr@chromium.org</owner>
   <owner>peter@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/offline/histograms.xml b/tools/metrics/histograms/metadata/offline/histograms.xml
index 5f0263b..ad079cf 100644
--- a/tools/metrics/histograms/metadata/offline/histograms.xml
+++ b/tools/metrics/histograms/metadata/offline/histograms.xml
@@ -23,7 +23,7 @@
 <histograms>
 
 <histogram name="Offline.Measurements.HttpProbeResult"
-    enum="OfflineMeasurementsHttpProbeResult" expires_after="2022-04-17">
+    enum="OfflineMeasurementsHttpProbeResult" expires_after="2022-06-19">
   <owner>curranmax@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -37,7 +37,7 @@
 </histogram>
 
 <histogram name="Offline.Measurements.IsAirplaneModeEnabled"
-    enum="BooleanEnabled" expires_after="2022-04-17">
+    enum="BooleanEnabled" expires_after="2022-06-19">
   <owner>curranmax@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -53,7 +53,7 @@
 </histogram>
 
 <histogram name="Offline.Measurements.IsRoaming" enum="Boolean"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>curranmax@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -81,7 +81,7 @@
 </histogram>
 
 <histogram name="Offline.Measurements.TimeBetweenChecks" units="minutes"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>curranmax@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -92,7 +92,7 @@
 </histogram>
 
 <histogram name="Offline.Measurements.UserState"
-    enum="OfflineMeasurementsUserState" expires_after="2022-04-17">
+    enum="OfflineMeasurementsUserState" expires_after="2022-06-19">
   <owner>curranmax@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -729,7 +729,7 @@
 </histogram>
 
 <histogram name="OfflinePages.DidNavigationThrottleCancelNavigation"
-    enum="Boolean" expires_after="2022-04-17">
+    enum="Boolean" expires_after="2022-06-19">
   <owner>curranmax@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/omnibox/histograms.xml b/tools/metrics/histograms/metadata/omnibox/histograms.xml
index e9e9eafe..cf7ef551 100644
--- a/tools/metrics/histograms/metadata/omnibox/histograms.xml
+++ b/tools/metrics/histograms/metadata/omnibox/histograms.xml
@@ -236,7 +236,7 @@
 </histogram>
 
 <histogram name="Omnibox.CutOrCopyAllText" units="count"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jdonnelly@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>chrome-omnibox-team@google.com</owner>
@@ -707,7 +707,7 @@
   </summary>
 </histogram>
 
-<histogram name="Omnibox.ProviderTime2" units="ms" expires_after="2022-04-17">
+<histogram name="Omnibox.ProviderTime2" units="ms" expires_after="2022-06-19">
   <owner>jdonnelly@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>chrome-omnibox-team@google.com</owner>
@@ -860,7 +860,7 @@
 </histogram>
 
 <histogram name="Omnibox.SuggestionUsed.AnswerInSuggest"
-    enum="SuggestionAnswerOptionalType" expires_after="2022-04-17">
+    enum="SuggestionAnswerOptionalType" expires_after="2022-06-19">
   <owner>jdonnelly@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>chrome-omnibox-team@google.com</owner>
@@ -1057,7 +1057,7 @@
 </histogram>
 
 <histogram name="Omnibox.TabMatchTime" units="microseconds"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>gangwu@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
@@ -1185,7 +1185,7 @@
 </histogram>
 
 <histogram name="Omnibox.ZeroSuggest.Eligible.OnFocusV2"
-    enum="ZeroSuggestEligibleOnFocus" expires_after="2022-04-17">
+    enum="ZeroSuggestEligibleOnFocus" expires_after="2022-06-19">
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml
index dc74ed9..5ca5eb6 100644
--- a/tools/metrics/histograms/metadata/optimization/histograms.xml
+++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -222,6 +222,32 @@
 </histogram>
 
 <histogram
+    name="OptimizationGuide.HintsFetcher.GetHintsRequest.DroppedHosts.{RequestContext}"
+    units="counts" expires_after="M106">
+  <owner>sophiechang@chromium.org</owner>
+  <owner>mcrouse@chromium.org</owner>
+  <summary>
+    Records the number of hosts that were dropped for a request made with
+    {RequestContext}, if the number of hosts dropped is non-zero. Will record at
+    most once per fetch.
+  </summary>
+  <token key="RequestContext" variants="RequestContext"/>
+</histogram>
+
+<histogram
+    name="OptimizationGuide.HintsFetcher.GetHintsRequest.DroppedUrls.{RequestContext}"
+    units="counts" expires_after="M106">
+  <owner>sophiechang@chromium.org</owner>
+  <owner>mcrouse@chromium.org</owner>
+  <summary>
+    Records the number of URLs that were dropped for a request made with
+    {RequestContext}, if the number of URLs dropped is non-zero. Will record at
+    most once per fetch.
+  </summary>
+  <token key="RequestContext" variants="RequestContext"/>
+</histogram>
+
+<histogram
     name="OptimizationGuide.HintsFetcher.GetHintsRequest.FetchLatency.{RequestContext}"
     units="ms" expires_after="2022-04-17">
   <owner>sophiechang@chromium.org</owner>
@@ -431,7 +457,7 @@
 <histogram
     name="OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus"
     enum="OptimizationGuideRaceNavigationFetchAttemptStatus"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index ea3d875..3dd210e 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -2514,7 +2514,7 @@
 </histogram>
 
 <histogram name="Cast.Sender.RemotePlayback.InitiationLocation"
-    enum="RemotePlaybackInitiationLocation" expires_after="2022-04-17">
+    enum="RemotePlaybackInitiationLocation" expires_after="2022-06-19">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -5858,7 +5858,7 @@
 </histogram>
 
 <histogram name="ExploreSites.MonthlyHostCount" units="hosts"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>dimich@chromium.org</owner>
   <summary>
     Number of unique hosts visited by the user during the last 30 days. Reported
@@ -6492,7 +6492,7 @@
 </histogram>
 
 <histogram name="Feedback.TrustSafetySentiment.SurveyRequested"
-    enum="TrustSafetySentimentFeatureArea" expires_after="2022-04-17">
+    enum="TrustSafetySentimentFeatureArea" expires_after="2022-06-19">
   <owner>sauski@google.com</owner>
   <owner>chrome-hats-eng@google.com</owner>
   <summary>
@@ -6502,7 +6502,7 @@
 </histogram>
 
 <histogram name="Feedback.TrustSafetySentiment.TriggerOccurred"
-    enum="TrustSafetySentimentFeatureArea" expires_after="2022-04-17">
+    enum="TrustSafetySentimentFeatureArea" expires_after="2022-06-19">
   <owner>sauski@google.com</owner>
   <owner>chrome-hats-eng@google.com</owner>
   <summary>
@@ -6882,7 +6882,7 @@
 </histogram>
 
 <histogram name="Gamepad.KnownGamepadConnectedWithId"
-    enum="GamepadVendorProduct" expires_after="2022-04-17">
+    enum="GamepadVendorProduct" expires_after="2022-06-19">
   <owner>mattreynolds@chromium.org</owner>
   <owner>deviceapi-team@google.com</owner>
   <summary>
@@ -6939,7 +6939,7 @@
 </histogram>
 
 <histogram name="GestureNavigation.Completed" enum="GestureNavigationDirection"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jinsukkim@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
   <summary>
@@ -7003,7 +7003,7 @@
 </histogram>
 
 <histogram name="GestureNavigation.Type" enum="GestureNavigationType"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jinsukkim@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
   <summary>
@@ -7126,7 +7126,7 @@
   </summary>
 </histogram>
 
-<histogram name="Hardware.TotalDiskSpace" units="GB" expires_after="2022-04-17">
+<histogram name="Hardware.TotalDiskSpace" units="GB" expires_after="2022-06-19">
   <owner>sadrul@chromium.org</owner>
   <owner>zmo@chromium.org</owner>
   <summary>
@@ -9165,7 +9165,7 @@
 </histogram>
 
 <histogram name="Mojo.Channel.WriteMessageSize" units="bytes"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>amistry@chromium.org</owner>
   <owner>bgeffon@chromium.org</owner>
   <owner>rockot@google.com</owner>
@@ -10341,7 +10341,7 @@
 </histogram>
 
 <histogram name="Pepper.InterfaceUsed" enum="PepperInterface"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>bbudge@chromium.org</owner>
   <owner>raymes@chromium.org</owner>
   <summary>
@@ -11567,7 +11567,7 @@
 </histogram>
 
 <histogram name="Privacy.AccuracyTip.AccuracyTipInteraction"
-    enum="AccuracyTipInteraction" expires_after="2022-04-17">
+    enum="AccuracyTipInteraction" expires_after="2022-06-19">
   <owner>dullweber@chromium.org</owner>
   <owner>eokoyomon@chromium.org</owner>
   <summary>
@@ -11820,7 +11820,7 @@
 </histogram>
 
 <histogram name="PushMessaging.CheckOriginForAbuseTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>knollr@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -11831,7 +11831,7 @@
 </histogram>
 
 <histogram name="PushMessaging.DeliverQueuedMessageTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>knollr@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -11922,7 +11922,7 @@
 </histogram>
 
 <histogram name="PushMessaging.RegistrationStatus"
-    enum="PushRegistrationStatus" expires_after="2022-04-17">
+    enum="PushRegistrationStatus" expires_after="2022-06-19">
   <owner>peter@chromium.org</owner>
   <owner>knollr@chromium.org</owner>
   <summary>
@@ -12004,7 +12004,7 @@
 </histogram>
 
 <histogram name="PushMessaging.UserVisibleStatus" enum="PushUserVisibleStatus"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>peter@chromium.org</owner>
   <owner>knollr@chromium.org</owner>
   <summary>
@@ -12244,7 +12244,7 @@
 </histogram>
 
 <histogram name="ReadingList.WindowDisplayedDuration" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>corising@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -13028,7 +13028,7 @@
 </histogram>
 
 <histogram base="true" name="SB2.RequestDestination" enum="RequestDestination"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -14042,7 +14042,7 @@
 </histogram>
 
 <histogram name="SpellCheck.SpellingService.Enabled" enum="BooleanEnabled"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>groby@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -14085,7 +14085,7 @@
 </histogram>
 
 <histogram name="SpellCheck.SuggestionHitRatio" units="%"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>gujen@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -14457,7 +14457,7 @@
 </histogram>
 
 <histogram name="SSORecallPromo.PromoAction" enum="SSOPromoUserAction"
-    expires_after="2022-04-20">
+    expires_after="2022-06-19">
   <owner>fernandex@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
@@ -14466,7 +14466,7 @@
 </histogram>
 
 <histogram name="SSORecallPromo.PromoSeenCount" units="units"
-    expires_after="2022-04-20">
+    expires_after="2022-06-19">
   <owner>fernandex@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
@@ -17053,7 +17053,7 @@
 </histogram>
 
 <histogram name="WebUITabStrip.OpenDuration" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>collinbaker@chromium.org</owner>
   <owner>tluk@chromium.org</owner>
   <summary>
@@ -17125,6 +17125,10 @@
 
 <histogram name="WebView.ApiCall" enum="WebViewApiCall"
     expires_after="2022-01-31">
+  <obsolete>
+    Renamed in M99. Please see &quot;Android.WebView.ApiCall&quot; going
+    forward, which has the same logging behavior.
+  </obsolete>
   <owner>ntfschr@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>Records calls to WebView APIs in WebViewChromium.</summary>
diff --git a/tools/metrics/histograms/metadata/page/histograms.xml b/tools/metrics/histograms/metadata/page/histograms.xml
index 3f52603..b952d07 100644
--- a/tools/metrics/histograms/metadata/page/histograms.xml
+++ b/tools/metrics/histograms/metadata/page/histograms.xml
@@ -1032,7 +1032,7 @@
 
 <histogram
     name="PageLoad.Clients.ThirdParty.Frames.NavigationToFirstContentfulPaint3"
-    units="ms" expires_after="2022-04-17">
+    units="ms" expires_after="2022-06-19">
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <summary>
@@ -1123,7 +1123,7 @@
 </histogram>
 
 <histogram name="PageLoad.Cpu.TotalUsageForegrounded" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>alexmt@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <summary>
@@ -1135,7 +1135,7 @@
 
 <histogram
     name="PageLoad.DocumentTiming.NavigationToDOMContentLoadedEventFired"
-    units="ms" expires_after="2022-04-17">
+    units="ms" expires_after="2022-06-19">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -1145,7 +1145,7 @@
 </histogram>
 
 <histogram name="PageLoad.DocumentTiming.NavigationToLoadEventFired" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -1250,7 +1250,7 @@
 </histogram>
 
 <histogram name="PageLoad.Experimental.Bytes.NetworkIncludingHeaders"
-    units="KB" expires_after="2022-04-17">
+    units="KB" expires_after="2022-06-19">
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The number of prefiltered (e.g., compressed) KiloBytes loaded over the
@@ -1261,7 +1261,7 @@
 </histogram>
 
 <histogram name="PageLoad.Experimental.Bytes.Total2" units="KB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <summary>
@@ -1596,7 +1596,7 @@
 
 <histogram
     name="PageLoad.Experimental.NavigationTiming.NavigationStartToFinalRequestStart"
-    units="ms" expires_after="2022-04-17">
+    units="ms" expires_after="2022-06-19">
   <owner>nhiroki@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -1640,7 +1640,7 @@
 
 <histogram
     name="PageLoad.Experimental.NavigationTiming.NavigationStartToFirstResponseStart"
-    units="ms" expires_after="2022-04-17">
+    units="ms" expires_after="2022-06-19">
   <owner>nhiroki@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -1651,7 +1651,7 @@
 
 <histogram
     name="PageLoad.Experimental.NavigationTiming.NavigationStartToNavigationCommitSent"
-    units="ms" expires_after="2022-04-17">
+    units="ms" expires_after="2022-06-19">
   <owner>nhiroki@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -1827,7 +1827,7 @@
 
 <histogram
     name="PageLoad.Experimental.PaintTiming.NavigationToFirstMeaningfulPaint"
-    units="ms" expires_after="2022-04-17">
+    units="ms" expires_after="2022-06-19">
   <owner>ksakamoto@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -2494,7 +2494,7 @@
 </histogram>
 
 <histogram name="PageLoad.LayoutInstability.CumulativeShiftScore.MainFrame"
-    units="scorex10" expires_after="2022-04-17">
+    units="scorex10" expires_after="2022-06-19">
   <owner>bmcquade@chromium.org</owner>
   <owner>skobes@chromium.org</owner>
   <summary>
@@ -2693,7 +2693,7 @@
 </histogram>
 
 <histogram name="PageLoad.PaintTiming.ForegroundToFirstContentfulPaint"
-    units="ms" expires_after="2022-04-17">
+    units="ms" expires_after="2022-06-19">
   <owner>sullivan@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -2774,7 +2774,7 @@
 </histogram>
 
 <histogram name="PageLoad.PaintTiming.NavigationToFirstImagePaint" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>sullivan@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -2785,7 +2785,7 @@
 </histogram>
 
 <histogram name="PageLoad.PaintTiming.NavigationToFirstPaint" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>ksakamoto@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -2936,7 +2936,7 @@
 </histogram>
 
 <histogram name="PageLoad.ParseTiming.NavigationToParseStart" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml
index f30db13..ead501ec 100644
--- a/tools/metrics/histograms/metadata/password/histograms.xml
+++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -55,7 +55,7 @@
 </variants>
 
 <histogram name="KeyboardAccessory.AccessoryActionImpression"
-    enum="AccessoryAction" expires_after="2022-04-17">
+    enum="AccessoryAction" expires_after="2022-06-19">
   <owner>fhorschig@chromium.org</owner>
   <owner>ioanap@chromium.org</owner>
   <summary>
@@ -65,7 +65,7 @@
 </histogram>
 
 <histogram name="KeyboardAccessory.AccessoryActionSelected"
-    enum="AccessoryAction" expires_after="2022-04-17">
+    enum="AccessoryAction" expires_after="2022-06-19">
   <owner>fhorschig@chromium.org</owner>
   <owner>ioanap@chromium.org</owner>
   <summary>
@@ -75,7 +75,7 @@
 </histogram>
 
 <histogram name="KeyboardAccessory.AccessoryBarShown"
-    enum="AccessoryBarContents" expires_after="2022-04-17">
+    enum="AccessoryBarContents" expires_after="2022-06-19">
   <owner>fhorschig@chromium.org</owner>
   <owner>ioanap@chromium.org</owner>
   <summary>
@@ -86,7 +86,7 @@
 </histogram>
 
 <histogram name="KeyboardAccessory.AccessorySheetSuggestionCount" units="count"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>fhorschig@chromium.org</owner>
   <owner>ioanap@chromium.org</owner>
   <summary>
@@ -194,7 +194,7 @@
 </histogram>
 
 <histogram name="PasswordGeneration.Event" enum="PasswordGenerationEvent"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>kazinova@google.com</owner>
   <owner>kolos@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
@@ -669,7 +669,7 @@
 </histogram>
 
 <histogram name="PasswordManager.AffiliationFetcher.FetchResult"
-    enum="AffiliationFetchResult" expires_after="2022-04-17">
+    enum="AffiliationFetchResult" expires_after="2022-06-19">
   <owner>vsemeniuk@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -822,7 +822,7 @@
 </histogram>
 
 <histogram name="PasswordManager.AutomaticChange.AcceptanceWithoutAutoButton"
-    enum="PasswordCheckResolutionAction" expires_after="2022-04-17">
+    enum="PasswordCheckResolutionAction" expires_after="2022-06-19">
   <owner>kolos@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <summary>
@@ -971,7 +971,7 @@
 </histogram>
 
 <histogram name="PasswordManager.BulkCheck.Error"
-    enum="PasswordLeakDetectionError" expires_after="2022-04-17">
+    enum="PasswordLeakDetectionError" expires_after="2022-06-19">
   <owner>vasilii@chromium.org</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>Error encountered during the password bulk check.</summary>
@@ -1003,7 +1003,7 @@
 </histogram>
 
 <histogram name="PasswordManager.BulkCheck.PasswordCheckReferrer"
-    enum="PasswordCheckReferrer" expires_after="2022-04-17">
+    enum="PasswordCheckReferrer" expires_after="2022-06-19">
   <owner>vsemeniuk@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1032,7 +1032,7 @@
 </histogram>
 
 <histogram name="PasswordManager.BulkCheck.TimePerCredential" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>vasilii@chromium.org</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>
@@ -1310,7 +1310,7 @@
 </histogram>
 
 <histogram name="PasswordManager.EditsInSaveBubble"
-    enum="PasswordManagerEditsInSaveBubbleEnum" expires_after="2022-04-17">
+    enum="PasswordManagerEditsInSaveBubbleEnum" expires_after="2022-06-19">
   <owner>vasilii@chromium.org</owner>
   <owner>kazinova@google.com</owner>
   <summary>
@@ -1371,7 +1371,7 @@
 </histogram>
 
 <histogram name="PasswordManager.FieldNameCollisionInVotes" enum="Boolean"
-    expires_after="2022-04-20">
+    expires_after="2022-06-19">
   <owner>kolos@chromium.org</owner>
   <owner>koerber@google.com</owner>
   <summary>
@@ -1390,7 +1390,7 @@
 </histogram>
 
 <histogram name="PasswordManager.FillingAssistance"
-    enum="PasswordManagerFillingAssistance" expires_after="2022-04-17">
+    enum="PasswordManagerFillingAssistance" expires_after="2022-06-19">
   <owner>kazinova@google.com</owner>
   <owner>battre@chromium.org</owner>
   <summary>
@@ -1713,7 +1713,7 @@
 
 <histogram name="PasswordManager.LeakDetection.DialogDismissalReason"
     enum="PasswordLeakDetectionDialogDismissalReason"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>vsemeniuk@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1736,7 +1736,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.Error"
-    enum="PasswordLeakDetectionError" expires_after="2022-04-17">
+    enum="PasswordLeakDetectionError" expires_after="2022-06-19">
   <owner>vsemeniuk@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1745,7 +1745,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.HttpResponseCode"
-    enum="HttpResponseCode" expires_after="2022-04-17">
+    enum="HttpResponseCode" expires_after="2022-06-19">
   <owner>vsemeniuk@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1869,14 +1869,14 @@
 </histogram>
 
 <histogram name="PasswordManager.LoginDatabaseInit"
-    enum="LoginDatabaseInitError" expires_after="2022-04-17">
+    enum="LoginDatabaseInitError" expires_after="2022-06-19">
   <owner>vasilii@chromium.org</owner>
   <owner>mamir@chromium.org</owner>
   <summary>An error on LoginDatabase initialization.</summary>
 </histogram>
 
 <histogram name="PasswordManager.ManagePasswordsReferrer"
-    enum="ManagePasswordsReferrer" expires_after="2022-04-17">
+    enum="ManagePasswordsReferrer" expires_after="2022-06-19">
   <owner>kazinova@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1978,7 +1978,7 @@
 </histogram>
 
 <histogram name="PasswordManager.NewlySavedPasswordIsGenerated"
-    enum="BooleanNewlySavedPasswordIsGenerated" expires_after="2022-04-17">
+    enum="BooleanNewlySavedPasswordIsGenerated" expires_after="2022-06-19">
   <owner>nepper@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <owner>kolos@chromium.org</owner>
@@ -2158,7 +2158,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordScriptsFetcher.CacheState"
-    enum="PasswordScriptsFetcherCacheState" expires_after="2022-04-17">
+    enum="PasswordScriptsFetcherCacheState" expires_after="2022-06-19">
   <owner>kolos@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <summary>
@@ -2169,7 +2169,7 @@
 
 <histogram
     name="PasswordManager.PasswordScriptsFetcher.HttpResponseAndNetErrorCode"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2022-04-17">
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2022-06-19">
   <owner>kolos@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <summary>
@@ -2179,7 +2179,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordScriptsFetcher.ParsingResult"
-    enum="PasswordScriptsFetcherParsingResult" expires_after="2022-04-17">
+    enum="PasswordScriptsFetcherParsingResult" expires_after="2022-06-19">
   <owner>kolos@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <summary>Result of parsing of a list of available password scripts.</summary>
@@ -2233,6 +2233,8 @@
   </summary>
   <token key="Function">
     <variant name="AddLoginAsync" summary="add a login to"/>
+    <variant name="DisableAutoSignInForOriginsAsync"
+        summary="disable autosignin for matching logins in"/>
     <variant name="FillMatchingLoginsAsync"
         summary="fill matching logins from"/>
     <variant name="GetAllLoginsAsync" summary="retrieve all logins from"/>
@@ -2262,6 +2264,9 @@
   <token key="Function">
     <variant name="AddLoginAsync"
         summary="AddLoginAsync() succeeded to add a login to"/>
+    <variant name="DisableAutoSignInForOriginsAsync"
+        summary="DisableAutoSignInForOriginsAsync() succeeded in disabling
+                 autosignin for matching logins in"/>
     <variant name="FillMatchingLoginsAsync"
         summary="succeeded in filling matching logins from"/>
     <variant name="GetAllLoginsAsync"
@@ -2528,7 +2533,7 @@
 </histogram>
 
 <histogram name="PasswordManager.SaveUIDismissalReason"
-    enum="PasswordManagerUIDismissalReason" expires_after="2022-04-17">
+    enum="PasswordManagerUIDismissalReason" expires_after="2022-06-19">
 <!-- Name completed by histogram_suffixes name="PasswordAccountStorageUserState" -->
 
   <owner>vasilii@chromium.org</owner>
@@ -2701,7 +2706,7 @@
 </histogram>
 
 <histogram name="PasswordManager.TouchToFill.CredentialIndex" units="index"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>ioanap@chromium.org</owner>
   <owner>fhorschig@chromium.org</owner>
   <summary>
@@ -2711,7 +2716,7 @@
 </histogram>
 
 <histogram name="PasswordManager.TouchToFill.DismissalReason"
-    enum="BottomSheet.StateChangeReason" expires_after="2022-04-17">
+    enum="BottomSheet.StateChangeReason" expires_after="2022-06-19">
   <owner>ioanap@chromium.org</owner>
   <owner>fhorschig@chromium.org</owner>
   <summary>
@@ -2737,7 +2742,7 @@
 </histogram>
 
 <histogram name="PasswordManager.UIDismissalReason"
-    enum="PasswordManagerUIDismissalReason" expires_after="2022-04-17">
+    enum="PasswordManagerUIDismissalReason" expires_after="2022-06-19">
   <owner>vasilii@chromium.org</owner>
   <summary>
     Why was the password manager's UI (bubble or infobar) closed? Save and
@@ -3251,7 +3256,7 @@
 </histogram>
 
 <histogram base="true" name="PasswordProtection.RequestOutcome"
-    enum="PasswordProtectionRequestOutcome" expires_after="2022-04-17">
+    enum="PasswordProtectionRequestOutcome" expires_after="2022-06-19">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -3296,7 +3301,7 @@
 </histogram>
 
 <histogram name="PasswordProtection.Verdict" enum="PasswordProtectionVerdict"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/payment/histograms.xml b/tools/metrics/histograms/metadata/payment/histograms.xml
index 3e8368f..13385be 100644
--- a/tools/metrics/histograms/metadata/payment/histograms.xml
+++ b/tools/metrics/histograms/metadata/payment/histograms.xml
@@ -86,7 +86,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.JourneyLoggerHasRecorded" enum="Boolean"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>rouslan@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/pdf/histograms.xml b/tools/metrics/histograms/metadata/pdf/histograms.xml
index a5e28c5b..e7cf53b 100644
--- a/tools/metrics/histograms/metadata/pdf/histograms.xml
+++ b/tools/metrics/histograms/metadata/pdf/histograms.xml
@@ -77,7 +77,7 @@
 </histogram>
 
 <histogram name="PDF.LoadStatus" enum="ChromePDFViewerLoadStatus"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>kmoon@chromium.org</owner>
   <owner>thestig@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/permissions/histograms.xml b/tools/metrics/histograms/metadata/permissions/histograms.xml
index 53a2610..b56f586 100644
--- a/tools/metrics/histograms/metadata/permissions/histograms.xml
+++ b/tools/metrics/histograms/metadata/permissions/histograms.xml
@@ -212,7 +212,7 @@
 </histogram>
 
 <histogram name="Permissions.Chip.TimeToInteraction" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>bsep@chromium.org</owner>
   <owner>engedy@chromium.org</owner>
   <owner>olesiamarukhno@google.com</owner>
@@ -280,7 +280,7 @@
 </histogram>
 
 <histogram name="Permissions.CrowdDeny.PreloadData.VersionAtAbuseCheckTime"
-    units="date" expires_after="2022-04-17">
+    units="date" expires_after="2022-06-19">
   <owner>elklm@chromium.org</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
   <summary>
@@ -471,7 +471,7 @@
 </histogram>
 
 <histogram name="Permissions.PredictionService.GeolocationRequest"
-    enum="BooleanSent" expires_after="2022-04-17">
+    enum="BooleanSent" expires_after="2022-06-19">
   <owner>engedy@chromium.org</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
   <summary>
@@ -924,7 +924,7 @@
 </histogram>
 
 <histogram base="true" name="Permissions.Usage.ElapsedTimeSinceGrant"
-    units="seconds" expires_after="2022-04-17">
+    units="seconds" expires_after="2022-06-19">
   <owner>engedy@chromium.org</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/phonehub/histograms.xml b/tools/metrics/histograms/metadata/phonehub/histograms.xml
index 5507ed28..c232c1a8 100644
--- a/tools/metrics/histograms/metadata/phonehub/histograms.xml
+++ b/tools/metrics/histograms/metadata/phonehub/histograms.xml
@@ -90,6 +90,18 @@
   <token key="MediaType" variants="CameraRollMediaType"/>
 </histogram>
 
+<histogram name="PhoneHub.CameraRoll.OptInEntryPoint"
+    enum="PhoneHubCameraRollOptInEntryPoint" expires_after="2022-10-31">
+  <owner>jianbing@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Emitted when a new multidevice suite user goes through the multidevice
+    onboarding flow, when the user enables the Camera Roll toggle in Settings,
+    and when user chose to turn on the feature in camera roll onboarding dialog
+    inside phone hub panel.
+  </summary>
+</histogram>
+
 <histogram name="PhoneHub.CompletedUserAction" enum="PhoneHubUserAction"
     expires_after="2022-10-31">
   <owner>jonmann@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/platform/histograms.xml b/tools/metrics/histograms/metadata/platform/histograms.xml
index 579361b1..6082c39 100644
--- a/tools/metrics/histograms/metadata/platform/histograms.xml
+++ b/tools/metrics/histograms/metadata/platform/histograms.xml
@@ -579,7 +579,7 @@
 </histogram>
 
 <histogram name="Platform.LogicalCpuCount" units="units"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>sonnyrao@chromium.org</owner>
   <owner>chromeos-performance@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/power/histograms.xml b/tools/metrics/histograms/metadata/power/histograms.xml
index c692f2ee..93e2cd1 100644
--- a/tools/metrics/histograms/metadata/power/histograms.xml
+++ b/tools/metrics/histograms/metadata/power/histograms.xml
@@ -1488,7 +1488,7 @@
 </histogram>
 
 <histogram name="PowerML.SmartDimComponent.WorkerType"
-    enum="PowerMLSmartDimComponentWorkerType" expires_after="2022-04-17">
+    enum="PowerMLSmartDimComponentWorkerType" expires_after="2022-06-19">
   <owner>alanlxl@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>napper@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/prefetch/histograms.xml b/tools/metrics/histograms/metadata/prefetch/histograms.xml
index 4094c4b6..cb011460 100644
--- a/tools/metrics/histograms/metadata/prefetch/histograms.xml
+++ b/tools/metrics/histograms/metadata/prefetch/histograms.xml
@@ -396,7 +396,7 @@
 </histogram>
 
 <histogram name="PrefetchProxy.Prefetch.Mainframe.BodyLength" units="bytes"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>robertogden@chromium.org</owner>
   <owner>ryansturm@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/print/histograms.xml b/tools/metrics/histograms/metadata/print/histograms.xml
index c13394a..892851e4 100644
--- a/tools/metrics/histograms/metadata/print/histograms.xml
+++ b/tools/metrics/histograms/metadata/print/histograms.xml
@@ -184,7 +184,7 @@
 </histogram>
 
 <histogram name="PrintPreview.PrintDocumentType"
-    enum="PrintPreviewPrintDocumentTypeBuckets" expires_after="2022-04-17">
+    enum="PrintPreviewPrintDocumentTypeBuckets" expires_after="2022-06-19">
   <owner>rbpotter@chromium.org</owner>
   <owner>awscreen@chromium.org</owner>
   <summary>
@@ -320,7 +320,7 @@
 </histogram>
 
 <histogram name="PrintPreview.UserAction" enum="PrintPreviewUserActionType"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>thestig@chromium.org</owner>
   <owner>awscreen@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/printing/histograms.xml b/tools/metrics/histograms/metadata/printing/histograms.xml
index eb8da23..9927b47 100644
--- a/tools/metrics/histograms/metadata/printing/histograms.xml
+++ b/tools/metrics/histograms/metadata/printing/histograms.xml
@@ -204,7 +204,7 @@
 </histogram>
 
 <histogram name="Printing.CUPS.JobDuration.JobDone" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>bmgordon@chromium.org</owner>
   <owner>project-bolton@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/profile/histograms.xml b/tools/metrics/histograms/metadata/profile/histograms.xml
index 93321828..abc8aeb 100644
--- a/tools/metrics/histograms/metadata/profile/histograms.xml
+++ b/tools/metrics/histograms/metadata/profile/histograms.xml
@@ -39,7 +39,7 @@
 </histogram>
 
 <histogram name="Profile.AllAccounts.Categories"
-    enum="ProfileAllAccountsCategories" expires_after="2022-04-17">
+    enum="ProfileAllAccountsCategories" expires_after="2022-06-19">
   <owner>jkrcal@chromium.org</owner>
   <owner>droger@chromium.org</owner>
   <summary>
@@ -53,7 +53,7 @@
 </histogram>
 
 <histogram name="Profile.AllAccounts.Names" enum="ProfileAllAccountsNames"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jkrcal@chromium.org</owner>
   <owner>droger@chromium.org</owner>
   <summary>
@@ -98,7 +98,7 @@
 </histogram>
 
 <histogram name="Profile.BrowserActive.PerProfile" enum="Profile"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>msarda@chromium.org</owner>
   <owner>tangltom@chromium.org</owner>
   <summary>
@@ -200,7 +200,7 @@
 </histogram>
 
 <histogram name="Profile.Destroyer.OffTheRecord" enum="ProfileDestructionType"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
   <summary>
@@ -221,7 +221,7 @@
 </histogram>
 
 <histogram name="Profile.DidDestroyProfileBeforeShutdown" enum="Boolean"
-    expires_after="2022-04-01">
+    expires_after="2022-06-19">
   <owner>nicolaso@chromium.org</owner>
   <owner>cbe-eng@google.com</owner>
   <summary>
@@ -244,7 +244,7 @@
   </summary>
 </histogram>
 
-<histogram name="Profile.ExtensionSize" units="MB" expires_after="2022-04-17">
+<histogram name="Profile.ExtensionSize" units="MB" expires_after="2022-06-19">
   <owner>etienneb@chromium.org</owner>
   <owner>gab@chromium.org</owner>
   <summary>Size of the extension cookies database.</summary>
@@ -267,7 +267,7 @@
 </histogram>
 
 <histogram name="Profile.Guest.OTR.Lifetime" units="minutes"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-incognito@google.com</owner>
   <summary>
@@ -422,7 +422,7 @@
 </histogram>
 
 <histogram name="Profile.NukeFromDisk.Result" enum="NukeProfileResult"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>nicolaso@chromium.org</owner>
   <owner>cbe-eng@google.com</owner>
   <summary>
@@ -657,7 +657,7 @@
 </histogram>
 
 <histogram base="true" name="Profile.State.UnconsentedPrimaryAccountType"
-    enum="ProfileUnconsentedPrimaryAccountType" expires_after="2022-04-17">
+    enum="ProfileUnconsentedPrimaryAccountType" expires_after="2022-06-19">
   <owner>jkrcal@chromium.org</owner>
   <owner>droger@chromium.org</owner>
   <summary>
@@ -830,7 +830,7 @@
 </histogram>
 
 <histogram name="Profile.ZombieProfileCount" units="profiles"
-    expires_after="2022-04-01">
+    expires_after="2022-06-19">
   <owner>nicolaso@chromium.org</owner>
   <owner>cbe-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/quota/histograms.xml b/tools/metrics/histograms/metadata/quota/histograms.xml
index 0447477..2cdf853f 100644
--- a/tools/metrics/histograms/metadata/quota/histograms.xml
+++ b/tools/metrics/histograms/metadata/quota/histograms.xml
@@ -43,7 +43,7 @@
 </histogram>
 
 <histogram name="Quota.AvailableDiskSpace" units="MB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jarrydg@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
@@ -76,7 +76,7 @@
   </summary>
 </histogram>
 
-<histogram name="Quota.DiskspaceShortage" units="MB" expires_after="2022-04-17">
+<histogram name="Quota.DiskspaceShortage" units="MB" expires_after="2022-06-19">
   <owner>jarrydg@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
@@ -188,13 +188,13 @@
 </histogram>
 
 <histogram name="Quota.GlobalUsageOfPersistentStorage" units="MB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jarrydg@chromium.org</owner>
   <summary>Global usage of persistent storage.</summary>
 </histogram>
 
 <histogram name="Quota.GlobalUsageOfTemporaryStorage" units="MB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jarrydg@chromium.org</owner>
   <summary>Global usage of temporary storage.</summary>
 </histogram>
@@ -234,7 +234,7 @@
 </histogram>
 
 <histogram name="Quota.PercentDiskAvailable" units="%"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jarrydg@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
@@ -264,7 +264,7 @@
 </histogram>
 
 <histogram name="Quota.PercentUsedForTemporaryStorage2" units="%"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jarrydg@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
@@ -284,7 +284,7 @@
   </summary>
 </histogram>
 
-<histogram name="Quota.QuotaForOrigin" units="MB" expires_after="2022-04-17">
+<histogram name="Quota.QuotaForOrigin" units="MB" expires_after="2022-06-19">
   <owner>jarrydg@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/renderer/histograms.xml b/tools/metrics/histograms/metadata/renderer/histograms.xml
index 00a87dda..71a28914 100644
--- a/tools/metrics/histograms/metadata/renderer/histograms.xml
+++ b/tools/metrics/histograms/metadata/renderer/histograms.xml
@@ -548,7 +548,7 @@
 </histogram>
 
 <histogram name="RendererScheduler.RendererMainThreadLoad5" units="%"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>altimin@chromium.org</owner>
   <summary>
     Renderer main thread load (percentage of time spent in tasks), reported in
diff --git a/tools/metrics/histograms/metadata/renderer4/histograms.xml b/tools/metrics/histograms/metadata/renderer4/histograms.xml
index fb158119..09003fba 100644
--- a/tools/metrics/histograms/metadata/renderer4/histograms.xml
+++ b/tools/metrics/histograms/metadata/renderer4/histograms.xml
@@ -124,7 +124,7 @@
 </histogram>
 
 <histogram name="Renderer4.GpuRasterizationEnabled" enum="BooleanEnabled"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>ericrk@chromium.org</owner>
   <owner>enne@chromium.org</owner>
   <summary>
@@ -273,7 +273,7 @@
 </histogram>
 
 <histogram name="Renderer4.ScrollingThread" enum="ScrollingThreadStatus"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
 <!-- Name completed by histogram_suffixes name="ScrollSourceDevice" -->
 
   <owner>bokan@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index 55402512..a53b84d 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -203,7 +203,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.CheckBrowseUrl.HasLocalMatch"
-    enum="BooleanMatched" expires_after="2022-04-17">
+    enum="BooleanMatched" expires_after="2022-06-19">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1331,7 +1331,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.Pref.Daily.Extended" enum="BooleanEnabled"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1597,7 +1597,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.HasTokenFromFetcher" enum="BooleanHasToken"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1656,7 +1656,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.IsPopulationMbbOrEsb" enum="Boolean"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1834,7 +1834,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.ThreatInfoSize" units="verdicts"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -2061,7 +2061,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4GetHash.CacheHit.Result"
-    enum="SafeBrowsingV4FullHashCacheResult" expires_after="2022-04-17">
+    enum="SafeBrowsingV4FullHashCacheResult" expires_after="2022-06-19">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>Track cache hits for V4 full hashes.</summary>
@@ -2077,7 +2077,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4GetHash.CountOfPrefixes" units="prefixes"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -2256,7 +2256,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4ProcessPartialUpdate.AdditionsHashesCount"
-    units="entries" expires_after="2022-04-17">
+    units="entries" expires_after="2022-06-19">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -2266,7 +2266,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4ProcessPartialUpdate.ApplyUpdate.Result"
-    enum="SafeBrowsingV4ApplyUpdateResult" expires_after="2022-04-17">
+    enum="SafeBrowsingV4ApplyUpdateResult" expires_after="2022-06-19">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/scheduler/histograms.xml b/tools/metrics/histograms/metadata/scheduler/histograms.xml
index 3b82832..f1ca32a 100644
--- a/tools/metrics/histograms/metadata/scheduler/histograms.xml
+++ b/tools/metrics/histograms/metadata/scheduler/histograms.xml
@@ -258,7 +258,7 @@
 </histogram>
 
 <histogram name="Scheduling.Renderer.DeadlineMode"
-    enum="RendererSchedulerDeadlineMode" expires_after="2022-04-17">
+    enum="RendererSchedulerDeadlineMode" expires_after="2022-06-19">
   <owner>weiliangc@chromium.org</owner>
   <owner>chrome-gpu-metrics@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/search/histograms.xml b/tools/metrics/histograms/metadata/search/histograms.xml
index 1ba44de..774fd6b 100644
--- a/tools/metrics/histograms/metadata/search/histograms.xml
+++ b/tools/metrics/histograms/metadata/search/histograms.xml
@@ -1521,7 +1521,7 @@
 </histogram>
 
 <histogram name="Search.RelatedSearches.CarouselLastVisibleItemPosition"
-    units="position" expires_after="2022-04-17">
+    units="position" expires_after="2022-06-19">
   <owner>donnd@chromium.org</owner>
   <owner>gangwu@chromium.org</owner>
   <owner>related-searches-vteam@google.com</owner>
@@ -1535,7 +1535,7 @@
 </histogram>
 
 <histogram name="Search.RelatedSearches.CarouselScrollAndClick"
-    enum="ScrollAndClickStatus" expires_after="2022-04-17">
+    enum="ScrollAndClickStatus" expires_after="2022-06-19">
   <owner>donnd@chromium.org</owner>
   <owner>gangwu@chromium.org</owner>
   <owner>related-searches-vteam@google.com</owner>
@@ -1547,7 +1547,7 @@
 </histogram>
 
 <histogram name="Search.RelatedSearches.CarouselScrolled" enum="Boolean"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>donnd@chromium.org</owner>
   <owner>gangwu@chromium.org</owner>
   <owner>related-searches-vteam@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/security/histograms.xml b/tools/metrics/histograms/metadata/security/histograms.xml
index 534f6eb..efc25e28 100644
--- a/tools/metrics/histograms/metadata/security/histograms.xml
+++ b/tools/metrics/histograms/metadata/security/histograms.xml
@@ -383,7 +383,7 @@
 </histogram>
 
 <histogram name="Security.PageInfo.TimeOpen" units="units"
-    expires_after="2022-06-12">
+    expires_after="2022-06-19">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -516,7 +516,7 @@
 </histogram>
 
 <histogram name="Security.SafetyTips.OpenTime.CloseTab" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jdeblasio@chromium.org</owner>
   <owner>estark@chromium.org</owner>
   <summary>
@@ -526,7 +526,7 @@
 </histogram>
 
 <histogram name="Security.SafetyTips.OpenTime.Dismiss" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jdeblasio@chromium.org</owner>
   <owner>estark@chromium.org</owner>
   <summary>
@@ -1150,7 +1150,7 @@
 </histogram>
 
 <histogram name="SiteIsolation.ProxyCount" units="proxies"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>alexmos@chromium.org</owner>
   <owner>creis@chromium.org</owner>
   <owner>lukasza@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/service/histograms.xml b/tools/metrics/histograms/metadata/service/histograms.xml
index a9a29fb..e5a7cc6 100644
--- a/tools/metrics/histograms/metadata/service/histograms.xml
+++ b/tools/metrics/histograms/metadata/service/histograms.xml
@@ -1186,7 +1186,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.StartWorker.Status"
-    enum="ServiceWorkerStatusCode" expires_after="2022-04-17">
+    enum="ServiceWorkerStatusCode" expires_after="2022-06-19">
   <owner>wanderview@chromium.org</owner>
   <owner>asamidoi@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
@@ -1212,7 +1212,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.StartWorker.Time" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>wanderview@chromium.org</owner>
   <owner>asamidoi@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/session/histograms.xml b/tools/metrics/histograms/metadata/session/histograms.xml
index fbcb7f7..f21ea1f 100644
--- a/tools/metrics/histograms/metadata/session/histograms.xml
+++ b/tools/metrics/histograms/metadata/session/histograms.xml
@@ -49,7 +49,7 @@
 </histogram>
 
 <histogram name="Session.IsActive" enum="BooleanActive"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>rogerm@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
@@ -655,7 +655,7 @@
 </histogram>
 
 <histogram name="SessionRestore.ForegroundTabFirstPaint4" units="ms"
-    expires_after="2022-04-20">
+    expires_after="2022-06-19">
   <owner>chrisha@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -668,7 +668,7 @@
 </histogram>
 
 <histogram name="SessionRestore.ForegroundTabFirstPaint4.FinishReason"
-    enum="SessionRestoreFinishReason" expires_after="2022-04-20">
+    enum="SessionRestoreFinishReason" expires_after="2022-06-19">
   <owner>chrisha@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/settings/histograms.xml b/tools/metrics/histograms/metadata/settings/histograms.xml
index 82eeae8..08a6ee7e 100644
--- a/tools/metrics/histograms/metadata/settings/histograms.xml
+++ b/tools/metrics/histograms/metadata/settings/histograms.xml
@@ -23,7 +23,7 @@
 <histograms>
 
 <histogram name="Settings.AdvancedSpellcheck.OnStartup" enum="BooleanEnabled"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>harrisonsean@chromium.org</owner>
   <owner>chrome-friendly-settings@google.com</owner>
   <summary>
@@ -78,7 +78,7 @@
 </histogram>
 
 <histogram name="Settings.GivenShowHomeButton_HomePageIsNewTabPage"
-    enum="Boolean" expires_after="2022-04-17">
+    enum="Boolean" expires_after="2022-06-19">
   <owner>mpearson@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -198,7 +198,7 @@
 </histogram>
 
 <histogram name="Settings.PreloadStatus.OnStartup" enum="BooleanEnabled"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>harrisonsean@chromium.org</owner>
   <owner>chrome-friendly-settings@google.com</owner>
   <summary>
@@ -355,7 +355,7 @@
 </histogram>
 
 <histogram name="Settings.ShowHomeButton" enum="BooleanEnabled"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>mpearson@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sharing/histograms.xml b/tools/metrics/histograms/metadata/sharing/histograms.xml
index 10a40e46..0722717 100644
--- a/tools/metrics/histograms/metadata/sharing/histograms.xml
+++ b/tools/metrics/histograms/metadata/sharing/histograms.xml
@@ -54,7 +54,7 @@
 </histogram>
 
 <histogram name="Sharing.ClickToCallDevicesToShow" units="devices"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
 <!-- Name completed by histogram_suffixes name="SharingClickToCallUi" -->
 
   <owner>mvanouwerkerk@chromium.org</owner>
@@ -135,7 +135,7 @@
 </histogram>
 
 <histogram name="Sharing.DefaultSharesheetAndroid.Opened" enum="ShareOrigin"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>sophey@chromium.org</owner>
   <owner>src/chrome/browser/share/OWNERS</owner>
   <summary>
@@ -498,7 +498,7 @@
 </histogram>
 
 <histogram name="Sharing.SendTabToSelf.NotificationStatus"
-    enum="SendTabToSelfNotificationStatus" expires_after="2022-04-17">
+    enum="SendTabToSelfNotificationStatus" expires_after="2022-06-19">
   <owner>ellyjones@chromium.org</owner>
   <owner>src/chrome/browser/share/OWNERS</owner>
   <summary>
@@ -606,7 +606,7 @@
 </histogram>
 
 <histogram name="Sharing.SharingHubAndroid.Opened" enum="ShareOrigin"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>sophey@chromium.org</owner>
   <owner>src/chrome/browser/share/OWNERS</owner>
   <summary>
@@ -631,7 +631,7 @@
 </histogram>
 
 <histogram name="Sharing.SharingHubAndroid.ThirdPartyAppUsage"
-    enum="SharingHubBottomRowIndex" expires_after="2022-04-17">
+    enum="SharingHubBottomRowIndex" expires_after="2022-06-19">
   <owner>sophey@chromium.org</owner>
   <owner>src/chrome/browser/share/OWNERS</owner>
   <summary>
@@ -653,7 +653,7 @@
 </histogram>
 
 <histogram name="Sharing.SharingHubAndroid.TimeToShare" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>sophey@chromium.org</owner>
   <owner>src/chrome/browser/share/OWNERS</owner>
   <summary>
@@ -663,7 +663,7 @@
 </histogram>
 
 <histogram name="Sharing.SharingHubAndroid.TimeToShowShareSheet" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>sophey@chromium.org</owner>
   <owner>src/chrome/browser/share/OWNERS</owner>
   <summary>
@@ -706,7 +706,7 @@
 </histogram>
 
 <histogram name="Sharing.SmsFetcherAvailableDeviceCount" units="devices"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>yigu@chromium.org</owner>
   <owner>web-identity@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/signin/histograms.xml b/tools/metrics/histograms/metadata/signin/histograms.xml
index 0cdcca9..6c27c50 100644
--- a/tools/metrics/histograms/metadata/signin/histograms.xml
+++ b/tools/metrics/histograms/metadata/signin/histograms.xml
@@ -160,7 +160,7 @@
 </histogram>
 
 <histogram name="Signin.AccountFetcher.AccountAvatarFetchTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>triploblastic@chromium.org</owner>
   <owner>aliceywang@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
@@ -367,7 +367,7 @@
 </histogram>
 
 <histogram name="Signin.AuthError" enum="GoogleServiceAuthError"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>msarda@chromium.org</owner>
   <owner>droger@chromium.org</owner>
   <summary>
@@ -509,7 +509,7 @@
 </histogram>
 
 <histogram name="Signin.Extensions.GaiaRemoteConsentFlowResult"
-    enum="GaiaRemoteConsentFlowResult" expires_after="2022-03-12">
+    enum="GaiaRemoteConsentFlowResult" expires_after="2022-06-19">
   <owner>alexilin@chromium.org</owner>
   <owner>droger@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/software/histograms.xml b/tools/metrics/histograms/metadata/software/histograms.xml
index bb2e483..497e5ba 100644
--- a/tools/metrics/histograms/metadata/software/histograms.xml
+++ b/tools/metrics/histograms/metadata/software/histograms.xml
@@ -289,7 +289,7 @@
 </histogram>
 
 <histogram name="SoftwareReporter.MajorVersion" units="units"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/startup/histograms.xml b/tools/metrics/histograms/metadata/startup/histograms.xml
index e8399ec37..951a878 100644
--- a/tools/metrics/histograms/metadata/startup/histograms.xml
+++ b/tools/metrics/histograms/metadata/startup/histograms.xml
@@ -32,7 +32,7 @@
 </histogram>
 
 <histogram name="Startup.AfterStartupTaskDelayedUntilTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>michaeln@chromium.org</owner>
   <summary>
     Time from the process creation until deferred after-startup tasks began
@@ -117,11 +117,14 @@
 </histogram>
 
 <histogram name="Startup.Android.Cold.TimeToFirstVisibleContent" units="ms"
-    expires_after="2022-06-05">
+    expires_after="never">
+<!-- expires-never: guiding metric (internal: go/chrome-browser-guiding-metrics) -->
+
   <owner>yfriedman@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
   <owner>yashard@chromium.org</owner>
+  <owner>chrome-analysis-team@google.com</owner>
   <summary>
     The time from Chrome tabbed activity creation to the moment the Chrome first
     appears ready.
@@ -167,6 +170,9 @@
     - If the app opens to the Chrome Start page.
 
     - For WebApps, PWAs or WebApks.
+
+    This histogram is of special interest to the chrome-analysis-team@. Do not
+    change its semantics or retire it without talking to them first.
   </summary>
 </histogram>
 
@@ -190,7 +196,7 @@
 </histogram>
 
 <histogram base="true" name="Startup.Android.FeedContentFirstLoadedTime"
-    units="ms" expires_after="2022-04-17">
+    units="ms" expires_after="2022-06-19">
 <!-- Name completed by histogram_suffixes name="JavaStartMode" -->
 
   <owner>hanxi@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/subresource/histograms.xml b/tools/metrics/histograms/metadata/subresource/histograms.xml
index ff36f289..5067ffc 100644
--- a/tools/metrics/histograms/metadata/subresource/histograms.xml
+++ b/tools/metrics/histograms/metadata/subresource/histograms.xml
@@ -141,7 +141,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.CnameAlias.Renderer.ListLength"
-    units="length" expires_after="2022-04-17">
+    units="length" expires_after="2022-06-19">
   <owner>cammie@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -675,7 +675,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.SafeBrowsing.TotalCheckTime" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -689,7 +689,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.SubresourceLoad.Evaluation.CPUDuration"
-    units="microseconds" expires_after="2022-04-17">
+    units="microseconds" expires_after="2022-06-19">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -774,7 +774,7 @@
 </histogram>
 
 <histogram name="SubresourceRedirect.CompressionAttempt.ResponseCode"
-    enum="HttpResponseCode" expires_after="2022-04-17">
+    enum="HttpResponseCode" expires_after="2022-06-19">
   <owner>robertogden@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index db6f464..2fe3d20 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -261,7 +261,7 @@
 </histogram>
 
 <histogram name="Sync.BookmarkModelMerger.UnsyncedEntitiesUponCompletion"
-    units="bookmarks" expires_after="2022-04-17">
+    units="bookmarks" expires_after="2022-06-19">
   <owner>rushans@google.com</owner>
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -1376,7 +1376,7 @@
 </histogram>
 
 <histogram name="Sync.PostedClientToServerMessage"
-    enum="SyncClientToServerMessageContents" expires_after="2022-04-17">
+    enum="SyncClientToServerMessageContents" expires_after="2022-06-19">
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
   <summary>
@@ -1404,7 +1404,7 @@
 </histogram>
 
 <histogram name="Sync.PostedClientToServerMessageError2" enum="SyncErrorType"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>mastiz@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -1480,7 +1480,7 @@
 </histogram>
 
 <histogram name="Sync.ProblematicServerSideBookmarks"
-    enum="RemoteBookmarkUpdateError" expires_after="2022-04-17">
+    enum="RemoteBookmarkUpdateError" expires_after="2022-06-19">
   <owner>mastiz@chromium.org</owner>
   <owner>rushans@google.com</owner>
   <component>Services&gt;Sync</component>
@@ -1710,7 +1710,7 @@
 </histogram>
 
 <histogram name="Sync.SyncErrorInfobarDisplayed" enum="SyncErrorInfobarTypes"
-    expires_after="2022-04-10">
+    expires_after="2022-06-19">
   <owner>fernandex@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <component>Services&gt;Sync</component>
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml
index 4b41267..350bc03 100644
--- a/tools/metrics/histograms/metadata/tab/histograms.xml
+++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -112,7 +112,7 @@
   </summary>
 </histogram>
 
-<histogram name="Tab.Count.Guest" units="units" expires_after="2022-04-17">
+<histogram name="Tab.Count.Guest" units="units" expires_after="2022-06-19">
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-incognito@google.com</owner>
   <summary>
@@ -145,7 +145,7 @@
 </histogram>
 
 <histogram name="Tab.ExternalApplicationOpened" enum="ExternalLauncherOption"
-    expires_after="2022-04-10">
+    expires_after="2022-06-19">
   <owner>mrefaat@chromium.org</owner>
   <owner>bling-team@google.com</owner>
   <summary>
@@ -287,7 +287,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.TimeToFirstUsableFrameAfterStartCapture"
-    units="ms" expires_after="2022-04-17">
+    units="ms" expires_after="2022-06-19">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -297,7 +297,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.TimeToNotifyObserversAfterCaptureReceived"
-    units="microseconds" expires_after="2022-04-17">
+    units="microseconds" expires_after="2022-06-19">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -311,7 +311,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.TimeToStoreAfterFrameReceived"
-    units="microseconds" expires_after="2022-04-17">
+    units="microseconds" expires_after="2022-06-19">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -323,7 +323,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.TimeToStoreAfterTabSwitch" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -710,7 +710,7 @@
 </histogram>
 
 <histogram name="TabGroups.UserCustomizedGroupCountPerLoad" units="groups"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>connily@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -1011,7 +1011,7 @@
 </histogram>
 
 <histogram name="TabManager.Discarding.DiscardCount" units="Discards"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>chrisha@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -1070,7 +1070,7 @@
 </histogram>
 
 <histogram name="TabManager.Discarding.ReloadCount" units="Reloads"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>chrisha@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -1906,7 +1906,7 @@
 </histogram>
 
 <histogram name="Tabs.SadTab.Reload.Event" enum="SadTabEvent"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>sonnyrao@chromium.org</owner>
   <owner>jamescook@chromium.org</owner>
   <summary>
@@ -2916,7 +2916,7 @@
   <token key="BatteryState" variants="BatteryState"/>
 </histogram>
 
-<histogram name="Tabs.WindowWidth" units="DIPs" expires_after="2022-04-17">
+<histogram name="Tabs.WindowWidth" units="DIPs" expires_after="2022-06-19">
   <owner>collinbaker@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/ukm/histograms.xml b/tools/metrics/histograms/metadata/ukm/histograms.xml
index 6513332..46d0eac 100644
--- a/tools/metrics/histograms/metadata/ukm/histograms.xml
+++ b/tools/metrics/histograms/metadata/ukm/histograms.xml
@@ -81,7 +81,7 @@
 </histogram>
 
 <histogram name="UKM.Entries.Dropped.ByEntryHash" enum="UkmEventNameHash"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>jwd@chromium.org</owner>
   <owner>ukm-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/v8/histograms.xml b/tools/metrics/histograms/metadata/v8/histograms.xml
index d233d0f..5b4874a 100644
--- a/tools/metrics/histograms/metadata/v8/histograms.xml
+++ b/tools/metrics/histograms/metadata/v8/histograms.xml
@@ -162,7 +162,7 @@
 </histogram>
 
 <histogram name="V8.CompileScriptMicroSeconds" units="microseconds"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
   <summary>
@@ -194,7 +194,7 @@
 </histogram>
 
 <histogram name="V8.CompileScriptMicroSeconds.ConsumeCache"
-    units="microseconds" expires_after="2022-04-17">
+    units="microseconds" expires_after="2022-06-19">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
   <summary>
@@ -490,7 +490,7 @@
 </histogram>
 
 <histogram name="V8.GC.Cycle.Full.Mark.Cpp" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>omerkatz@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>
@@ -601,7 +601,7 @@
 </histogram>
 
 <histogram name="V8.GC.Cycle.MainThread.Full.Mark.Cpp" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>omerkatz@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>
@@ -683,6 +683,16 @@
   </summary>
 </histogram>
 
+<histogram name="V8.GC.Cycle.Reason.Young" enum="GarbageCollectionReason"
+    expires_after="M100">
+  <owner>nikolaos@chromium.org</owner>
+  <owner>v8-memory-sheriffs@google.com</owner>
+  <summary>
+    Reason a young generation garbage collection was started in V8. Reported at
+    the end of the garbage collection cycle.
+  </summary>
+</histogram>
+
 <histogram name="V8.GC.Cycle.Young" units="ms" expires_after="2022-05-22">
   <owner>nikolaos@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
@@ -875,7 +885,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCFinalizeMC" units="ms" expires_after="2022-04-17">
+<histogram name="V8.GCFinalizeMC" units="ms" expires_after="2022-06-19">
   <owner>mlippautz@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
@@ -1039,7 +1049,7 @@
 </histogram>
 
 <histogram name="V8.GCIncrementalMarkingSum" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>mlippautz@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>
@@ -1576,7 +1586,7 @@
 </histogram>
 
 <histogram name="V8.WasmCompileFunctionPeakMemoryBytes" units="bytes"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>ecmziegler@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensb@chromium.org</owner>
@@ -1598,7 +1608,7 @@
 </histogram>
 
 <histogram name="V8.WasmCompileHugeFunctionPeakMemoryBytes" units="bytes"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>clemensb@chromium.org</owner>
   <owner>ecmziegler@chromium.org</owner>
   <summary>
@@ -1664,7 +1674,7 @@
 </histogram>
 
 <histogram name="V8.WasmFinishModuleStreamingMicroSeconds" units="microseconds"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>bbudge@chromium.org</owner>
   <owner>clemensb@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
@@ -1694,7 +1704,7 @@
 </histogram>
 
 <histogram name="V8.WasmFunctionsPerModule" units="functions"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>ecmziegler@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensb@chromium.org</owner>
@@ -1911,7 +1921,7 @@
 </histogram>
 
 <histogram name="V8.WasmTierUpModuleMicroSeconds" units="microseconds"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>ecmziegler@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensb@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/variations/histograms.xml b/tools/metrics/histograms/metadata/variations/histograms.xml
index 844f3760b..6b9ad62 100644
--- a/tools/metrics/histograms/metadata/variations/histograms.xml
+++ b/tools/metrics/histograms/metadata/variations/histograms.xml
@@ -47,7 +47,7 @@
 </histogram>
 
 <histogram name="Variations.AppSeedFreshness" units="minutes"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>rmcelrath@chromium.org</owner>
   <owner>ntfschr@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
diff --git a/tools/metrics/histograms/metadata/web_apk/histograms.xml b/tools/metrics/histograms/metadata/web_apk/histograms.xml
index a25d717..43fa1c0 100644
--- a/tools/metrics/histograms/metadata/web_apk/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_apk/histograms.xml
@@ -83,7 +83,7 @@
 </histogram>
 
 <histogram name="WebApk.Install.InstallDuration" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>hartmanng@chromium.org</owner>
   <owner>
     src/chrome/android/java/src/org/chromium/chrome/browser/webapps/OWNERS
diff --git a/tools/metrics/histograms/metadata/web_audio/histograms.xml b/tools/metrics/histograms/metadata/web_audio/histograms.xml
index 11d54462..359bf21 100644
--- a/tools/metrics/histograms/metadata/web_audio/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_audio/histograms.xml
@@ -70,7 +70,7 @@
 </histogram>
 
 <histogram name="WebAudio.AudioContext.HardwareSampleRate" units="Hz"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>hongchan@chromium.org</owner>
   <summary>
     The hardware sample rate (in Hz) used by an AudioContext. Recorded for every
diff --git a/tools/metrics/histograms/metadata/web_core/histograms.xml b/tools/metrics/histograms/metadata/web_core/histograms.xml
index fc5e6896..b2c3d746 100644
--- a/tools/metrics/histograms/metadata/web_core/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_core/histograms.xml
@@ -31,7 +31,7 @@
 </variants>
 
 <histogram name="WebCore.DistillabilityUs" units="microseconds"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>wychen@chromium.org</owner>
   <owner>gilmanmh@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/web_rtc/histograms.xml b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
index bea07f4..92eacb2 100644
--- a/tools/metrics/histograms/metadata/web_rtc/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
@@ -180,7 +180,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.Agc.DigitalGainApplied" units="dB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>alessiob@chromium.org</owner>
   <summary>
     Logs adaptive digital compression gain that is applied by AgcManagerDirect.
@@ -212,7 +212,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.Agc2.DigitalGainApplied" units="dB"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>alessiob@chromium.org</owner>
   <summary>
     Logs adaptive digital compression gain that is applied by
@@ -381,7 +381,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.ApmCaptureInputLevelPeakRms"
-    units="dBFS (negated)" expires_after="2022-04-17">
+    units="dBFS (negated)" expires_after="2022-06-19">
   <owner>hlundin@chromium.org</owner>
   <summary>
     This histogram reports the peak RMS of the signal coming in to WebRTC's
@@ -1213,7 +1213,7 @@
 </histogram>
 
 <histogram name="WebRTC.Call.TimeReceivingVideoRtpPacketsInSeconds" units="s"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>saza@chromium.org</owner>
   <summary>
     The amount of time between the arrival of the first and last video RTP
@@ -1305,7 +1305,7 @@
 </histogram>
 
 <histogram name="WebRTC.DesktopCapture.Win.DesktopCapturerImpl"
-    enum="WebRtcDesktopCapturerImpl" expires_after="2021-12-31">
+    enum="WebRtcDesktopCapturerImpl" expires_after="2022-04-01">
   <owner>jamiewalch@chromium.org</owner>
   <owner>auorion@microsoft.com</owner>
   <owner>edgecapabilitiesdev@microsoft.com</owner>
@@ -1316,7 +1316,7 @@
 </histogram>
 
 <histogram name="WebRTC.DesktopCapture.Win.WgcCapturerResult"
-    enum="WebRtcWgcCapturerResult" expires_after="2021-12-31">
+    enum="WebRtcWgcCapturerResult" expires_after="2022-04-01">
   <owner>jamiewalch@chromium.org</owner>
   <owner>auorion@microsoft.com</owner>
   <owner>edgecapabilitiesdev@microsoft.com</owner>
@@ -1327,7 +1327,7 @@
 </histogram>
 
 <histogram name="WebRTC.DesktopCapture.Win.WgcCaptureSessionGetFrameResult"
-    enum="WebRtcWgcCaptureSessionGetFrameResult" expires_after="2021-12-31">
+    enum="WebRtcWgcCaptureSessionGetFrameResult" expires_after="2022-04-01">
   <owner>jamiewalch@chromium.org</owner>
   <owner>auorion@microsoft.com</owner>
   <owner>edgecapabilitiesdev@microsoft.com</owner>
@@ -1337,7 +1337,7 @@
 </histogram>
 
 <histogram name="WebRTC.DesktopCapture.Win.WgcCaptureSessionStartResult"
-    enum="WebRtcWgcCaptureSessionStartResult" expires_after="2021-12-31">
+    enum="WebRtcWgcCaptureSessionStartResult" expires_after="2022-04-01">
   <owner>jamiewalch@chromium.org</owner>
   <owner>auorion@microsoft.com</owner>
   <owner>edgecapabilitiesdev@microsoft.com</owner>
@@ -1347,7 +1347,7 @@
 </histogram>
 
 <histogram name="WebRTC.DesktopCapture.Win.{Capturer}CapturerFrameTime"
-    units="ms" expires_after="2021-12-31">
+    units="ms" expires_after="2022-04-01">
   <owner>jamiewalch@chromium.org</owner>
   <owner>auorion@microsoft.com</owner>
   <owner>edgecapabilitiesdev@microsoft.com</owner>
@@ -1482,7 +1482,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.ConnectionState"
-    enum="IceConnectionStates" expires_after="2022-04-17">
+    enum="IceConnectionStates" expires_after="2022-06-19">
   <owner>qingsi@google.com</owner>
   <owner>jeroendb@google.com</owner>
   <summary>
@@ -2060,7 +2060,7 @@
 </histogram>
 
 <histogram name="WebRTC.ReliableDataChannelMessageSize" units="bytes"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>perkj@chromium.org</owner>
   <summary>
     Sizes of messages sent over reliable data channels. The size of an
@@ -2278,7 +2278,7 @@
 </histogram>
 
 <histogram name="WebRTC.UnreliableDataChannelMessageSize" units="bytes"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>perkj@chromium.org</owner>
   <summary>
     Sizes of messages sent over unreliable data channels. The size of an
diff --git a/tools/metrics/histograms/metadata/webapps/histograms.xml b/tools/metrics/histograms/metadata/webapps/histograms.xml
index c743ae7..797fd40 100644
--- a/tools/metrics/histograms/metadata/webapps/histograms.xml
+++ b/tools/metrics/histograms/metadata/webapps/histograms.xml
@@ -147,7 +147,7 @@
 </histogram>
 
 <histogram name="Launch.WebAppDisplayMode" enum="WebAppDisplayMode"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>peter@chromium.org</owner>
   <owner>yfriedman@chromium.org</owner>
   <summary>
@@ -157,7 +157,7 @@
 </histogram>
 
 <histogram name="Webapp.AddToHomescreenDialog.Timeout" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>dominickn@chromium.org</owner>
   <owner>peconn@chromium.org</owner>
   <summary>
@@ -177,7 +177,7 @@
 </histogram>
 
 <histogram name="Webapp.CheckServiceWorker.Status"
-    enum="ServiceWorkerOfflineCapability" expires_after="2022-04-17">
+    enum="ServiceWorkerOfflineCapability" expires_after="2022-06-19">
   <owner>asamidoi@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -189,7 +189,7 @@
 </histogram>
 
 <histogram name="Webapp.CheckServiceWorker.Time" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>asamidoi@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -448,7 +448,7 @@
 </histogram>
 
 <histogram name="WebApp.Preinstalled.AppToReplaceStillInstalledCount"
-    units="apps" expires_after="2022-04-17">
+    units="apps" expires_after="2022-06-19">
   <owner>alancutter@chromium.org</owner>
   <owner>desktop-pwas-team@google.com</owner>
   <summary>
@@ -465,7 +465,7 @@
 </histogram>
 
 <histogram name="WebApp.Preinstalled.AppToReplaceStillInstalledInShelfCount"
-    units="apps" expires_after="2022-04-17">
+    units="apps" expires_after="2022-06-19">
   <owner>alancutter@chromium.org</owner>
   <owner>desktop-pwas-team@google.com</owner>
   <summary>
@@ -492,7 +492,7 @@
 </histogram>
 
 <histogram name="WebApp.Preinstalled.DisabledCount" units="apps"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>alancutter@chromium.org</owner>
   <owner>desktop-pwas-team@google.com</owner>
   <summary>
@@ -512,7 +512,7 @@
 </histogram>
 
 <histogram name="WebApp.Preinstalled.UninstallAndReplaceCount" units="apps"
-    expires_after="2022-04-17">
+    expires_after="2022-06-19">
   <owner>alancutter@chromium.org</owner>
   <owner>desktop-pwas-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 222792e..63c4d36 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -8889,7 +8889,6 @@
 </event>
 
 <event name="IOS.IsDefaultBrowser">
-  <owner>javierrobles@chromium.org</owner>
   <owner>rkgibson@chromium.org</owner>
   <summary>
     As of iOS14, users will be able to set a default browser other than Safari.
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index c745589..7f56dfa 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,20 +5,20 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "083528a13c67adcf55a6cd1d79ec4980e526638a",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/cefb3e0ec3a0580c996f801e854fe02963c03d5c/trace_processor_shell.exe"
+            "hash": "0931de4613dbe928517484045db58b281acf6329",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/183e582aa736806e0d9dd6f16afbb7ce866123e3/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "84a4b4eeb5acdf697dc199eab292ecdff6bc4c71",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/cefb3e0ec3a0580c996f801e854fe02963c03d5c/trace_processor_shell"
+            "hash": "3e6d109ef4b029fcc94596ecb18191b579906172",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/183e582aa736806e0d9dd6f16afbb7ce866123e3/trace_processor_shell"
         },
         "linux_arm64": {
             "hash": "5074025a2898ec41a872e70a5719e417acb0a380",
             "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "linux": {
-            "hash": "9e5e4909b2b6a6f4e1aeb2b5bc081567935aeea0",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/cefb3e0ec3a0580c996f801e854fe02963c03d5c/trace_processor_shell"
+            "hash": "ff013c15176d6caafefcf29cb1537eab9d4252eb",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/183e582aa736806e0d9dd6f16afbb7ce866123e3/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/style_variable_generator/base_generator.py b/tools/style_variable_generator/base_generator.py
index 5fe0e84..0efa4e2 100644
--- a/tools/style_variable_generator/base_generator.py
+++ b/tools/style_variable_generator/base_generator.py
@@ -147,6 +147,10 @@
         if isinstance(value_obj, dict):
             generate_per_mode = value_obj.pop('generate_per_mode', None)
             generate_inverted = value_obj.pop('generate_inverted', None)
+        elif self._CreateValue(value_obj).blended_colors:
+            # A blended color could evaluate to different colors in different
+            # modes, so add it to all the modes.
+            value_obj = {mode: value_obj for mode in Modes.ALL}
 
         generated_context = dict(context)
         generated_context['generated'] = True
diff --git a/tools/style_variable_generator/blend_colors_test.json5 b/tools/style_variable_generator/blend_colors_test.json5
new file mode 100644
index 0000000..7d50a09
--- /dev/null
+++ b/tools/style_variable_generator/blend_colors_test.json5
@@ -0,0 +1,25 @@
+{
+  options: {
+    CSS: {
+      prefix: 'cros'
+    },
+  },
+  colors: {
+    bg_color: {
+      light: "$white",
+      dark: "$google_grey_900",
+    },
+    highlight_color_hover: {
+      light: "rgba($black_rgb, 0.2)",
+      dark: "rgba($white_rgb, 0.2)",
+    },
+    foo_color: {
+      light: "blend($highlight_color_hover, $bg_color)",
+      dark: "blend($highlight_color_hover, $bg_color)",
+    },
+    bar_color: "blend($highlight_color_hover, $bg_color)",
+  },
+  opacities: {
+    disabled_opacity: 0.38,
+  },
+}
diff --git a/tools/style_variable_generator/blend_colors_test_expected.css b/tools/style_variable_generator/blend_colors_test_expected.css
new file mode 100644
index 0000000..fa9b779
--- /dev/null
+++ b/tools/style_variable_generator/blend_colors_test_expected.css
@@ -0,0 +1,43 @@
+/* Copyright 2020 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+/* This file is generated from:
+ *  blend_colors_test.json5
+ *  colors_test_palette.json5
+ */
+
+html:not(body) {
+  --google-grey-900-rgb: 32, 33, 36;
+  --google-grey-900: rgb(var(--google-grey-900-rgb));
+
+  --cros-bg-color-rgb: 255, 255, 255;
+  --cros-bg-color: rgb(var(--cros-bg-color-rgb));
+
+  --cros-highlight-color-hover-rgb: 0, 0, 0;
+  --cros-highlight-color-hover: rgba(var(--cros-highlight-color-hover-rgb), 0.2);
+
+  --cros-foo-color-rgb: 204, 204, 204;
+  --cros-foo-color: rgb(var(--cros-foo-color-rgb));
+
+  --cros-bar-color-rgb: 204, 204, 204;
+  --cros-bar-color: rgb(var(--cros-bar-color-rgb));
+
+  --cros-disabled-opacity: 0.38;
+}
+
+@media (prefers-color-scheme: dark) {
+html:not(body) {
+  --cros-bg-color-rgb: var(--google-grey-900-rgb);
+  --cros-bg-color: var(--google-grey-900);
+
+  --cros-highlight-color-hover-rgb: 255, 255, 255;
+  --cros-highlight-color-hover: rgba(var(--cros-highlight-color-hover-rgb), 0.2);
+
+  --cros-foo-color-rgb: 77, 77, 80;
+  --cros-foo-color: rgb(var(--cros-foo-color-rgb));
+
+  --cros-bar-color-rgb: 77, 77, 80;
+  --cros-bar-color: rgb(var(--cros-bar-color-rgb));
+}
+}
diff --git a/tools/style_variable_generator/style_variable_generator_test.py b/tools/style_variable_generator/style_variable_generator_test.py
index ced1a69..b536109 100755
--- a/tools/style_variable_generator/style_variable_generator_test.py
+++ b/tools/style_variable_generator/style_variable_generator_test.py
@@ -139,6 +139,14 @@
         self.expected_output_file = 'colors_test_expected.protojson'
 
 
+class BlendStyleGeneratorTest(unittest.TestCase, BaseStyleGeneratorTest):
+    def setUp(self):
+        self.generator = CSSStyleGenerator()
+        self.generator.AddJSONFilesToModel(
+            ['colors_test_palette.json5', 'blend_colors_test.json5'])
+        self.expected_output_file = 'blend_colors_test_expected.css'
+
+
 class InvertedStyleGeneratorTest(unittest.TestCase, BaseStyleGeneratorTest):
     def setUp(self):
         self.generator = CSSStyleGenerator()
diff --git a/tools/traffic_annotation/auditor/chromeos/safe_list.txt b/tools/traffic_annotation/auditor/chromeos/safe_list.txt
index 17248a37..d30a6fbc 100644
--- a/tools/traffic_annotation/auditor/chromeos/safe_list.txt
+++ b/tools/traffic_annotation/auditor/chromeos/safe_list.txt
@@ -2,28 +2,23 @@
 # Please do not add any new safelist.
 #
 missing,chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl.cc
-missing,chrome/browser/ash/printing/server_printers_fetcher.cc
 missing,chrome/browser/ui/ash/thumbnail_loader.cc
 missing,chrome/browser/ash/printing/server_printers_fetcher.cc
 missing,chrome/browser/ash/customization/customization_wallpaper_downloader.cc
+missing,ash/components/device_activity/device_activity_client.cc
 all,chromeos/printing/printer_config_cache.cc
 all,chrome/browser/ash/customization/customization_document.cc
 all,ash/webui/projector_app/projector_xhr_sender.cc
 all,ash/components/geolocation/simple_geolocation_request.cc
-all,ash/components/timezone/timezone_request.ccall,chromeos/printing/printer_config_cache.cc
 all,ash/wallpaper/wallpaper_controller_impl.cc
-all,ash/webui/projector_app/projector_xhr_sender.cc
-all,ash/components/geolocation/simple_geolocation_request.cc
 all,ash/components/timezone/timezone_request.cc
 all,ash/components/trial_group/trial_group_checker.cc
 all,chrome/browser/ash/net/network_portal_detector_impl.cc
 all,chrome/browser/ash/policy/uploading/system_log_uploader.cc
 all,chrome/browser/ui/ash/assistant/search_and_assistant_enabled_checker.cc
 all,chromeos/geolocation/simple_geolocation_request.cc
-all,chromeos/timezone/timezone_request.cc
 all,components/quirks/quirks_client.cc
 all,chromeos/timezone/timezone_request.cc
-all,chromeos/geolocation/simple_geolocation_request.cc
 all,chromeos/services/device_sync/cryptauth_client_impl.cc
 all,chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
 all,chrome/services/sharing/nearby/platform/webrtc.cc
@@ -38,9 +33,7 @@
 all,chrome/browser/ash/net/network_diagnostics/tls_prober.cc
 all,ash/quick_pair/repository/fast_pair/fast_pair_image_decoder.cc
 all,ash/quick_pair/repository/fast_pair/device_metadata_fetcher.cc
-all,chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
 all,chrome/browser/search/background/ntp_background_service.cc
-all,chrome/browser/ash/net/network_diagnostics/tls_prober.cc
 all,chromeos/components/quick_answers/result_loader.cc
 all,chrome/browser/apps/app_service/webapk/webapk_install_task.cc
 all,chrome/browser/ash/login/saml/password_sync_token_fetcher.cc
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index a1029d24..7e86dc9 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -347,4 +347,7 @@
  <item id="fedcm" added_in_milestone="98" content_hash_code="02f3cbfc" os_list="linux,windows,chromeos,android" file_path="content/browser/webid/idp_network_request_manager.cc" />
  <item id="minidump_uploader_android" added_in_milestone="98" content_hash_code="0327544e" os_list="android" file_path="components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/util/HttpURLConnectionFactoryImpl.java" />
  <item id="chrome_variations_android" added_in_milestone="98" content_hash_code="04eeb61f" os_list="android" file_path="components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java" />
+ <item id="wallpaper_google_photos_count" added_in_milestone="99" content_hash_code="073c3f10" os_list="chromeos" file_path="chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc" />
+ <item id="backdrop_images_info_download" added_in_milestone="99" content_hash_code="030771dc" os_list="chromeos" file_path="chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc" />
+ <item id="backdrop_surprise_me_image_download" added_in_milestone="99" content_hash_code="06f0b87b" os_list="chromeos" file_path="chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc" />
 </annotations>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index 9944f59..8caaba5e 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -56,6 +56,8 @@
       <traffic_annotation unique_id="ambient_photo_cache"/>
       <traffic_annotation unique_id="ambient_photo_controller"/>
       <traffic_annotation unique_id="app_suggestion_get_favicon"/>
+      <traffic_annotation unique_id="backdrop_images_info_download"/>
+      <traffic_annotation unique_id="backdrop_surprise_me_image_download"/>
       <traffic_annotation unique_id="arc_auth_code_fetcher"/>
       <traffic_annotation unique_id="calendar_get_events"/>
       <traffic_annotation unique_id="chrome_plugin_vm_api"/>
@@ -81,6 +83,7 @@
       <traffic_annotation unique_id="terms_of_service_fetch"/>
       <traffic_annotation unique_id="url_icon_source_fetch"/>
       <traffic_annotation unique_id="wallpaper_fetcher"/>
+      <traffic_annotation unique_id="wallpaper_google_photos_count"/>
     </sender>
     <!-- ChromeOS-specific features -->
     <sender name="ChromeOS Recovery">
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index a66cecb..38753d9 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -390,7 +390,7 @@
     const AXTreeManager* manager =
         AXTreeManagerMap::GetInstance().GetManager(tree_id());
     if (manager)
-      return manager->GetNodeFromTree(tree_id(), anchor_id());
+      return manager->GetNodeFromTree(anchor_id());
 
     return nullptr;
   }
diff --git a/ui/accessibility/ax_tree_manager_map.cc b/ui/accessibility/ax_tree_manager_map.cc
index 5fb9cf3..8e12b19 100644
--- a/ui/accessibility/ax_tree_manager_map.cc
+++ b/ui/accessibility/ax_tree_manager_map.cc
@@ -34,10 +34,13 @@
 }
 
 AXTreeManager* AXTreeManagerMap::GetManager(AXTreeID tree_id) {
-  if (tree_id == AXTreeIDUnknown() || !base::Contains(map_, tree_id))
+  if (tree_id == AXTreeIDUnknown())
+    return nullptr;
+  auto iter = map_.find(tree_id);
+  if (iter == map_.end())
     return nullptr;
 
-  return map_.at(tree_id);
+  return iter->second;
 }
 
 AXTreeManager* AXTreeManagerMap::GetManagerForChildTree(
diff --git a/ui/accessibility/platform/BUILD.gn b/ui/accessibility/platform/BUILD.gn
index 853cb4b9..1d62612 100644
--- a/ui/accessibility/platform/BUILD.gn
+++ b/ui/accessibility/platform/BUILD.gn
@@ -160,6 +160,8 @@
         "ax_private_roles_mac.h",
         "ax_private_webkit_constants_mac.h",
         "ax_private_webkit_constants_mac.mm",
+        "ax_utils_mac.h",
+        "ax_utils_mac.mm",
         "inspect/ax_inspect_utils_mac.h",
         "inspect/ax_inspect_utils_mac.mm",
         "inspect/ax_transform_mac.h",
diff --git a/ui/accessibility/platform/ax_platform_node_delegate.h b/ui/accessibility/platform/ax_platform_node_delegate.h
index 3d1374c8..e9653804 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate.h
@@ -70,6 +70,10 @@
 // otherwise.
 class AX_EXPORT AXPlatformNodeDelegate {
  public:
+  using AXPosition = ui::AXNodePosition::AXPositionInstance;
+  using SerializedPosition = ui::AXNodePosition::SerializedPosition;
+  using AXRange = ui::AXRange<AXPosition::element_type>;
+
   AXPlatformNodeDelegate(const AXPlatformNodeDelegate&) = delete;
   AXPlatformNodeDelegate& operator=(const AXPlatformNodeDelegate&) = delete;
 
diff --git a/ui/accessibility/platform/ax_utils_mac.h b/ui/accessibility/platform/ax_utils_mac.h
new file mode 100644
index 0000000..e5a83b1
--- /dev/null
+++ b/ui/accessibility/platform/ax_utils_mac.h
@@ -0,0 +1,32 @@
+// 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 UI_ACCESSIBILITY_PLATFORM_AX_UTILS_MAC_H_
+#define UI_ACCESSIBILITY_PLATFORM_AX_UTILS_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "ui/accessibility/ax_export.h"
+#include "ui/accessibility/platform/ax_platform_node_delegate.h"
+
+namespace ui {
+
+// Uses a system API to verify that the given object is an AXTextMarker object.
+AX_EXPORT bool IsAXTextMarker(id text_marker);
+
+// Uses a system API to verify that the given object is an AXTextMarkerRange
+// object.
+AX_EXPORT bool IsAXTextMarkerRange(id marker_range);
+
+// Returns the AXNodePosition representing the given AXTextMarker.
+AX_EXPORT AXPlatformNodeDelegate::AXPosition AXTextMarkerToAXPosition(
+    id text_marker);
+
+// Returns the AXRange representing the given AXTextMarkerRange.
+AX_EXPORT AXPlatformNodeDelegate::AXRange AXTextMarkerRangeToAXRange(
+    id marker_range);
+
+}  // namespace ui
+
+#endif  // UI_ACCESSIBILITY_PLATFORM_AX_UTILS_MAC_H_
diff --git a/ui/accessibility/platform/ax_utils_mac.mm b/ui/accessibility/platform/ax_utils_mac.mm
new file mode 100644
index 0000000..e063918c
--- /dev/null
+++ b/ui/accessibility/platform/ax_utils_mac.mm
@@ -0,0 +1,94 @@
+// 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 "ui/accessibility/platform/ax_utils_mac.h"
+
+#include "base/mac/scoped_cftyperef.h"
+#include "ui/accessibility/ax_range.h"
+
+// The following are private accessibility APIs required for cursor navigation
+// and text selection. VoiceOver started relying on them in Mac OS X 10.11.
+// They are public as of the 12.0 SDK.
+#if !defined(MAC_OS_VERSION_12_0) || \
+    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_12_0
+using AXTextMarkerRangeRef = CFTypeRef;
+using AXTextMarkerRef = CFTypeRef;
+extern "C" {
+CFTypeID AXTextMarkerGetTypeID();
+AXTextMarkerRef AXTextMarkerCreate(CFAllocatorRef,
+                                   const UInt8* bytes,
+                                   CFIndex length);
+size_t AXTextMarkerGetLength(AXTextMarkerRef);
+const UInt8* AXTextMarkerGetBytePtr(AXTextMarkerRef);
+
+CFTypeID AXTextMarkerRangeGetTypeID();
+AXTextMarkerRef AXTextMarkerRangeCopyStartMarker(AXTextMarkerRangeRef);
+AXTextMarkerRef AXTextMarkerRangeCopyEndMarker(AXTextMarkerRangeRef);
+}  // extern "C"
+#endif
+
+namespace ui {
+
+bool IsAXTextMarker(id object) {
+  if (object == nil)
+    return false;
+
+  AXTextMarkerRef cf_text_marker = static_cast<AXTextMarkerRef>(object);
+  DCHECK(cf_text_marker);
+  return CFGetTypeID(cf_text_marker) == AXTextMarkerGetTypeID();
+}
+
+bool IsAXTextMarkerRange(id object) {
+  if (object == nil)
+    return false;
+
+  AXTextMarkerRangeRef cf_marker_range =
+      static_cast<AXTextMarkerRangeRef>(object);
+  DCHECK(cf_marker_range);
+  return CFGetTypeID(cf_marker_range) == AXTextMarkerRangeGetTypeID();
+}
+
+AXPlatformNodeDelegate::AXPosition AXTextMarkerToAXPosition(id text_marker) {
+  if (!IsAXTextMarker(text_marker))
+    return AXNodePosition::CreateNullPosition();
+
+  AXTextMarkerRef cf_text_marker = static_cast<AXTextMarkerRef>(text_marker);
+  if (AXTextMarkerGetLength(cf_text_marker) !=
+      sizeof(AXPlatformNodeDelegate::SerializedPosition))
+    return AXNodePosition::CreateNullPosition();
+
+  const UInt8* source_buffer = AXTextMarkerGetBytePtr(cf_text_marker);
+  if (!source_buffer)
+    return AXNodePosition::CreateNullPosition();
+
+  return AXNodePosition::Unserialize(
+      *reinterpret_cast<const AXPlatformNodeDelegate::SerializedPosition*>(
+          source_buffer));
+}
+
+AXPlatformNodeDelegate::AXRange AXTextMarkerRangeToAXRange(
+    id text_marker_range) {
+  if (!IsAXTextMarkerRange(text_marker_range)) {
+    return AXPlatformNodeDelegate::AXRange();
+  }
+
+  AXTextMarkerRangeRef cf_marker_range =
+      static_cast<AXTextMarkerRangeRef>(text_marker_range);
+
+  base::ScopedCFTypeRef<AXTextMarkerRef> start_marker(
+      AXTextMarkerRangeCopyStartMarker(cf_marker_range));
+  base::ScopedCFTypeRef<AXTextMarkerRef> end_marker(
+      AXTextMarkerRangeCopyEndMarker(cf_marker_range));
+  if (!start_marker.get() || !end_marker.get())
+    return AXPlatformNodeDelegate::AXRange();
+
+  // |AXPlatformNodeDelegate::AXRange| takes ownership of its anchor and focus.
+  AXPlatformNodeDelegate::AXPosition anchor =
+      AXTextMarkerToAXPosition(static_cast<id>(start_marker.get()));
+  AXPlatformNodeDelegate::AXPosition focus =
+      AXTextMarkerToAXPosition(static_cast<id>(end_marker.get()));
+  return AXPlatformNodeDelegate::AXRange(std::move(anchor), std::move(focus));
+}
+
+}  // namespace ui
diff --git a/ui/android/java/res/values-night/colors.xml b/ui/android/java/res/values-night/colors.xml
index 8139f79..40c73952 100644
--- a/ui/android/java/res/values-night/colors.xml
+++ b/ui/android/java/res/values-night/colors.xml
@@ -102,4 +102,5 @@
     <color name="menu_chip_background_color_disabled">@color/menu_chip_background_color_disabled_light</color>
     <color name="iph_highlight_blue">@color/iph_highlight_blue_light</color>
     <color name="rating_star_yellow">@color/rating_star_yellow_light</color>
+    <color name="price_drop_annotation_bg_color">@color/google_green_800</color>
 </resources>
diff --git a/ui/android/java/res/values/color_palette.xml b/ui/android/java/res/values/color_palette.xml
index cbab914..b1093a60 100644
--- a/ui/android/java/res/values/color_palette.xml
+++ b/ui/android/java/res/values/color_palette.xml
@@ -69,8 +69,10 @@
     <color name="baseline_secondary_300_with_neutral_100_alpha_8">#53A7E1</color>
     <color name="baseline_secondary_300_with_neutral_100_alpha_24">#448FC4</color>
     <color name="baseline_secondary_900">#001D35</color>
+    <color name="baseline_tertiary_50">#E7F8ED</color>
     <color name="baseline_tertiary_200">#6DD58C</color>
     <color name="baseline_tertiary_600">#146C2E</color>
+    <color name="baseline_tertiary_800">#0A3818</color>
 
     <!-- Remapped to 2021 color palette. Do not add new references to these. -->
     <color name="modern_white">@color/baseline_neutral_0</color>
@@ -85,8 +87,11 @@
     <color name="modern_blue_600_alpha_65" tools:ignore="UnusedResources">@color/baseline_primary_600_alpha_65</color>
     <color name="modern_blue_700">@color/baseline_primary_700</color>
     <color name="modern_blue_800" tools:ignore="UnusedResources">@color/baseline_primary_800</color>
+
+    <color name="google_green_50">@color/baseline_tertiary_50</color>
     <color name="google_green_300">@color/baseline_tertiary_200</color>
     <color name="google_green_600">@color/baseline_tertiary_600</color>
+    <color name="google_green_800">@color/baseline_tertiary_800</color>
     <color name="modern_grey_50" tools:ignore="UnusedResources">@color/baseline_neutral_50</color>
     <color name="modern_grey_100">@color/baseline_neutral_100</color>
     <color name="modern_grey_100_alpha_38">@color/baseline_neutral_100_alpha_38</color>
diff --git a/ui/android/java/res/values/semantic_colors_adaptive.xml b/ui/android/java/res/values/semantic_colors_adaptive.xml
index 49d3d59..32abfcb 100644
--- a/ui/android/java/res/values/semantic_colors_adaptive.xml
+++ b/ui/android/java/res/values/semantic_colors_adaptive.xml
@@ -134,4 +134,5 @@
     <color name="menu_chip_background_color_disabled">@color/menu_chip_background_color_disabled_dark</color>
     <color name="iph_highlight_blue">@color/iph_highlight_blue_dark</color>
     <color name="rating_star_yellow">@color/rating_star_yellow_dark</color>
+    <color name="price_drop_annotation_bg_color">@color/google_green_50</color>
 </resources>
diff --git a/ui/base/ime/ash/ime_input_context_handler_interface.h b/ui/base/ime/ash/ime_input_context_handler_interface.h
index 6fd023f..7e69ef7 100644
--- a/ui/base/ime/ash/ime_input_context_handler_interface.h
+++ b/ui/base/ime/ash/ime_input_context_handler_interface.h
@@ -87,6 +87,8 @@
   // Returns true if there is any composition text.
   virtual bool HasCompositionText() = 0;
 
+  virtual std::u16string GetCompositionText() = 0;
+
   // Returns the ukm::SourceId that identifies the currently focused client.
   virtual ukm::SourceId GetClientSourceForMetrics() = 0;
 };
diff --git a/ui/base/ime/ash/input_method_ash.cc b/ui/base/ime/ash/input_method_ash.cc
index 3cf9e53..45169d665 100644
--- a/ui/base/ime/ash/input_method_ash.cc
+++ b/ui/base/ime/ash/input_method_ash.cc
@@ -962,6 +962,20 @@
   return client && client->HasCompositionText();
 }
 
+std::u16string InputMethodAsh::GetCompositionText() {
+  TextInputClient* client = GetTextInputClient();
+  if (!client) {
+    return u"";
+  }
+
+  gfx::Range composition_range;
+  client->GetCompositionTextRange(&composition_range);
+  std::u16string composition_text;
+  client->GetTextFromRange(composition_range, &composition_text);
+
+  return composition_text;
+}
+
 ukm::SourceId InputMethodAsh::GetClientSourceForMetrics() {
   TextInputClient* client = GetTextInputClient();
   return client ? client->GetClientSourceForMetrics() : ukm::kInvalidSourceId;
diff --git a/ui/base/ime/ash/input_method_ash.h b/ui/base/ime/ash/input_method_ash.h
index fad27b3..ed32781 100644
--- a/ui/base/ime/ash/input_method_ash.h
+++ b/ui/base/ime/ash/input_method_ash.h
@@ -83,6 +83,7 @@
   InputMethod* GetInputMethod() override;
   void ConfirmCompositionText(bool reset_engine, bool keep_selection) override;
   bool HasCompositionText() override;
+  std::u16string GetCompositionText() override;
   ukm::SourceId GetClientSourceForMetrics() override;
 
  protected:
diff --git a/ui/base/ime/ash/mock_ime_input_context_handler.cc b/ui/base/ime/ash/mock_ime_input_context_handler.cc
index 7f2e37f5..d7581a9 100644
--- a/ui/base/ime/ash/mock_ime_input_context_handler.cc
+++ b/ui/base/ime/ash/mock_ime_input_context_handler.cc
@@ -156,6 +156,10 @@
   return !last_update_composition_arg_.composition_text.text.empty();
 }
 
+std::u16string MockIMEInputContextHandler::GetCompositionText() {
+  return last_update_composition_arg_.composition_text.text;
+}
+
 ukm::SourceId MockIMEInputContextHandler::GetClientSourceForMetrics() {
   return ukm::kInvalidSourceId;
 }
diff --git a/ui/base/ime/ash/mock_ime_input_context_handler.h b/ui/base/ime/ash/mock_ime_input_context_handler.h
index 27364fe..45c3b4a 100644
--- a/ui/base/ime/ash/mock_ime_input_context_handler.h
+++ b/ui/base/ime/ash/mock_ime_input_context_handler.h
@@ -66,6 +66,7 @@
   InputMethod* GetInputMethod() override;
   void ConfirmCompositionText(bool reset_engine, bool keep_selection) override;
   bool HasCompositionText() override;
+  std::u16string GetCompositionText() override;
   ukm::SourceId GetClientSourceForMetrics() override;
 
   std::vector<GrammarFragment> get_grammar_fragments() const {
diff --git a/ui/ozone/platform/wayland/host/shell_surface_wrapper.h b/ui/ozone/platform/wayland/host/shell_surface_wrapper.h
index 3d7f961..1985f43 100644
--- a/ui/ozone/platform/wayland/host/shell_surface_wrapper.h
+++ b/ui/ozone/platform/wayland/host/shell_surface_wrapper.h
@@ -18,7 +18,10 @@
  public:
   virtual ~ShellSurfaceWrapper() {}
 
-  // Initializes the ShellSurface.
+  // Initializes the ShellSurface. The implementation should not commit surface
+  // state changes and defer that to the window that owns the surface, such that
+  // the window properties are set before the initial commit
+  // (https://crbug.com/1268308).
   virtual bool Initialize() = 0;
 
   // Sends acknowledge configure event back to wayland.
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
index 9c64a7d9..eb1d9b6 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -80,6 +80,7 @@
   // NonClientView::GetWindowMask, since |non_client_view| is not created yet
   // during the call to WaylandWindow::Initialize().
   UpdateWindowMask();
+  root_surface()->Commit(true);
   return true;
 }
 
diff --git a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc
index 4d7cb5e..4c19758f 100644
--- a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc
+++ b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc
@@ -110,8 +110,6 @@
 
   InitializeXdgDecoration();
 
-  wayland_window_->root_surface()->Commit();
-  connection_->ScheduleFlush();
   return true;
 }
 
diff --git a/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.cc b/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.cc
index 1815f2a..7b32383 100644
--- a/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.cc
+++ b/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.cc
@@ -65,8 +65,6 @@
   zxdg_toplevel_v6_add_listener(zxdg_toplevel_v6_.get(),
                                 &zxdg_toplevel_v6_listener, this);
 
-  wayland_window_->root_surface()->Commit();
-  connection_->ScheduleFlush();
   return true;
 }
 
diff --git a/ui/strings/app_locale_settings.grd b/ui/strings/app_locale_settings.grd
index 4918fda4..935849f 100644
--- a/ui/strings/app_locale_settings.grd
+++ b/ui/strings/app_locale_settings.grd
@@ -34,7 +34,7 @@
       <output filename="app_locale_settings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="app_locale_settings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="app_locale_settings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="app_locale_settings_am.pak" type="data_package" lang="am" />
@@ -223,7 +223,7 @@
           75%
         </message>
       </if>
-      <if expr="(is_linux or is_android or is_bsd or is_fuchsia) and not (chromeos or lacros)">
+      <if expr="(is_linux or is_android or is_bsd or is_fuchsia) and not (chromeos_ash or chromeos_lacros)">
         <!-- The font used in Web UI (e.g. History). Note that these are only
              backups. We try to use the system font if possible. -->
         <message name="IDS_WEB_FONT_FAMILY" use_name_for_id="true">
@@ -236,7 +236,7 @@
         </message>
       </if>
       <!-- For Chrome OS -->
-      <if expr="chromeos or lacros">
+      <if expr="chromeos_ash or chromeos_lacros">
         <!-- The font name like: 'Font Name, 12px' -->
         <message name="IDS_UI_FONT_FAMILY_CROS" use_name_for_id="true">
           Roboto, 12px
diff --git a/ui/strings/ax_strings.grd b/ui/strings/ax_strings.grd
index 7d7902f..6f5cdb96 100644
--- a/ui/strings/ax_strings.grd
+++ b/ui/strings/ax_strings.grd
@@ -40,7 +40,7 @@
       <output filename="ax_strings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="ax_strings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="ax_strings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="ax_strings_am.pak" type="data_package" lang="am" />
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 3525976..dba7a6f 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -46,7 +46,7 @@
       <output filename="ui_strings_zh-HK.pak" type="data_package" lang="zh-HK" />
       <output filename="ui_strings_zu.pak" type="data_package" lang="zu" />
     </if>
-    <if expr="chromeos or lacros">
+    <if expr="chromeos_ash or chromeos_lacros">
       <output filename="ui_strings_is.pak" type="data_package" lang="is" />
     </if>
     <output filename="ui_strings_am.pak" type="data_package" lang="am" />
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_list.html b/ui/webui/resources/cr_components/certificate_manager/certificate_list.html
index 4cc7888..fd6a180 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_list.html
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_list.html
@@ -18,7 +18,7 @@
       <cr-button id="import" on-click="onImportTap_"
           hidden="[[!canImport_(certificateType, importAllowed, isKiosk_)]]">
         [[i18n('certificateManagerImport')]]</cr-button>
-<if expr="chromeos or lacros">
+<if expr="chromeos_ash or chromeos_lacros">
       <cr-button id="importAndBind" on-click="onImportAndBindTap_"
           hidden="[[!canImportAndBind_(certificateType, importAllowed,
                  isGuest_)]]">
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_list.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_list.ts
index 297da25..e4da8ce 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_list.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_list.ts
@@ -43,7 +43,7 @@
       certificateType: String,
       importAllowed: Boolean,
 
-      // <if expr="chromeos or lacros">
+      // <if expr="chromeos_ash or chromeos_lacros">
       isGuest_: {
         type: Boolean,
         value() {
@@ -66,7 +66,7 @@
   certificates: Array<CertificatesOrgGroup>;
   certificateType: CertificateType;
   importAllowed: boolean;
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private isGuest_: boolean;
   // </if>
   private isKiosk_: boolean;
@@ -95,7 +95,7 @@
         this.importAllowed;
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private canImportAndBind_(): boolean {
     return !this.isGuest_ &&
         this.certificateType === CertificateType.PERSONAL && this.importAllowed;
@@ -141,7 +141,7 @@
     this.handleImport_(false, e.target as HTMLElement);
   }
 
-  // <if expr="chromeos or lacros">
+  // <if expr="chromeos_ash or chromeos_lacros">
   private onImportAndBindTap_(e: Event) {
     this.handleImport_(true, e.target as HTMLElement);
   }
diff --git a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_item.html b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_item.html
index 3269312..eebb09c 100644
--- a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_item.html
+++ b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_item.html
@@ -30,15 +30,30 @@
   }
 
 </style>
-<div id="container" class="cr-row continuation"
-    on-click="onSelected_"
-    on-keydown="onKeydown_">
-  <bluetooth-icon device="[[device]]"></bluetooth-icon>
-  <div id="deviceName" class="text-row">
-    [[getDeviceName_(device.*)]]
-     <!-- TODO(crbug.com/1010321): Add A11Y. -->
-    <div id="secondaryLabel" class="secondary">
-      [[secondaryLabel_]]
+<div id="wrapper" focus-row-container>
+  <div id="container"
+      class="cr-row continuation"
+      actionable
+      focus-row-control
+      selectable
+      aria-label$="[[getAriaLabel_(device.*, itemIndex, listSize)]]"
+      role="button"
+      focus-type="rowWrapper"
+      on-keydown="onKeydown_"
+      on-click="onSelected_">
+    <bluetooth-icon device="[[device]]"></bluetooth-icon>
+    <div aria-live="polite"
+        aria-label="[[getSecondaryAriaLabel_(
+          secondaryLabel_, pairingFailed_, device.*)]]"
+        class="text-row">
+      <div id="deviceName" aria-hidden="true">
+        [[getDeviceName_(device.*)]]
+      </div>
+      <div id="secondaryLabel"
+          aria-hidden="true"
+          class="secondary">
+        [[secondaryLabel_]]
+      </div>
     </div>
   </div>
 </div>
diff --git a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_item.js b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_item.js
index 21849587..b5e6130a0 100644
--- a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_item.js
+++ b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_item.js
@@ -12,9 +12,8 @@
 
 import {I18nBehavior, I18nBehaviorInterface} from '//resources/js/i18n_behavior.m.js';
 import {html, mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
+import {FocusRowBehavior} from 'chrome://resources/js/cr/ui/focus_row_behavior.m.js';
 import {assertNotReached} from '../../../js/assert.m.js';
-
 import {DeviceItemState} from './bluetooth_types.js';
 import {mojoString16ToString} from './bluetooth_utils.js';
 
@@ -24,7 +23,7 @@
  * @extends {PolymerElement}
  */
 const SettingsBluetoothPairingDeviceItemElementBase =
-    mixinBehaviors([I18nBehavior], PolymerElement);
+    mixinBehaviors([I18nBehavior, FocusRowBehavior], PolymerElement);
 
 /** @polymer */
 export class SettingsBluetoothPairingDeviceItemElement extends
@@ -40,7 +39,7 @@
   static get properties() {
     return {
       /**
-       * @type {!chromeos.bluetoothConfig.mojom.BluetoothDeviceProperties}
+       * @type {?chromeos.bluetoothConfig.mojom.BluetoothDeviceProperties}
        */
       device: Object,
 
@@ -50,6 +49,15 @@
         value: DeviceItemState.DEFAULT,
       },
 
+      /** The index of this item in its parent list, used for its a11y label. */
+      itemIndex: Number,
+
+      /**
+       * The total number of elements in this item's parent list, used for its
+       * a11y label.
+       */
+      listSize: Number,
+
       /** @private {string} */
       secondaryLabel_: {
         type: String,
@@ -131,6 +139,70 @@
       detail: {device: this.device},
     }));
   }
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getAriaLabel_() {
+    if (!this.device) {
+      return '';
+    }
+
+    return this.i18n(
+        this.getA11yLabelMessageId_(), this.itemIndex + 1, this.listSize,
+        this.getDeviceName_());
+  }
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getA11yLabelMessageId_() {
+    const deviceType = chromeos.bluetoothConfig.mojom.DeviceType;
+    switch (this.device.deviceType) {
+      case deviceType.kUnknown:
+        return 'bluetoothPairingDeviceItemA11YLabelUnknown';
+      case deviceType.kComputer:
+        return 'bluetoothPairingDeviceItemA11YLabelComputer';
+      case deviceType.kPhone:
+        return 'bluetoothPairingDeviceItemA11YLabelPhone';
+      case deviceType.kHeadset:
+        return 'bluetoothPairingDeviceItemA11YLabelHeadset';
+      case deviceType.kVideoCamera:
+        return 'bluetoothPairingDeviceItemA11YLabelVideoCamera';
+      case deviceType.kGameController:
+        return 'bluetoothPairingDeviceItemA11YLabelGameContoller';
+      case deviceType.kKeyboard:
+        return 'bluetoothPairingDeviceItemA11YLabelKeyboard';
+      case deviceType.kMouse:
+        return 'bluetoothPairingDeviceItemA11YLabelMouse';
+      case deviceType.kTablet:
+        return 'bluetoothPairingDeviceItemA11YLabelTablet';
+      default:
+        assertNotReached();
+    }
+  }
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getSecondaryAriaLabel_() {
+    const deviceName = this.getDeviceName_();
+    switch (this.deviceItemState) {
+      case DeviceItemState.FAILED:
+        return this.i18n(
+            'bluetoothPairingDeviceItemSecondaryErrorA11YLabel', deviceName);
+      case DeviceItemState.PAIRING:
+        return this.i18n(
+            'bluetoothPairingDeviceItemSecondaryPairingA11YLabel', deviceName);
+      case DeviceItemState.DEFAULT:
+        return '';
+      default:
+        assertNotReached();
+    }
+  }
 }
 
 customElements.define(
diff --git a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_selection_page.html b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_selection_page.html
index a2b53ce4..36cf56d 100644
--- a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_selection_page.html
+++ b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_selection_page.html
@@ -24,20 +24,29 @@
     <div id="deviceListTitle" tabindex="0">
       [[getDeviceListTitle_(devices.*, isBluetoothEnabled)]]
     </div>
-    <!-- TODO(crbug.com/1010321): Fix item focus. -->
     <template  is="dom-if" if="[[shouldShowDeviceList_(devices.*,
         isBluetoothEnabled)]]" restamp>
       <div id="container" class="layout vertical flex" scrollable
           no-bottom-scroll-border>
-        <iron-list items="[[devices]]" preserve-focus>
-          <template>
-            <bluetooth-pairing-device-item
-                device="[[item]]"
-                device-item-state="[[getDeviceItemState_(
-                    item, devicePendingPairing.*, failedPairingDeviceId)]]" >
-            </bluetooth-pairing-device-item>
-          </template>
-        </iron-list>
+          <iron-list items="[[devices]]"
+              scroll-target="container"
+              preserve-focus>
+            <template>
+              <bluetooth-pairing-device-item
+                  item="[[item]]"
+                  device="[[item]]"
+                  device-item-state="[[getDeviceItemState_(
+                      item, devicePendingPairing.*, failedPairingDeviceId)]]"
+                  tabindex$="[[tabIndex]]"
+                  focus-row-index="[[index]]"
+                  iron-list-tab-index="[[tabIndex]]"
+                  last-focused="{{lastFocused_}}"
+                  list-blurred="{{listBlurred_}}"
+                  item-index="[[index]]"
+                  list-size="[[devices.length]]" >
+              </bluetooth-pairing-device-item>
+            </template>
+          </iron-list>
       </div>
     </template>
   </div>
diff --git a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_selection_page.js b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_selection_page.js
index 002e411..03e0a272 100644
--- a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_selection_page.js
+++ b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_device_selection_page.js
@@ -78,7 +78,19 @@
           cancel: ButtonState.ENABLED,
           pair: ButtonState.HIDDEN,
         },
-      }
+      },
+
+      /**
+       * Used by FocusRowBehavior to track the last focused element on a row.
+       * @private
+       */
+      lastFocused_: Object,
+
+      /**
+       * Used by FocusRowBehavior to track if the list has been blurred.
+       * @private
+       */
+      listBlurred_: Boolean,
     };
   }
 
@@ -107,11 +119,15 @@
   }
 
   /**
-   * @param {!chromeos.bluetoothConfig.mojom.BluetoothDeviceProperties} device
+   * @param {?chromeos.bluetoothConfig.mojom.BluetoothDeviceProperties} device
    * @return {!DeviceItemState}
    * @private
    */
   getDeviceItemState_(device) {
+    if (!device) {
+      return DeviceItemState.DEFAULT;
+    }
+
     if (device.id === this.failedPairingDeviceId) {
       return DeviceItemState.FAILED;
     }
diff --git a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.d.ts b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.d.ts
index 3df43de..21fc89e 100644
--- a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.d.ts
+++ b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.d.ts
@@ -5,6 +5,10 @@
 import {CrSearchFieldBehavior} from './cr_search_field_behavior.js';
 
 interface CrSearchFieldElement extends CrSearchFieldBehavior, HTMLElement {
+  $: {
+    clearSearch: HTMLElement,
+    searchInput: HTMLElement,
+  };
   autofocus: boolean;
 }