diff --git a/.gn b/.gn
index 89f22ef..4aa9891 100644
--- a/.gn
+++ b/.gn
@@ -128,9 +128,7 @@
   "//extensions/browser/api/test:*",  # 1 error
   "//extensions/browser/api/usb:*",  # 12 errors
   "//extensions/browser/api/virtual_keyboard_private:*",  # 2 errors
-  "//extensions/browser/api/vpn_provider:*",  # 13 errors
   "//extensions/browser/api/web_request:*",  # 37 errors
-  "//extensions/browser/api/webcam_private:*",  # 8 errors
   "//extensions/browser/api:*",  # 7 errors
   "//extensions/browser/updater:*",  # 31 errors
   "//extensions/browser:*",  # 20 errors
diff --git a/BUILD.gn b/BUILD.gn
index d224910..46462a3 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -81,6 +81,8 @@
     "//services/service_manager/public/cpp",
     "//skia:skia_unittests",
     "//sql:sql_unittests",
+    "//third_party/angle/src/tests:angle_end2end_tests",
+    "//third_party/angle/src/tests:angle_white_box_tests",
     "//third_party/flatbuffers:flatbuffers_unittests",
     "//third_party/liburlpattern:liburlpattern_unittests",
     "//tools/binary_size:binary_size_trybot_py",
@@ -190,7 +192,6 @@
       "//mojo:mojo_unittests",
       "//net:net_perftests",
       "//storage:storage_unittests",
-      "//third_party/angle/src/tests:angle_end2end_tests",
       "//third_party/angle/src/tests:angle_unittests",
       "//third_party/blink/common:blink_common_unittests",
       "//third_party/blink/renderer/controller:blink_unittests",
@@ -241,7 +242,6 @@
       "//google_apis/gcm:mcs_probe",
       "//media/capture:capture_unittests",
       "//media/cast:cast_unittests",
-      "//third_party/angle/src/tests:angle_white_box_tests",
       "//third_party/catapult/telemetry:bitmaptools($host_toolchain)",
     ]
   } else if (is_ios) {
@@ -482,7 +482,7 @@
     }
   }
 
-  if (is_ios || is_win || (is_linux || is_chromeos_lacros)) {
+  if (is_ios || is_win || (is_linux || is_chromeos_lacros) || is_fuchsia) {
     deps += [
       "//base:base_i18n_perftests",
       "//google_apis:google_apis_unittests",
diff --git a/DEPS b/DEPS
index 6cadb8b..2ede054 100644
--- a/DEPS
+++ b/DEPS
@@ -203,7 +203,7 @@
   # 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': '2779617a24693e22ad550b0b15467568c5a4a1ee',
+  'v8_revision': '43ed322b6a782285f8fcfbaadd3926c371c3a60b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -211,11 +211,11 @@
   # 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': 'c600e47812c88d45087582122dbb004851ae966b',
+  'angle_revision': 'de32c3d2de006a2c8d2acfa829634fcf2d1564ab',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '009667d5f97c46652d9a804b3579e28fd692a43e',
+  'swiftshader_revision': '3549479dc4ccd92ef3b9e179080dcec1a430b01d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -266,7 +266,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': '43761d2e076c2c0d8e14a136c830c899b9fa17f4',
+  'catapult_revision': 'd1a3011cd91205aa96b74b5dfc227d391e88108d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -274,7 +274,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': '6f4e54f003dd3094626dd026fbb4c4fcbb7b52d6',
+  'devtools_frontend_revision': 'd08c5531762807570b5a929d3496eae8632bf2b5',
   # 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.
@@ -314,11 +314,11 @@
   # 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': '99c3a691c23dbd75075eb852d2f6a1c569148826',
+  'dawn_revision': 'ea26a8ce553fe2efa94830f3d7326072487bde3f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '425c25a8ad2d190f61531fd46e0a65050cc1b98a',
+  'quiche_revision': '002828690efcb43cecc5972d695d49b0953d2ad9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -364,7 +364,7 @@
   # revisions.
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:0d67e272bdb8145f87d238bc0b2cb8bf80ccec90',
+  'gn_version': 'git_revision:595e3be7c8381d4eeefce62a63ec12bae9ce5140',
 
   # Also, if you change these, update buildtools/DEPS too. Also update the
   # libc++ svn_revision in //buildtools/deps_revisions.gni.
@@ -893,7 +893,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c1aa4ecfcc8cbbbcf21e1b77125aa602339846ec',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '82b992a1656d7d1cd0ee3cbea8ff609ffdfed380',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1249,7 +1249,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '3dd5b80bc4f172dd82925bb259cb7c82348409c5',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + 'f9e9052e7e6f1f75212737926b267a0e41621892',
+    Var('chromium_git') + '/openscreen' + '@' + '6051838253185d8478f5fa4d70c96ef6c0241f94',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '97cfe495bb7a3853266b646d1c79e169387f9c7a',
@@ -1266,7 +1266,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'bdc7f626dbce0778814d8987aef3402113ca6241',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '53a231c0ae868366179bd560b68a2f5f4b166541',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1518,7 +1518,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '3c2fe3888658d82b47ca831d59a2e07579619c2d',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'ad0be281a41cc402c247c23206eba81be73ab401',
+    Var('webrtc_git') + '/src.git' + '@' + '1c5e63e5451374783aaf0259f5c23d3688a5b2ff',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1590,7 +1590,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d0ccd38b2dfb8aa50214dd87000a8c30cb37258f',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6f7379c80b03c2f1aac2182807e684f7694accbd',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index a47f5c55..283209c 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -118,6 +118,7 @@
       'filepath': 'chrome/browser/apps/app_service/'\
                   '|chrome/browser/ui/app_list/app_service/'\
                   '|chrome/browser/ui/ash/launcher/app_service/'\
+                  '|chrome/browser/ui/views/apps/app_dialog/' \
                   '|components/services/app_service/',
     },
     'app_shortcuts': {
@@ -1038,6 +1039,10 @@
     'fuchsia': {
       'filepath': 'fuchsia',
     },
+    'full_restore': {
+      'filepath': 'chrome/browser/chromeos/full_restore/|' \
+                  'components/full_restore/'
+    },
     'fuzzing': {
       'filepath': 'fuzz|Fuzz',
     },
@@ -2517,6 +2522,7 @@
                         'thestig@chromium.org'],
     'fsp': ['mtomasz+watch@chromium.org'],
     'fuchsia': ['fuchsia-reviews@chromium.org'],
+    'full_restore': ['nancylingwang@chromium.org'],
     'fuzzing': ['fuzzing@chromium.org'],
     'gamepad': ['mattreynolds+watch@chromium.org'],
     'gcm': ['peter@chromium.org'],
diff --git a/android_webview/expectations/system_webview_bundle.AndroidManifest.expected b/android_webview/expectations/system_webview_bundle.AndroidManifest.expected
index 73dcc3b..3e78591 100644
--- a/android_webview/expectations/system_webview_bundle.AndroidManifest.expected
+++ b/android_webview/expectations/system_webview_bundle.AndroidManifest.expected
@@ -1,5 +1,6 @@
 <?xml version="1.0" ?>
 <manifest
+    android:isolatedSplits="true"
     package="com.android.webview"
     platformBuildVersionCode="30"
     platformBuildVersionName="11"
diff --git a/android_webview/expectations/trichrome_webview_bundle.AndroidManifest.expected b/android_webview/expectations/trichrome_webview_bundle.AndroidManifest.expected
index 2861c0cc..a867c69 100644
--- a/android_webview/expectations/trichrome_webview_bundle.AndroidManifest.expected
+++ b/android_webview/expectations/trichrome_webview_bundle.AndroidManifest.expected
@@ -1,5 +1,6 @@
 <?xml version="1.0" ?>
 <manifest
+    android:isolatedSplits="true"
     package="com.android.webview"
     platformBuildVersionCode="30"
     platformBuildVersionName="11"
diff --git a/android_webview/system_webview_apk_tmpl.gni b/android_webview/system_webview_apk_tmpl.gni
index fa280ae..58304b8 100644
--- a/android_webview/system_webview_apk_tmpl.gni
+++ b/android_webview/system_webview_apk_tmpl.gni
@@ -59,6 +59,13 @@
           "//android_webview:android_webview_no_weblayer_java",
           "//weblayer/browser/java:base_module_java",
         ]
+        if (defined(invoker.expected_android_manifest)) {
+          _bundle_target_gen_dir =
+              get_label_info(invoker.bundle_target, "target_gen_dir")
+          _bundle_name = get_label_info(invoker.bundle_target, "name")
+          extra_verification_manifest = "${_bundle_target_gen_dir}/${_bundle_name}__weblayer_bundle_module_manifest/AndroidManifest.xml"
+          extra_verification_manifest_dep = "${invoker.bundle_target}__weblayer_bundle_module__merge_manifests"
+        }
       } else {
         deps += [ "//android_webview:android_webview_java" ]
       }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index b6b069c..869928b 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1308,8 +1308,6 @@
     "system/status_area_widget_delegate.h",
     "system/supervised/supervised_icon_string.cc",
     "system/supervised/supervised_icon_string.h",
-    "system/supervised/supervised_notification_controller.cc",
-    "system/supervised/supervised_notification_controller.h",
     "system/system_notification_controller.cc",
     "system/system_notification_controller.h",
     "system/time/time_tray_item_view.cc",
@@ -2251,7 +2249,6 @@
     "system/session/logout_confirmation_controller_unittest.cc",
     "system/session/session_limit_notification_controller_unittest.cc",
     "system/status_area_widget_unittest.cc",
-    "system/supervised/supervised_notification_controller_unittest.cc",
     "system/time/time_view_unittest.cc",
     "system/toast/toast_manager_unittest.cc",
     "system/tracing_notification_controller_unittest.cc",
diff --git a/ash/ambient/ambient_controller.cc b/ash/ambient/ambient_controller.cc
index a2bd92c..81913f6 100644
--- a/ash/ambient/ambient_controller.cc
+++ b/ash/ambient/ambient_controller.cc
@@ -253,7 +253,6 @@
         }
       } else {
         DCHECK(visibility == AmbientUiVisibility::kClosed);
-        GetAmbientBackendModel()->ResetImageFailures();
         inactivity_timer_.Stop();
         user_activity_observer_.Reset();
         power_status_observer_.Reset();
@@ -314,6 +313,11 @@
   }
 }
 
+void AmbientController::OnFirstSessionStarted() {
+  if (IsAmbientModeEnabled())
+    ambient_photo_controller_.ScheduleFetchBackupImages();
+}
+
 void AmbientController::OnActiveUserPrefServiceChanged(
     PrefService* pref_service) {
   if (!AmbientClient::Get()->IsAmbientModeAllowed() ||
@@ -445,6 +449,7 @@
   DVLOG(1) << __func__;
 
   ambient_ui_model_.SetUiVisibility(AmbientUiVisibility::kClosed);
+  GetAmbientBackendModel()->ResetImageFailures();
 }
 
 void AmbientController::ToggleInSessionUi() {
@@ -504,6 +509,10 @@
 }
 
 void AmbientController::OnEnabledPrefChanged() {
+  // TODO(b/176094707) conditionally create/destroy photo_controller and cache
+  // if Ambient is enabled
+  ambient_photo_controller_.InitCache();
+
   if (IsAmbientModeEnabled()) {
     DVLOG(1) << "Ambient mode enabled";
 
@@ -530,11 +539,10 @@
     OnLockScreenBackgroundTimeoutPrefChanged();
     OnPhotoRefreshIntervalPrefChanged();
 
-    ambient_photo_controller_ = std::make_unique<AmbientPhotoController>();
-
     ambient_ui_model_observer_.Observe(&ambient_ui_model_);
 
-    ambient_backend_model_observer_.Observe(GetAmbientBackendModel());
+    ambient_backend_model_observer_.Observe(
+        ambient_photo_controller_.ambient_backend_model());
 
     auto* power_manager_client = chromeos::PowerManagerClient::Get();
     DCHECK(power_manager_client);
@@ -556,8 +564,6 @@
         pref_change_registrar_->Remove(pref_name);
     }
 
-    ambient_photo_controller_.reset();
-
     ambient_ui_model_observer_.Reset();
     ambient_backend_model_observer_.Reset();
     power_manager_client_observer_.Reset();
@@ -624,8 +630,7 @@
 }
 
 AmbientBackendModel* AmbientController::GetAmbientBackendModel() {
-  DCHECK(ambient_photo_controller_);
-  return ambient_photo_controller_->ambient_backend_model();
+  return ambient_photo_controller_.ambient_backend_model();
 }
 
 void AmbientController::OnImagesReady() {
@@ -680,13 +685,11 @@
 }
 
 void AmbientController::StartRefreshingImages() {
-  DCHECK(ambient_photo_controller_);
-  ambient_photo_controller_->StartScreenUpdate();
+  ambient_photo_controller_.StartScreenUpdate();
 }
 
 void AmbientController::StopRefreshingImages() {
-  DCHECK(ambient_photo_controller_);
-  ambient_photo_controller_->StopScreenUpdate();
+  ambient_photo_controller_.StopScreenUpdate();
 }
 
 void AmbientController::set_backend_controller_for_testing(
diff --git a/ash/ambient/ambient_controller.h b/ash/ambient/ambient_controller.h
index c2cb6afea..c756e4c2 100644
--- a/ash/ambient/ambient_controller.h
+++ b/ash/ambient/ambient_controller.h
@@ -65,6 +65,7 @@
 
   // SessionObserver:
   void OnLockStateChanged(bool locked) override;
+  void OnFirstSessionStarted() override;
   void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
 
   // PowerStatus::Observer:
@@ -118,7 +119,7 @@
   }
 
   AmbientPhotoController* ambient_photo_controller() {
-    return ambient_photo_controller_.get();
+    return &ambient_photo_controller_;
   }
 
   AmbientUiModel* ambient_ui_model() { return &ambient_ui_model_; }
@@ -175,7 +176,7 @@
 
   AmbientAccessTokenController access_token_controller_;
   std::unique_ptr<AmbientBackendController> ambient_backend_controller_;
-  std::unique_ptr<AmbientPhotoController> ambient_photo_controller_;
+  AmbientPhotoController ambient_photo_controller_;
 
   // Monitors the device inactivity and controls the auto-show of ambient.
   base::OneShotTimer inactivity_timer_;
diff --git a/ash/ambient/ambient_photo_controller.cc b/ash/ambient/ambient_photo_controller.cc
index b935972..e414d18 100644
--- a/ash/ambient/ambient_photo_controller.cc
+++ b/ash/ambient/ambient_photo_controller.cc
@@ -104,14 +104,9 @@
 AmbientPhotoController::AmbientPhotoController()
     : fetch_topic_retry_backoff_(&kFetchTopicRetryBackoffPolicy),
       resume_fetch_image_backoff_(&kResumeFetchImageBackoffPolicy),
-      photo_cache_(AmbientPhotoCache::Create(GetCacheRootPath().Append(
-          FILE_PATH_LITERAL(kAmbientModeCacheDirectoryName)))),
-      backup_photo_cache_(AmbientPhotoCache::Create(GetCacheRootPath().Append(
-          FILE_PATH_LITERAL(kAmbientModeBackupCacheDirectoryName)))),
       task_runner_(
           base::ThreadPool::CreateSequencedTaskRunner(GetTaskTraits())) {
   ambient_backend_model_observer_.Add(&ambient_backend_model_);
-  ScheduleFetchBackupImages();
 }
 
 AmbientPhotoController::~AmbientPhotoController() = default;
@@ -144,6 +139,18 @@
   weak_factory_.InvalidateWeakPtrs();
 }
 
+void AmbientPhotoController::ScheduleFetchBackupImages() {
+  if (backup_photo_refresh_timer_.IsRunning())
+    return;
+
+  backup_photo_refresh_timer_.Start(
+      FROM_HERE,
+      std::max(kBackupPhotoRefreshDelay,
+               resume_fetch_image_backoff_.GetTimeUntilRelease()),
+      base::BindOnce(&AmbientPhotoController::FetchBackupImages,
+                     weak_factory_.GetWeakPtr()));
+}
+
 void AmbientPhotoController::OnTopicsChanged() {
   if (ambient_backend_model_.topics().size() < kMaxNumberOfCachedImages)
     ScheduleFetchTopics(/*backoff=*/false);
@@ -180,6 +187,17 @@
   backup_photo_cache_->Clear();
 }
 
+void AmbientPhotoController::InitCache() {
+  if (!photo_cache_) {
+    photo_cache_ = AmbientPhotoCache::Create(GetCacheRootPath().Append(
+        FILE_PATH_LITERAL(kAmbientModeCacheDirectoryName)));
+  }
+  if (!backup_photo_cache_) {
+    backup_photo_cache_ = AmbientPhotoCache::Create(GetCacheRootPath().Append(
+        FILE_PATH_LITERAL(kAmbientModeBackupCacheDirectoryName)));
+  }
+}
+
 void AmbientPhotoController::ScheduleFetchTopics(bool backoff) {
   // If retry, using the backoff delay, otherwise the default delay.
   const base::TimeDelta delay =
@@ -199,19 +217,6 @@
                      weak_factory_.GetWeakPtr()));
 }
 
-void AmbientPhotoController::ScheduleFetchBackupImages() {
-  DVLOG(3) << __func__;
-  if (backup_photo_refresh_timer_.IsRunning())
-    return;
-
-  backup_photo_refresh_timer_.Start(
-      FROM_HERE,
-      std::max(kBackupPhotoRefreshDelay,
-               resume_fetch_image_backoff_.GetTimeUntilRelease()),
-      base::BindOnce(&AmbientPhotoController::FetchBackupImages,
-                     weak_factory_.GetWeakPtr()));
-}
-
 void AmbientPhotoController::FetchBackupImages() {
   const auto& backup_photo_urls = GetBackupPhotoUrls();
   backup_retries_to_read_from_cache_ = backup_photo_urls.size();
diff --git a/ash/ambient/ambient_photo_controller.h b/ash/ambient/ambient_photo_controller.h
index d5446ba..81f1abcf 100644
--- a/ash/ambient/ambient_photo_controller.h
+++ b/ash/ambient/ambient_photo_controller.h
@@ -58,6 +58,8 @@
   void StartScreenUpdate();
   void StopScreenUpdate();
 
+  void ScheduleFetchBackupImages();
+
   AmbientBackendModel* ambient_backend_model() {
     return &ambient_backend_model_;
   }
@@ -66,7 +68,7 @@
     return photo_refresh_timer_;
   }
 
-  base::OneShotTimer& backup_photo_refresh_timer_for_testing() {
+  const base::OneShotTimer& backup_photo_refresh_timer_for_testing() const {
     return backup_photo_refresh_timer_;
   }
 
@@ -76,9 +78,10 @@
   // Clear cache when Settings changes.
   void ClearCache();
 
+  void InitCache();
+
  private:
   friend class AmbientAshTestBase;
-  friend class AmbientPhotoControllerTest;
 
   void FetchTopics();
 
@@ -88,8 +91,6 @@
 
   void ScheduleRefreshImage();
 
-  void ScheduleFetchBackupImages();
-
   // Download backup cache images.
   void FetchBackupImages();
 
diff --git a/ash/ambient/ambient_photo_controller_unittest.cc b/ash/ambient/ambient_photo_controller_unittest.cc
index 925ed78..748588da9 100644
--- a/ash/ambient/ambient_photo_controller_unittest.cc
+++ b/ash/ambient/ambient_photo_controller_unittest.cc
@@ -80,10 +80,6 @@
                               loop.QuitClosure());
     loop.Run();
   }
-
-  void ScheduleFetchBackupImages() {
-    photo_controller()->ScheduleFetchBackupImages();
-  }
 };
 
 // Test that topics are downloaded when starting screen update.
@@ -286,7 +282,7 @@
   std::string expected_data = "backup data";
   SetBackupDownloadPhotoData(expected_data);
 
-  ScheduleFetchBackupImages();
+  photo_controller()->ScheduleFetchBackupImages();
 
   EXPECT_TRUE(
       photo_controller()->backup_photo_refresh_timer_for_testing().IsRunning());
@@ -312,7 +308,7 @@
 }
 
 TEST_F(AmbientPhotoControllerTest, ShouldResetTimerWhenBackupImagesFail) {
-  ScheduleFetchBackupImages();
+  photo_controller()->ScheduleFetchBackupImages();
 
   EXPECT_TRUE(
       photo_controller()->backup_photo_refresh_timer_for_testing().IsRunning());
@@ -330,7 +326,7 @@
 
 TEST_F(AmbientPhotoControllerTest,
        ShouldStartDownloadBackupImagesOnAmbientModeStart) {
-  ScheduleFetchBackupImages();
+  photo_controller()->ScheduleFetchBackupImages();
 
   EXPECT_TRUE(
       photo_controller()->backup_photo_refresh_timer_for_testing().IsRunning());
diff --git a/ash/ambient/test/ambient_ash_test_base.cc b/ash/ambient/test/ambient_ash_test_base.cc
index 3eae4f68..ed10e79 100644
--- a/ash/ambient/test/ambient_ash_test_base.cc
+++ b/ash/ambient/test/ambient_ash_test_base.cc
@@ -185,6 +185,10 @@
   ambient_controller()->set_backend_controller_for_testing(nullptr);
   ambient_controller()->set_backend_controller_for_testing(
       std::make_unique<FakeAmbientBackendControllerImpl>());
+  photo_controller()->set_photo_cache_for_testing(
+      std::make_unique<TestAmbientPhotoCacheImpl>());
+  photo_controller()->set_backup_photo_cache_for_testing(
+      std::make_unique<TestAmbientPhotoCacheImpl>());
   token_controller()->SetTokenUsageBufferForTesting(
       base::TimeDelta::FromSeconds(30));
   SetAmbientModeEnabled(true);
@@ -198,14 +202,6 @@
 void AmbientAshTestBase::SetAmbientModeEnabled(bool enabled) {
   Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean(
       ambient::prefs::kAmbientModeEnabled, enabled);
-
-  if (enabled) {
-    photo_controller()->set_photo_cache_for_testing(
-        std::make_unique<TestAmbientPhotoCacheImpl>());
-    photo_controller()->set_backup_photo_cache_for_testing(
-        std::make_unique<TestAmbientPhotoCacheImpl>());
-    photo_controller()->backup_photo_refresh_timer_for_testing().Stop();
-  }
 }
 
 void AmbientAshTestBase::ShowAmbientScreen() {
diff --git a/ash/ambient/ui/assistant_response_container_view.cc b/ash/ambient/ui/assistant_response_container_view.cc
index 759b461b..9ebc8ebb 100644
--- a/ash/ambient/ui/assistant_response_container_view.cc
+++ b/ash/ambient/ui/assistant_response_container_view.cc
@@ -15,6 +15,7 @@
 #include "ash/assistant/ui/main_stage/assistant_text_element_view.h"
 #include "ash/assistant/ui/main_stage/element_animator.h"
 #include "ui/views/layout/box_layout.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 
 namespace ash {
 
@@ -34,10 +35,6 @@
 
 AssistantResponseContainerView::~AssistantResponseContainerView() = default;
 
-const char* AssistantResponseContainerView::GetClassName() const {
-  return "AssistantResponseContainerView";
-}
-
 gfx::Size AssistantResponseContainerView::CalculatePreferredSize() const {
   return gfx::Size(kPreferredWidthDip,
                    content_view()->GetHeightForWidth(kPreferredWidthDip));
@@ -88,4 +85,7 @@
       std::make_unique<AssistantErrorElementView>(error_element));
 }
 
+BEGIN_METADATA(AssistantResponseContainerView, AnimatedContainerView)
+END_METADATA
+
 }  //  namespace ash
diff --git a/ash/ambient/ui/assistant_response_container_view.h b/ash/ambient/ui/assistant_response_container_view.h
index 01aac1d..31b58d8 100644
--- a/ash/ambient/ui/assistant_response_container_view.h
+++ b/ash/ambient/ui/assistant_response_container_view.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "ash/assistant/ui/main_stage/animated_container_view.h"
-#include "base/macros.h"
 
 namespace ash {
 
@@ -18,11 +17,16 @@
 
 class AssistantResponseContainerView : public AnimatedContainerView {
  public:
+  METADATA_HEADER(AssistantResponseContainerView);
+
   explicit AssistantResponseContainerView(AssistantViewDelegate* delegate);
+  AssistantResponseContainerView(const AssistantResponseContainerView&) =
+      delete;
+  AssistantResponseContainerView& operator=(
+      const AssistantResponseContainerView&) = delete;
   ~AssistantResponseContainerView() override;
 
   // AnimatedContainerView:
-  const char* GetClassName() const override;
   gfx::Size CalculatePreferredSize() const override;
   void OnContentsPreferredSizeChanged(views::View* content_view) override;
 
@@ -34,8 +38,6 @@
   // AnimatedContainerView:
   std::unique_ptr<ElementAnimator> HandleUiElement(
       const AssistantUiElement* ui_element) override;
-
-  DISALLOW_COPY_AND_ASSIGN(AssistantResponseContainerView);
 };
 
 }  //  namespace ash
diff --git a/ash/ambient/ui/glanceable_info_view.cc b/ash/ambient/ui/glanceable_info_view.cc
index 9dac125..52bfce2 100644
--- a/ash/ambient/ui/glanceable_info_view.cc
+++ b/ash/ambient/ui/glanceable_info_view.cc
@@ -30,6 +30,7 @@
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 #include "ui/views/view.h"
 
 namespace ash {
@@ -86,11 +87,6 @@
 }
 
 GlanceableInfoView::~GlanceableInfoView() = default;
-
-const char* GlanceableInfoView::GetClassName() const {
-  return "GlanceableInfoView";
-}
-
 void GlanceableInfoView::OnWeatherInfoUpdated() {
   Show();
 }
@@ -174,4 +170,7 @@
       0, 0, GetTimeFontDescent() - GetTemperatureFontDescent(), 0));
 }
 
+BEGIN_METADATA(GlanceableInfoView, views::View)
+END_METADATA
+
 }  // namespace ash
diff --git a/ash/ambient/ui/glanceable_info_view.h b/ash/ambient/ui/glanceable_info_view.h
index 10516c07..23fb212 100644
--- a/ash/ambient/ui/glanceable_info_view.h
+++ b/ash/ambient/ui/glanceable_info_view.h
@@ -27,14 +27,13 @@
 class GlanceableInfoView : public views::View,
                            public AmbientBackendModelObserver {
  public:
+  METADATA_HEADER(GlanceableInfoView);
+
   explicit GlanceableInfoView(AmbientViewDelegate* delegate);
   GlanceableInfoView(const GlanceableInfoView&) = delete;
   GlanceableInfoView& operator=(const GlanceableInfoView&) = delete;
   ~GlanceableInfoView() override;
 
-  // views::View:
-  const char* GetClassName() const override;
-
   // AmbientBackendModelObserver:
   void OnWeatherInfoUpdated() override;
 
diff --git a/ash/ambient/ui/media_string_view.cc b/ash/ambient/ui/media_string_view.cc
index c03c396..0660680 100644
--- a/ash/ambient/ui/media_string_view.cc
+++ b/ash/ambient/ui/media_string_view.cc
@@ -36,6 +36,7 @@
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 
 namespace ash {
 
@@ -45,7 +46,7 @@
 // zones.
 class FadeoutLayerDelegate : public ui::LayerDelegate {
  public:
-  explicit FadeoutLayerDelegate() : layer_(ui::LAYER_TEXTURED) {
+  FadeoutLayerDelegate() : layer_(ui::LAYER_TEXTURED) {
     layer_.set_delegate(this);
     layer_.SetFillsBoundsOpaquely(false);
   }
@@ -124,10 +125,6 @@
 
 MediaStringView::~MediaStringView() = default;
 
-const char* MediaStringView::GetClassName() const {
-  return "MediaStringView";
-}
-
 void MediaStringView::OnViewBoundsChanged(views::View* observed_view) {
   UpdateMaskLayer();
 }
@@ -344,4 +341,7 @@
   }
 }
 
+BEGIN_METADATA(MediaStringView, views::View)
+END_METADATA
+
 }  // namespace ash
diff --git a/ash/ambient/ui/media_string_view.h b/ash/ambient/ui/media_string_view.h
index fe71978..0ce2a4ff 100644
--- a/ash/ambient/ui/media_string_view.h
+++ b/ash/ambient/ui/media_string_view.h
@@ -34,14 +34,13 @@
                         public media_session::mojom::MediaControllerObserver,
                         public ui::ImplicitAnimationObserver {
  public:
+  METADATA_HEADER(MediaStringView);
+
   MediaStringView();
   MediaStringView(const MediaStringView&) = delete;
   MediaStringView& operator=(const MediaStringView&) = delete;
   ~MediaStringView() override;
 
-  // views::View:
-  const char* GetClassName() const override;
-
   // views::ViewObserver:
   void OnViewBoundsChanged(views::View* observed_view) override;
 
diff --git a/ash/ambient/ui/photo_view.cc b/ash/ambient/ui/photo_view.cc
index df7fc45e..1fbeffe 100644
--- a/ash/ambient/ui/photo_view.cc
+++ b/ash/ambient/ui/photo_view.cc
@@ -22,6 +22,7 @@
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/layout/fill_layout.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 
 namespace ash {
 
@@ -46,10 +47,6 @@
 
 PhotoView::~PhotoView() = default;
 
-const char* PhotoView::GetClassName() const {
-  return "PhotoView";
-}
-
 void PhotoView::OnImageAdded() {
   // If NeedToAnimate() is true, will start transition animation and
   // UpdateImages() when animation completes. Otherwise, update images
@@ -152,4 +149,7 @@
   return image_views_.at(image_index_)->GetCurrentImage();
 }
 
+BEGIN_METADATA(PhotoView, views::View)
+END_METADATA
+
 }  // namespace ash
diff --git a/ash/ambient/ui/photo_view.h b/ash/ambient/ui/photo_view.h
index 9d6aae7..c2c39bd 100644
--- a/ash/ambient/ui/photo_view.h
+++ b/ash/ambient/ui/photo_view.h
@@ -32,14 +32,13 @@
                              public AmbientBackendModelObserver,
                              public ui::ImplicitAnimationObserver {
  public:
+  METADATA_HEADER(PhotoView);
+
   explicit PhotoView(AmbientViewDelegate* delegate);
   PhotoView(const PhotoView&) = delete;
   PhotoView& operator=(PhotoView&) = delete;
   ~PhotoView() override;
 
-  // views::View:
-  const char* GetClassName() const override;
-
   // AmbientBackendModelObserver:
   void OnImageAdded() override;
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index f160b19..a8cf9de 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1226,16 +1226,9 @@
       <message name="IDS_ASH_PHONE_HUB_ENABLE_HOTSPOT_NO_RECEPTION_STATE_TOOLTIP" desc="Tooltip message that indicates to the user that the Enable Hotspot feature is disabled because their phone does not have mobile data.">
         Your phone must have mobile data to provide a hotspot
       </message>
-
-      <message name="IDS_ASH_STYLUS_BATTERY_LOW_LABEL">
+      <message name="IDS_ASH_STYLUS_BATTERY_LOW_LABEL" desc="The label next to the stylus battery indicator when the battery level is below 24%">
         Low
       </message>
-      <message name="IDS_ASH_STYLUS_BATTERY_MED_LABEL">
-        Med
-      </message>
-      <message name="IDS_ASH_STYLUS_BATTERY_HI_LABEL">
-        Hi
-      </message>
       <message name="IDS_ASH_STYLUS_TOOLS_CAPTURE_REGION_ACTION" desc="Title of the capture region action in the stylus tools (a pop-up panel next to the status tray). This causes a partial screenshot to be taken (the user selects an area of the screen to take a screenshot of).">
         Capture region
       </message>
@@ -1359,6 +1352,9 @@
       <message name="IDS_ASH_OVERVIEW_WINDOW_CLOSING_A11Y_ALERT" desc="The accessibility alert read by screen readers to alert the user that a window in overview mode is closing.">
         Window <ph name="WINDOW_TITILE">$1<ex>1</ex></ph> closed.
       </message>
+      <message name="IDS_ASH_OVERVIEW_VISIBLE_ON_ALL_DESKS_TOAST" desc="The text for the toast that appears when user tries to drag a visible on all desks window to a desk.">
+        Already assigned to all desks.
+      </message>
 
       <!-- Virtual Desks -->
       <message name="IDS_ASH_DESKS_NEW_DESK_BUTTON" desc="The label of the new virtual desk (a.k.a. workspaces) button.">
@@ -3165,6 +3161,9 @@
       </message>
 
       <!-- Capture Mode -->
+      <message name="IDS_ASH_SCREEN_CAPTURE_HDCP_BLOCKED_MESSAGE" desc="The text of the notification that informs the user the screen recording is blocked due to existence of protected content.">
+        Screen capture is not allowed while protected content is present
+      </message>
       <message name="IDS_ASH_SCREEN_CAPTURE_DISABLED_TITLE" desc="The title of the notification when capture mode is disabled.">
         Screen capture is blocked
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_OVERVIEW_VISIBLE_ON_ALL_DESKS_TOAST.png.sha1 b/ash/ash_strings_grd/IDS_ASH_OVERVIEW_VISIBLE_ON_ALL_DESKS_TOAST.png.sha1
new file mode 100644
index 0000000..9f9a621
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_OVERVIEW_VISIBLE_ON_ALL_DESKS_TOAST.png.sha1
@@ -0,0 +1 @@
+0feb78aa3efb3b45ffd5e7ec3c2ad58b3a51026a
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SCREEN_CAPTURE_HDCP_BLOCKED_MESSAGE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SCREEN_CAPTURE_HDCP_BLOCKED_MESSAGE.png.sha1
new file mode 100644
index 0000000..cde6887
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SCREEN_CAPTURE_HDCP_BLOCKED_MESSAGE.png.sha1
@@ -0,0 +1 @@
+fe2d0b79ce066bd00acca7ec224ca239cfa1e8a9
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STYLUS_BATTERY_HI_LABEL.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STYLUS_BATTERY_HI_LABEL.png.sha1
deleted file mode 100644
index 4254ac3..0000000
--- a/ash/ash_strings_grd/IDS_ASH_STYLUS_BATTERY_HI_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-f9dfa5adb30c929b0694cbf46ed7893fee05d1fb
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STYLUS_BATTERY_MED_LABEL.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STYLUS_BATTERY_MED_LABEL.png.sha1
deleted file mode 100644
index f9b12a6..0000000
--- a/ash/ash_strings_grd/IDS_ASH_STYLUS_BATTERY_MED_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a2bda703b9e32c023a76e2845d05b1c17718630e
\ No newline at end of file
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc
index ea45c251..70ff0b8 100644
--- a/ash/capture_mode/capture_mode_controller.cc
+++ b/ash/capture_mode/capture_mode_controller.cc
@@ -45,6 +45,7 @@
 #include "ui/base/clipboard/clipboard_data.h"
 #include "ui/base/clipboard/clipboard_non_backed.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/display/types/display_constants.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/public/cpp/notification.h"
 #include "ui/message_center/public/cpp/notification_delegate.h"
@@ -158,78 +159,69 @@
 
 // Shows a Capture Mode related notification with the given parameters.
 void ShowNotification(
-    const base::string16& title,
-    const base::string16& message,
+    const std::string& notification_id,
+    int title_id,
+    int message_id,
     const message_center::RichNotificationData& optional_fields,
-    scoped_refptr<message_center::NotificationDelegate> delegate) {
+    scoped_refptr<message_center::NotificationDelegate> delegate,
+    message_center::SystemNotificationWarningLevel warning_level =
+        message_center::SystemNotificationWarningLevel::NORMAL,
+    const gfx::VectorIcon& notification_icon = kCaptureModeIcon) {
   const auto type = optional_fields.image.IsEmpty()
                         ? message_center::NOTIFICATION_TYPE_SIMPLE
                         : message_center::NOTIFICATION_TYPE_IMAGE;
   std::unique_ptr<message_center::Notification> notification =
       CreateSystemNotification(
-          type, kScreenCaptureNotificationId, title, message,
+          type, notification_id, l10n_util::GetStringUTF16(title_id),
+          l10n_util::GetStringUTF16(message_id),
           l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_DISPLAY_SOURCE),
           GURL(),
           message_center::NotifierId(
               message_center::NotifierType::SYSTEM_COMPONENT,
               kScreenCaptureNotifierId),
-          optional_fields, delegate, kCaptureModeIcon,
-          message_center::SystemNotificationWarningLevel::NORMAL);
+          optional_fields, delegate, notification_icon, warning_level);
 
   // Remove the previous notification before showing the new one if there is
   // any.
   auto* message_center = message_center::MessageCenter::Get();
-  message_center->RemoveNotification(kScreenCaptureNotificationId,
+  message_center->RemoveNotification(notification_id,
                                      /*by_user=*/false);
   message_center->AddNotification(std::move(notification));
 }
 
 // Shows a notification informing the user that Capture Mode operations are
-// currently disabled.
-void ShowDisabledNotification() {
-  std::unique_ptr<message_center::Notification> notification =
-      CreateSystemNotification(
-          message_center::NOTIFICATION_TYPE_SIMPLE,
-          kScreenCaptureNotificationId,
-          l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_DISABLED_TITLE),
-          l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_DISABLED_MESSAGE),
-          /*display_source=*/base::string16(), GURL(),
-          message_center::NotifierId(
-              message_center::NotifierType::SYSTEM_COMPONENT,
-              kScreenCaptureNotifierId),
-          /*optional_fields=*/{}, /*delegate=*/nullptr,
-          vector_icons::kBusinessIcon,
-          message_center::SystemNotificationWarningLevel::CRITICAL_WARNING);
-  message_center::MessageCenter::Get()->AddNotification(
-      std::move(notification));
+// currently disabled. If |for_hdcp| is true, then this was due to a content-
+// enforced protection, otherwise it was due to DLP which is admin enforced.
+void ShowDisabledNotification(bool for_hdcp) {
+  ShowNotification(
+      kScreenCaptureNotificationId, IDS_ASH_SCREEN_CAPTURE_DISABLED_TITLE,
+      for_hdcp ? IDS_ASH_SCREEN_CAPTURE_HDCP_BLOCKED_MESSAGE
+               : IDS_ASH_SCREEN_CAPTURE_DISABLED_MESSAGE,
+      /*optional_fields=*/{}, /*delegate=*/nullptr,
+      message_center::SystemNotificationWarningLevel::CRITICAL_WARNING,
+      for_hdcp ? kCaptureModeIcon : vector_icons::kBusinessIcon);
 }
 
 // Shows a notification informing the user that a Capture Mode operation has
 // failed.
 void ShowFailureNotification() {
-  ShowNotification(
-      l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_FAILURE_TITLE),
-      l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_FAILURE_MESSAGE),
-      /*optional_fields=*/{}, /*delegate=*/nullptr);
+  ShowNotification(kScreenCaptureStoppedNotificationId,
+                   IDS_ASH_SCREEN_CAPTURE_FAILURE_TITLE,
+                   IDS_ASH_SCREEN_CAPTURE_FAILURE_MESSAGE,
+                   /*optional_fields=*/{}, /*delegate=*/nullptr);
 }
 
-// Shows a notification informing the user that video recording was stopped.
-void ShowVideoRecordingStoppedNotification() {
-  std::unique_ptr<message_center::Notification> notification =
-      CreateSystemNotification(
-          message_center::NOTIFICATION_TYPE_SIMPLE,
-          kScreenCaptureStoppedNotificationId,
-          l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_STOPPED_TITLE),
-          l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_STOPPED_MESSAGE),
-          /*display_source=*/base::string16(), GURL(),
-          message_center::NotifierId(
-              message_center::NotifierType::SYSTEM_COMPONENT,
-              kScreenCaptureNotifierId),
-          /*optional_fields=*/{}, /*delegate=*/nullptr,
-          vector_icons::kBusinessIcon,
-          message_center::SystemNotificationWarningLevel::CRITICAL_WARNING);
-  message_center::MessageCenter::Get()->AddNotification(
-      std::move(notification));
+// Shows a notification informing the user that video recording was stopped. If
+// |for_hdcp| is true, then this was due to a content-enforced protection,
+// otherwise it was due to DLP which is admin enforced.
+void ShowVideoRecordingStoppedNotification(bool for_hdcp) {
+  ShowNotification(
+      kScreenCaptureStoppedNotificationId, IDS_ASH_SCREEN_CAPTURE_STOPPED_TITLE,
+      for_hdcp ? IDS_ASH_SCREEN_CAPTURE_HDCP_BLOCKED_MESSAGE
+               : IDS_ASH_SCREEN_CAPTURE_DISABLED_MESSAGE,
+      /*optional_fields=*/{}, /*delegate=*/nullptr,
+      message_center::SystemNotificationWarningLevel::CRITICAL_WARNING,
+      for_hdcp ? kCaptureModeIcon : vector_icons::kBusinessIcon);
 }
 
 // Copies the bitmap representation of the given |image| to the clipboard.
@@ -326,7 +318,7 @@
     return;
 
   if (delegate_->IsCaptureModeInitRestricted()) {
-    ShowDisabledNotification();
+    ShowDisabledNotification(/*for_hdcp=*/false);
     return;
   }
 
@@ -345,12 +337,18 @@
           kResetCaptureRegionDuration) {
     SetUserCaptureRegion(gfx::Rect(), /*by_user=*/false);
   }
+
+  delegate_->OnSessionStateChanged(/*started=*/true);
+
   capture_mode_session_ = std::make_unique<CaptureModeSession>(this);
 }
 
 void CaptureModeController::Stop() {
   DCHECK(IsActive());
+  capture_mode_session_->ReportSessionHistograms();
   capture_mode_session_.reset();
+
+  delegate_->OnSessionStateChanged(/*started=*/false);
 }
 
 void CaptureModeController::SetUserCaptureRegion(const gfx::Rect& region,
@@ -362,7 +360,7 @@
 
 void CaptureModeController::CaptureScreenshotsOfAllDisplays() {
   if (delegate_->IsCaptureModeInitRestricted()) {
-    ShowDisabledNotification();
+    ShowDisabledNotification(/*for_hdcp=*/false);
     return;
   }
   // Get a vector of RootWindowControllers with primary root window at first.
@@ -390,20 +388,23 @@
     return;
 
   if (!IsCaptureAllowed(*capture_params)) {
-    ShowDisabledNotification();
+    ShowDisabledNotification(/*for_hdcp=*/false);
     Stop();
     return;
   }
 
-  DCHECK(capture_mode_session_);
-  capture_mode_session_->ReportSessionHistograms();
-
   if (type_ == CaptureModeType::kImage) {
     CaptureImage(*capture_params, BuildImagePath());
-  }
+  } else {
+    // HDCP affects only video recording.
+    if (ShouldBlockRecordingForContentProtection(capture_params->window)) {
+      ShowDisabledNotification(/*for_hdcp=*/true);
+      Stop();
+      return;
+    }
 
-  else
     CaptureVideo(*capture_params);
+  }
 }
 
 void CaptureModeController::EndVideoRecording(EndRecordingReason reason) {
@@ -416,6 +417,31 @@
   delegate_->OpenFeedbackDialog();
 }
 
+void CaptureModeController::SetWindowProtectionMask(aura::Window* window,
+                                                    uint32_t protection_mask) {
+  if (protection_mask == display::CONTENT_PROTECTION_METHOD_NONE)
+    protected_windows_.erase(window);
+  else
+    protected_windows_[window] = protection_mask;
+
+  RefreshContentProtection();
+}
+
+void CaptureModeController::RefreshContentProtection() {
+  if (!is_recording_in_progress_)
+    return;
+
+  DCHECK(video_recording_watcher_);
+  if (ShouldBlockRecordingForContentProtection(
+          video_recording_watcher_->window_being_recorded())) {
+    // HDCP violation is also considered a failure, and we're not going to wait
+    // for any buffered frames in the recording service.
+    RecordEndRecordingReason(EndRecordingReason::kHdcpInterruption);
+    OnRecordingEnded(/*success=*/false);
+    ShowVideoRecordingStoppedNotification(/*for_hdcp=*/true);
+  }
+}
+
 void CaptureModeController::OnMuxerOutput(const std::string& chunk) {
   DCHECK(video_file_handler_);
   video_file_handler_.AsyncCall(&VideoFileHandler::AppendChunk)
@@ -470,6 +496,22 @@
   OnVideoRecordCountDownFinished();
 }
 
+bool CaptureModeController::ShouldBlockRecordingForContentProtection(
+    aura::Window* window) const {
+  if (window->IsRootWindow()) {
+    // Recording fullscreen or partial region of it. Block if this root has a
+    // window with protection.
+    for (const auto& iter : protected_windows_) {
+      if (iter.first->GetRootWindow() == window)
+        return true;
+    }
+
+    return false;
+  }
+
+  return protected_windows_.contains(window);
+}
+
 void CaptureModeController::EndSessionOrRecording(EndRecordingReason reason) {
   if (IsActive()) {
     // Suspend or user session changes can happen while the capture mode session
@@ -767,14 +809,11 @@
     const gfx::Image& preview_image,
     const CaptureModeType type) {
   const bool for_video = type == CaptureModeType::kVideo;
-  const base::string16 title = l10n_util::GetStringUTF16(
-      for_video ? IDS_ASH_SCREEN_CAPTURE_RECORDING_TITLE
-                : IDS_ASH_SCREEN_CAPTURE_SCREENSHOT_TITLE);
-  const base::string16 message =
-      for_video && low_disk_space_threshold_reached_
-          ? l10n_util::GetStringUTF16(
-                IDS_ASH_SCREEN_CAPTURE_LOW_DISK_SPACE_MESSAGE)
-          : l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_MESSAGE);
+  const int title_id = for_video ? IDS_ASH_SCREEN_CAPTURE_RECORDING_TITLE
+                                 : IDS_ASH_SCREEN_CAPTURE_SCREENSHOT_TITLE;
+  const int message_id = for_video && low_disk_space_threshold_reached_
+                             ? IDS_ASH_SCREEN_CAPTURE_LOW_DISK_SPACE_MESSAGE
+                             : IDS_ASH_SCREEN_CAPTURE_MESSAGE;
 
   message_center::RichNotificationData optional_fields;
   message_center::ButtonInfo edit_button(
@@ -788,7 +827,7 @@
   optional_fields.image = preview_image;
 
   ShowNotification(
-      title, message, optional_fields,
+      kScreenCaptureNotificationId, title_id, message_id, optional_fields,
       base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
           base::BindRepeating(&CaptureModeController::HandleNotificationClicked,
                               weak_ptr_factory_.GetWeakPtr(),
@@ -897,6 +936,18 @@
   if (!capture_params)
     return;
 
+  // During the 3-second count down, screen content might have changed such that
+  // admin-restricted or HDCP content became present. We must check again.
+  if (!IsCaptureAllowed(*capture_params)) {
+    ShowDisabledNotification(/*for_hdcp=*/false);
+    return;
+  }
+
+  if (ShouldBlockRecordingForContentProtection(capture_params->window)) {
+    ShowDisabledNotification(/*for_hdcp=*/true);
+    return;
+  }
+
   // We enable the software-composited cursor, in order for the video capturer
   // to be able to record it.
   is_recording_in_progress_ = true;
@@ -941,7 +992,7 @@
 }
 
 void CaptureModeController::InterruptVideoRecording() {
-  ShowVideoRecordingStoppedNotification();
+  ShowVideoRecordingStoppedNotification(/*for_hdcp=*/false);
   EndVideoRecording(EndRecordingReason::kDlpInterruption);
 }
 
diff --git a/ash/capture_mode/capture_mode_controller.h b/ash/capture_mode/capture_mode_controller.h
index 02b45bd1..b5a3ed15 100644
--- a/ash/capture_mode/capture_mode_controller.h
+++ b/ash/capture_mode/capture_mode_controller.h
@@ -95,6 +95,20 @@
   // Called when the feedback button on the capture bar is pressed.
   void OpenFeedbackDialog();
 
+  // Sets the |protection_mask| that is currently set on the given |window|. If
+  // the |protection_mask| is |display::CONTENT_PROTECTION_METHOD_NONE|, then
+  // the window will no longer be tracked.
+  // Note that content protection (a.k.a. HDCP (High-bandwidth Digital Content
+  // Protection)) is different from DLP (Data Leak Prevention). The latter is
+  // enforced by admins and applies to both image and video capture, whereas
+  // the former is enforced by apps and content providers and is applied only to
+  // video capture.
+  void SetWindowProtectionMask(aura::Window* window, uint32_t protection_mask);
+
+  // If a video recording is in progress, it will end if so required by content
+  // protection.
+  void RefreshContentProtection();
+
   // recording::mojom::RecordingServiceClient:
   void OnMuxerOutput(const std::string& chunk) override;
   void OnRecordingEnded(bool success) override;
@@ -114,6 +128,10 @@
  private:
   friend class CaptureModeTestApi;
 
+  // Returns true if screen recording needs to be blocked due to protected
+  // content. |window| is the window being recorded or desired to be recorded.
+  bool ShouldBlockRecordingForContentProtection(aura::Window* window) const;
+
   // Used by user session change, and suspend events to end the capture mode
   // session if it's active, or stop the video recording if one is in progress.
   void EndSessionOrRecording(EndRecordingReason reason);
@@ -276,6 +294,13 @@
   // Watches events that lead to ending video recording.
   std::unique_ptr<VideoRecordingWatcher> video_recording_watcher_;
 
+  // Tracks the windows that currently have content protection enabled, so that
+  // we prevent them from being video recorded. Each window is mapped to its
+  // cureently-set protection_mask. Windows in this map are only the ones that
+  // have protection masks other than |display::CONTENT_PROTECTION_METHOD_NONE|.
+  base::flat_map<aura::Window*, /*protection_mask*/ uint32_t>
+      protected_windows_;
+
   // If set, it will be called when either an image or video file is saved.
   base::OnceCallback<void(const base::FilePath&)> on_file_saved_callback_;
 
diff --git a/ash/capture_mode/capture_mode_metrics.h b/ash/capture_mode/capture_mode_metrics.h
index 462ad32..a1daa23 100644
--- a/ash/capture_mode/capture_mode_metrics.h
+++ b/ash/capture_mode/capture_mode_metrics.h
@@ -25,7 +25,8 @@
   kFileIoError,
   kDlpInterruption,
   kLowDiskSpace,
-  kMaxValue = kLowDiskSpace,
+  kHdcpInterruption,
+  kMaxValue = kHdcpInterruption,
 };
 
 // Enumeration of capture bar buttons that can be pressed while in capture mode.
diff --git a/ash/capture_mode/capture_mode_session.cc b/ash/capture_mode/capture_mode_session.cc
index e1cf721..7587118 100644
--- a/ash/capture_mode/capture_mode_session.cc
+++ b/ash/capture_mode/capture_mode_session.cc
@@ -23,6 +23,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
 #include "cc/paint/paint_flags.h"
+#include "ui/aura/client/capture_client.h"
 #include "ui/aura/window.h"
 #include "ui/base/cursor/cursor_factory.h"
 #include "ui/base/cursor/cursor_util.h"
@@ -445,12 +446,28 @@
 
   UpdateRootWindowDimmers();
 
+  // A context menu may have input capture when entering a session. Remove
+  // capture from it, otherwise subsequent mouse events will cause it to close,
+  // and then we won't be able to take a screenshot of the menu. Store it so we
+  // can return capture to it when exiting the session.
+  auto* capture_client = aura::client::GetCaptureClient(current_root_);
+  input_capture_window_ = capture_client->GetCaptureWindow();
+  if (input_capture_window_) {
+    capture_client->ReleaseCapture(input_capture_window_);
+    input_capture_window_->AddObserver(this);
+  }
+
   TabletModeController::Get()->AddObserver(this);
   current_root_->AddObserver(this);
   display::Screen::GetScreen()->AddObserver(this);
 }
 
 CaptureModeSession::~CaptureModeSession() {
+  if (input_capture_window_) {
+    input_capture_window_->RemoveObserver(this);
+    aura::client::GetCaptureClient(current_root_)
+        ->SetCapture(input_capture_window_);
+  }
   display::Screen::GetScreen()->RemoveObserver(this);
   current_root_->RemoveObserver(this);
   TabletModeController::Get()->RemoveObserver(this);
@@ -655,6 +672,11 @@
 }
 
 void CaptureModeSession::OnWindowDestroying(aura::Window* window) {
+  if (window == input_capture_window_) {
+    input_capture_window_->RemoveObserver(this);
+    input_capture_window_ = nullptr;
+    return;
+  }
   DCHECK_EQ(current_root_, window);
   MaybeChangeRoot(Shell::GetPrimaryRootWindow());
 }
diff --git a/ash/capture_mode/capture_mode_session.h b/ash/capture_mode/capture_mode_session.h
index 7c8f42b..f288354 100644
--- a/ash/capture_mode/capture_mode_session.h
+++ b/ash/capture_mode/capture_mode_session.h
@@ -305,6 +305,10 @@
   // The current focused fine tune position. This changes as user tabs while a
   // in capture region mode.
   FineTunePosition focused_fine_tune_position_ = FineTunePosition::kNone;
+
+  // The window which had input capture prior to entering the session. It may be
+  // null if no such window existed.
+  aura::Window* input_capture_window_ = nullptr;
 };
 
 }  // namespace ash
diff --git a/ash/capture_mode/capture_mode_unittests.cc b/ash/capture_mode/capture_mode_unittests.cc
index 194f61e..5051d2b 100644
--- a/ash/capture_mode/capture_mode_unittests.cc
+++ b/ash/capture_mode/capture_mode_unittests.cc
@@ -17,11 +17,13 @@
 #include "ash/capture_mode/capture_mode_util.h"
 #include "ash/capture_mode/stop_recording_button_tray.h"
 #include "ash/display/cursor_window_controller.h"
+#include "ash/display/output_protection_delegate.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/home_screen/home_screen_controller.h"
 #include "ash/magnifier/magnifier_glass.h"
 #include "ash/public/cpp/ash_features.h"
+#include "ash/public/cpp/capture_mode_test_api.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/system/status_area_widget.h"
@@ -29,6 +31,8 @@
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
 #include "ash/wm/window_state.h"
+#include "ash/wm/window_util.h"
+#include "base/callback_helpers.h"
 #include "base/run_loop.h"
 #include "base/scoped_observation.h"
 #include "base/test/bind.h"
@@ -39,6 +43,7 @@
 #include "components/account_id/account_id.h"
 #include "ui/aura/window_tracker.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/display/types/display_constants.h"
 #include "ui/events/keycodes/keyboard_codes_posix.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/geometry/insets.h"
@@ -319,6 +324,14 @@
     loop.Run();
   }
 
+  void WaitForCaptureFileToBeSaved() {
+    base::RunLoop run_loop;
+    CaptureModeTestApi().SetOnCaptureFileSavedCallback(
+        base::BindLambdaForTesting(
+            [&run_loop](const base::FilePath& path) { run_loop.Quit(); }));
+    run_loop.Run();
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -1500,6 +1513,171 @@
   EXPECT_FALSE(window->subtree_capture_id().is_valid());
 }
 
+// Tests the behavior of screen recording with the presence of HDCP secure
+// content on the screen in all capture mode sources (fullscreen, region, and
+// window) depending on the test param.
+class CaptureModeHdcpTest
+    : public CaptureModeTest,
+      public ::testing::WithParamInterface<CaptureModeSource> {
+ public:
+  CaptureModeHdcpTest() = default;
+  ~CaptureModeHdcpTest() override = default;
+
+  // CaptureModeTest:
+  void SetUp() override {
+    CaptureModeTest::SetUp();
+    window_ = CreateTestWindow(gfx::Rect(200, 200));
+    protection_delegate_ =
+        std::make_unique<OutputProtectionDelegate>(window_.get());
+    CaptureModeController::Get()->SetUserCaptureRegion(gfx::Rect(20, 50),
+                                                       /*by_user=*/true);
+  }
+
+  void TearDown() override {
+    protection_delegate_.reset();
+    window_.reset();
+    CaptureModeTest::TearDown();
+  }
+
+  // Enters the capture mode session.
+  void StartSessionForVideo() {
+    StartCaptureSession(GetParam(), CaptureModeType::kVideo);
+  }
+
+  // Starts video recording from the capture mode source set by the test param.
+  void StartRecording() {
+    auto* controller = CaptureModeController::Get();
+    ASSERT_TRUE(controller->IsActive());
+
+    switch (GetParam()) {
+      case CaptureModeSource::kFullscreen:
+      case CaptureModeSource::kRegion:
+        controller->StartVideoRecordingImmediatelyForTesting();
+        break;
+
+      case CaptureModeSource::kWindow:
+        // Window capture mode selects the window under the cursor as the
+        // capture source.
+        auto* event_generator = GetEventGenerator();
+        event_generator->MoveMouseToCenterOf(window_.get());
+        controller->StartVideoRecordingImmediatelyForTesting();
+        break;
+    }
+  }
+
+ protected:
+  std::unique_ptr<aura::Window> window_;
+  std::unique_ptr<OutputProtectionDelegate> protection_delegate_;
+};
+
+TEST_P(CaptureModeHdcpTest, WindowBecomesProtectedWhileRecording) {
+  StartSessionForVideo();
+  StartRecording();
+
+  auto* controller = CaptureModeController::Get();
+  EXPECT_TRUE(controller->is_recording_in_progress());
+
+  // The window becomes HDCP protected, which should end video recording.
+  base::HistogramTester histogram_tester;
+  protection_delegate_->SetProtection(display::CONTENT_PROTECTION_METHOD_HDCP,
+                                      base::DoNothing());
+
+  EXPECT_FALSE(controller->is_recording_in_progress());
+  histogram_tester.ExpectBucketCount(
+      kEndRecordingReasonInClamshellHistogramName,
+      EndRecordingReason::kHdcpInterruption, 1);
+}
+
+TEST_P(CaptureModeHdcpTest, ProtectedWindowDestruction) {
+  auto window_2 = CreateTestWindow(gfx::Rect(100, 50));
+  OutputProtectionDelegate protection_delegate_2(window_2.get());
+  protection_delegate_2.SetProtection(display::CONTENT_PROTECTION_METHOD_HDCP,
+                                      base::DoNothing());
+
+  StartSessionForVideo();
+  StartRecording();
+
+  // Recording cannot start because of another protected window on the screen,
+  // except when we're capturing a different |window_|.
+  auto* controller = CaptureModeController::Get();
+  EXPECT_FALSE(controller->IsActive());
+  if (GetParam() == CaptureModeSource::kWindow) {
+    EXPECT_TRUE(controller->is_recording_in_progress());
+    controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton);
+    EXPECT_FALSE(controller->is_recording_in_progress());
+    // Wait for the video file to be saved so that we can start a new recording.
+    WaitForCaptureFileToBeSaved();
+  } else {
+    EXPECT_FALSE(controller->is_recording_in_progress());
+  }
+
+  // When the protected window is destroyed, it's possbile now to record from
+  // all capture sources.
+  window_2.reset();
+  StartSessionForVideo();
+  StartRecording();
+
+  EXPECT_FALSE(controller->IsActive());
+  EXPECT_TRUE(controller->is_recording_in_progress());
+}
+
+TEST_P(CaptureModeHdcpTest, WindowBecomesProtectedBeforeRecording) {
+  protection_delegate_->SetProtection(display::CONTENT_PROTECTION_METHOD_HDCP,
+                                      base::DoNothing());
+  StartSessionForVideo();
+  StartRecording();
+
+  // Recording cannot even start.
+  auto* controller = CaptureModeController::Get();
+  EXPECT_FALSE(controller->is_recording_in_progress());
+  EXPECT_FALSE(controller->IsActive());
+}
+
+TEST_P(CaptureModeHdcpTest, ProtectedWindowInMultiDisplay) {
+  UpdateDisplay("400x400,401+0-400x400");
+  auto roots = Shell::GetAllRootWindows();
+  ASSERT_EQ(2u, roots.size());
+  protection_delegate_->SetProtection(display::CONTENT_PROTECTION_METHOD_HDCP,
+                                      base::DoNothing());
+
+  // Move the cursor to the secondary display before starting the session to
+  // make sure the session starts on that display.
+  auto* event_generator = GetEventGenerator();
+  MoveMouseToAndUpdateCursorDisplay(roots[1]->GetBoundsInScreen().CenterPoint(),
+                                    event_generator);
+  StartSessionForVideo();
+  // Also, make sure the selected region is in the secondary display.
+  auto* controller = CaptureModeController::Get();
+  EXPECT_EQ(controller->capture_mode_session()->current_root(), roots[1]);
+  StartRecording();
+
+  // Recording should be able to start (since the protected window is on the
+  // first display) unless the protected window itself is the one being
+  // recorded.
+  if (GetParam() == CaptureModeSource::kWindow) {
+    EXPECT_FALSE(controller->is_recording_in_progress());
+  } else {
+    EXPECT_TRUE(controller->is_recording_in_progress());
+
+    // Moving the protected window to the display being recorded should
+    // terminate the recording.
+    base::HistogramTester histogram_tester;
+    window_util::MoveWindowToDisplay(window_.get(),
+                                     roots[1]->GetHost()->GetDisplayId());
+    ASSERT_EQ(window_->GetRootWindow(), roots[1]);
+    EXPECT_FALSE(controller->is_recording_in_progress());
+    histogram_tester.ExpectBucketCount(
+        kEndRecordingReasonInClamshellHistogramName,
+        EndRecordingReason::kHdcpInterruption, 1);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         CaptureModeHdcpTest,
+                         testing::Values(CaptureModeSource::kFullscreen,
+                                         CaptureModeSource::kRegion,
+                                         CaptureModeSource::kWindow));
+
 TEST_F(CaptureModeTest, ClosingWindowBeingRecorded) {
   auto window = CreateTestWindow(gfx::Rect(200, 200));
   StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo);
diff --git a/ash/capture_mode/test_capture_mode_delegate.cc b/ash/capture_mode/test_capture_mode_delegate.cc
index 4d606e5..1689abc 100644
--- a/ash/capture_mode/test_capture_mode_delegate.cc
+++ b/ash/capture_mode/test_capture_mode_delegate.cc
@@ -121,4 +121,6 @@
 void TestCaptureModeDelegate::BindAudioStreamFactory(
     mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) {}
 
+void TestCaptureModeDelegate::OnSessionStateChanged(bool started) {}
+
 }  // namespace ash
diff --git a/ash/capture_mode/test_capture_mode_delegate.h b/ash/capture_mode/test_capture_mode_delegate.h
index 88503048..572d249 100644
--- a/ash/capture_mode/test_capture_mode_delegate.h
+++ b/ash/capture_mode/test_capture_mode_delegate.h
@@ -41,6 +41,7 @@
       override;
   void BindAudioStreamFactory(
       mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) override;
+  void OnSessionStateChanged(bool started) override;
 
  private:
   std::unique_ptr<FakeRecordingService> fake_service_;
diff --git a/ash/clipboard/clipboard_history.cc b/ash/clipboard/clipboard_history.cc
index 5891cbb6..2965c00 100644
--- a/ash/clipboard/clipboard_history.cc
+++ b/ash/clipboard/clipboard_history.cc
@@ -6,6 +6,7 @@
 
 #include "ash/clipboard/clipboard_history_util.h"
 #include "ash/clipboard/clipboard_nudge_controller.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/stl_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "ui/base/clipboard/clipboard_monitor.h"
@@ -66,8 +67,6 @@
   if (!ClipboardHistoryUtil::IsEnabledInCurrentMode())
     return;
 
-  // TODO(newcomer): Prevent Clipboard from recording metrics when pausing
-  // observation.
   if (num_pause_ > 0)
     return;
 
@@ -87,6 +86,15 @@
     return;
   }
 
+  // Debounce calls to `OnClipboardOperation()`. Certain surfaces
+  // (Omnibox) may Read/Write to the clipboard multiple times in one user
+  // initiated operation.
+  clipboard_histogram_weak_factory_.InvalidateWeakPtrs();
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&ClipboardHistory::OnClipboardOperation,
+                                clipboard_histogram_weak_factory_.GetWeakPtr(),
+                                /*copy=*/true));
+
   // We post commit |clipboard_data| at the end of the current task sequence to
   // debounce the case where multiple copies are programmatically performed.
   // Since only the most recent copy will be at the top of the clipboard, the
@@ -102,6 +110,39 @@
                      commit_data_weak_factory_.GetWeakPtr(), *clipboard_data));
 }
 
+void ClipboardHistory::OnClipboardDataRead() {
+  if (num_pause_ > 0)
+    return;
+
+  // Debounce calls to `OnClipboardOperation()`. Certain surfaces
+  // (Omnibox) may Read/Write to the clipboard multiple times in one user
+  // initiated operation.
+  clipboard_histogram_weak_factory_.InvalidateWeakPtrs();
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&ClipboardHistory::OnClipboardOperation,
+                                clipboard_histogram_weak_factory_.GetWeakPtr(),
+                                /*copy=*/false));
+}
+
+void ClipboardHistory::OnClipboardOperation(bool copy) {
+  if (copy) {
+    consecutive_copies_++;
+    if (consecutive_pastes_ > 0) {
+      base::UmaHistogramCounts100("Ash.Clipboard.ConsecutivePastes",
+                                  consecutive_pastes_);
+      consecutive_pastes_ = 0;
+    }
+    return;
+  }
+
+  consecutive_pastes_++;
+  if (consecutive_copies_ > 0) {
+    base::UmaHistogramCounts100("Ash.Clipboard.ConsecutiveCopies",
+                                consecutive_copies_);
+    consecutive_copies_ = 0;
+  }
+}
+
 base::WeakPtr<ClipboardHistory> ClipboardHistory::GetWeakPtr() {
   return weak_factory_.GetWeakPtr();
 }
diff --git a/ash/clipboard/clipboard_history.h b/ash/clipboard/clipboard_history.h
index d2ce410..8a2ffb5c 100644
--- a/ash/clipboard/clipboard_history.h
+++ b/ash/clipboard/clipboard_history.h
@@ -56,8 +56,9 @@
   // do nothing.
   void RemoveItemForId(const base::UnguessableToken& id);
 
-  // ClipboardMonitor:
+  // ui::ClipboardObserver:
   void OnClipboardDataChanged() override;
+  void OnClipboardDataRead() override;
 
   base::WeakPtr<ClipboardHistory> GetWeakPtr();
 
@@ -73,9 +74,18 @@
   void Pause();
   void Resume();
 
+  // Keeps track of consecutive clipboard operations and records metrics.
+  void OnClipboardOperation(bool copy);
+
   // The count of pauses.
   size_t num_pause_ = 0;
 
+  // The number of consecutive copies, reset after a paste.
+  int consecutive_copies_ = 0;
+
+  // The number of consecutive pastes, reset after a copy.
+  int consecutive_pastes_ = 0;
+
   // The history of data copied to the Clipboard. Items of the list are sorted
   // by recency.
   std::list<ClipboardHistoryItem> history_list_;
@@ -84,9 +94,14 @@
   // ClipboardHistory.
   mutable base::ObserverList<Observer> observers_;
 
-  // Factory to create WeakPtrs used to debounce calls to CommitData().
+  // Factory to create WeakPtrs used to debounce calls to `CommitData()`.
   base::WeakPtrFactory<ClipboardHistory> commit_data_weak_factory_{this};
 
+  // Factory to create WeakPtrs used to debounce calls to
+  // `OnClipboardOperation()`.
+  base::WeakPtrFactory<ClipboardHistory> clipboard_histogram_weak_factory_{
+      this};
+
   // Factory to create WeakPtrs for ClipboardHistory.
   base::WeakPtrFactory<ClipboardHistory> weak_factory_{this};
 };
diff --git a/ash/clipboard/clipboard_history_controller_impl.cc b/ash/clipboard/clipboard_history_controller_impl.cc
index 90fc252..7dc8caf 100644
--- a/ash/clipboard/clipboard_history_controller_impl.cc
+++ b/ash/clipboard/clipboard_history_controller_impl.cc
@@ -260,9 +260,25 @@
   accelerator_target_->OnMenuShown();
 
   // The first menu item should be selected as default after the clipboard
-  // history menu shows.
-  context_menu_->SelectMenuItemWithCommandId(
-      ClipboardHistoryUtil::kFirstItemCommandId);
+  // history menu shows. Note that the menu item is selected asynchronously
+  // to avoid the interference from synthesized mouse events.
+  menu_task_timer_.Start(
+      FROM_HERE, base::TimeDelta(),
+      base::BindOnce(
+          [](const base::WeakPtr<ClipboardHistoryControllerImpl>&
+                 controller_weak_ptr) {
+            if (!controller_weak_ptr)
+              return;
+
+            controller_weak_ptr->context_menu_->SelectMenuItemWithCommandId(
+                ClipboardHistoryUtil::kFirstItemCommandId);
+            if (controller_weak_ptr->initial_item_selected_callback_for_test_) {
+              controller_weak_ptr->initial_item_selected_callback_for_test_
+                  .Run();
+            }
+          },
+          weak_ptr_factory_.GetWeakPtr()));
+
   for (auto& observer : observers_)
     observer.OnClipboardHistoryMenuShown();
 }
@@ -567,14 +583,15 @@
 
   // Reset `context_menu_` in the asynchronous way. Because the menu may be
   // accessed after `OnMenuClosed()` is called.
-  base::SequencedTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(
-                     [](const base::WeakPtr<ClipboardHistoryControllerImpl>&
-                            controller_weak_ptr) {
-                       if (controller_weak_ptr)
-                         controller_weak_ptr->context_menu_.reset();
-                     },
-                     weak_ptr_factory_.GetWeakPtr()));
+  menu_task_timer_.Start(
+      FROM_HERE, base::TimeDelta(),
+      base::BindOnce(
+          [](const base::WeakPtr<ClipboardHistoryControllerImpl>&
+                 controller_weak_ptr) {
+            if (controller_weak_ptr)
+              controller_weak_ptr->context_menu_.reset();
+          },
+          weak_ptr_factory_.GetWeakPtr()));
 }
 
 }  // namespace ash
diff --git a/ash/clipboard/clipboard_history_controller_impl.h b/ash/clipboard/clipboard_history_controller_impl.h
index 23aa497453..6ddde181 100644
--- a/ash/clipboard/clipboard_history_controller_impl.h
+++ b/ash/clipboard/clipboard_history_controller_impl.h
@@ -12,9 +12,11 @@
 #include "ash/clipboard/clipboard_history.h"
 #include "ash/clipboard/clipboard_history_item.h"
 #include "ash/public/cpp/clipboard_history_controller.h"
+#include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/optional.h"
+#include "base/timer/timer.h"
 
 namespace gfx {
 class Rect;
@@ -71,6 +73,11 @@
     return context_menu_.get();
   }
 
+  void set_initial_item_selected_callback_for_test(
+      base::RepeatingClosure new_callback) {
+    initial_item_selected_callback_for_test_ = new_callback;
+  }
+
  private:
   class AcceleratorTarget;
   class MenuDelegate;
@@ -141,6 +148,15 @@
   // Whether a paste is currently being performed.
   bool currently_pasting_ = false;
 
+  // Used to post asynchronous tasks when opening or closing the clipboard
+  // history menu. Note that those tasks have data races between each other.
+  // The timer can guarantee that at most one task is alive.
+  base::OneShotTimer menu_task_timer_;
+
+  // Called when the first item view is selected after the clipboard history
+  // menu opens.
+  base::RepeatingClosure initial_item_selected_callback_for_test_;
+
   base::WeakPtrFactory<ClipboardHistoryControllerImpl> weak_ptr_factory_{this};
 };
 
diff --git a/ash/clipboard/clipboard_history_controller_unittest.cc b/ash/clipboard/clipboard_history_controller_unittest.cc
index adb3e6bf..05effb5 100644
--- a/ash/clipboard/clipboard_history_controller_unittest.cc
+++ b/ash/clipboard/clipboard_history_controller_unittest.cc
@@ -187,7 +187,6 @@
   } kTestCases[] = {{user_manager::USER_TYPE_REGULAR, true},
                     {user_manager::USER_TYPE_GUEST, true},
                     {user_manager::USER_TYPE_PUBLIC_ACCOUNT, false},
-                    {user_manager::USER_TYPE_SUPERVISED, true},
                     {user_manager::USER_TYPE_KIOSK_APP, false},
                     {user_manager::USER_TYPE_CHILD, true},
                     {user_manager::USER_TYPE_ARC_KIOSK_APP, false},
diff --git a/ash/clipboard/clipboard_history_util.cc b/ash/clipboard/clipboard_history_util.cc
index 04cf7e6..241ca44 100644
--- a/ash/clipboard/clipboard_history_util.cc
+++ b/ash/clipboard/clipboard_history_util.cc
@@ -123,7 +123,7 @@
       return false;
     case LoginStatus::USER:
     case LoginStatus::GUEST:
-    case LoginStatus::SUPERVISED:
+    case LoginStatus::CHILD:
       return true;
   }
 }
diff --git a/ash/display/display_prefs.cc b/ash/display/display_prefs.cc
index a856436..60d754e 100644
--- a/ash/display/display_prefs.cc
+++ b/ash/display/display_prefs.cc
@@ -187,7 +187,7 @@
     return false;
   return *user_type == user_manager::USER_TYPE_REGULAR ||
          *user_type == user_manager::USER_TYPE_CHILD ||
-         *user_type == user_manager::USER_TYPE_SUPERVISED ||
+         *user_type == user_manager::USER_TYPE_SUPERVISED_DEPRECATED ||
          *user_type == user_manager::USER_TYPE_KIOSK_APP;
 }
 
diff --git a/ash/display/output_protection_delegate.cc b/ash/display/output_protection_delegate.cc
index d96d2606a..3caedee 100644
--- a/ash/display/output_protection_delegate.cc
+++ b/ash/display/output_protection_delegate.cc
@@ -4,6 +4,8 @@
 
 #include "ash/display/output_protection_delegate.h"
 
+#include "ash/capture_mode/capture_mode_controller.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/shell.h"
 #include "base/callback_helpers.h"
 #include "ui/display/display.h"
@@ -20,6 +22,14 @@
   return Shell::Get()->display_configurator()->content_protection_manager();
 }
 
+void MaybeSetCaptureModeWindowProtection(aura::Window* window,
+                                         uint32_t protection_mask) {
+  if (features::IsCaptureModeEnabled()) {
+    CaptureModeController::Get()->SetWindowProtectionMask(window,
+                                                          protection_mask);
+  }
+}
+
 }  // namespace
 
 struct OutputProtectionDelegate::ClientIdHolder {
@@ -48,6 +58,8 @@
 
   display::Screen::GetScreen()->RemoveObserver(this);
   window_->RemoveObserver(this);
+  MaybeSetCaptureModeWindowProtection(window_,
+                                      display::CONTENT_PROTECTION_METHOD_NONE);
 }
 
 void OutputProtectionDelegate::OnDisplayMetricsChanged(
@@ -76,6 +88,8 @@
   DCHECK_EQ(window, window_);
   display::Screen::GetScreen()->RemoveObserver(this);
   window_->RemoveObserver(this);
+  MaybeSetCaptureModeWindowProtection(window_,
+                                      display::CONTENT_PROTECTION_METHOD_NONE);
   window_ = nullptr;
 }
 
@@ -93,6 +107,13 @@
 
 void OutputProtectionDelegate::SetProtection(uint32_t protection_mask,
                                              SetProtectionCallback callback) {
+  protection_mask_ = protection_mask;
+
+  // Capture mode screen recording doesn't rely on display protection, and hence
+  // must be informed with the new window's protection.
+  if (window_)
+    MaybeSetCaptureModeWindowProtection(window_, protection_mask);
+
   if (!RegisterClientIfNecessary()) {
     std::move(callback).Run(/*success=*/false);
     return;
@@ -100,7 +121,6 @@
 
   manager()->ApplyContentProtection(client_->id, display_id_, protection_mask,
                                     std::move(callback));
-  protection_mask_ = protection_mask;
 }
 
 void OutputProtectionDelegate::OnWindowMayHaveMovedToAnotherDisplay() {
@@ -118,6 +138,11 @@
     manager()->ApplyContentProtection(client_->id, display_id_,
                                       display::CONTENT_PROTECTION_METHOD_NONE,
                                       base::DoNothing());
+
+    // The window may have moved to a display that is currently being recorded,
+    // so we need to refresh Capture Mode's content protection.
+    if (features::IsCaptureModeEnabled())
+      CaptureModeController::Get()->RefreshContentProtection();
   }
   display_id_ = new_display_id;
 }
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 54df7423..a036e83 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -1993,8 +1993,10 @@
   Shell::Get()->login_screen_controller()->OnFocusPod(big_user_account_id);
   UpdateEasyUnlockIconForUser(big_user_account_id);
 
-  // http://crbug/866790: After Supervised Users are deprecated, remove this.
-  if (big_user.basic_user_info.type == user_manager::USER_TYPE_SUPERVISED) {
+  // TODO(crbug/1164090): After Supervised Users are deprecated, remove this.
+  if (big_user.basic_user_info.type ==
+      user_manager::USER_TYPE_SUPERVISED_DEPRECATED) {
+    // TODO(crbug/1164090): Remove this string and the associated screenshot.
     base::string16 message = l10n_util::GetStringUTF16(
         IDS_ASH_LOGIN_POD_LEGACY_SUPERVISED_EXPIRATION_WARNING);
     // Shows supervised user deprecation message as a persistent error bubble.
diff --git a/ash/login/ui/lock_contents_view.h b/ash/login/ui/lock_contents_view.h
index f085dee..ad318b4 100644
--- a/ash/login/ui/lock_contents_view.h
+++ b/ash/login/ui/lock_contents_view.h
@@ -470,6 +470,7 @@
   // Bubble for displaying warning banner message.
   LoginErrorBubble* warning_banner_bubble_;
   // Bubble for displaying supervised user deprecation message.
+  // TODO(crbug/1164090): Remove this.
   LoginErrorBubble* supervised_user_deprecation_bubble_;
 
   // Bottom status indicator displaying entreprise domain or ADB enabled alert
diff --git a/ash/login/ui/login_password_view.cc b/ash/login/ui/login_password_view.cc
index b613081..4ced3391 100644
--- a/ash/login/ui/login_password_view.cc
+++ b/ash/login/ui/login_password_view.cc
@@ -941,14 +941,22 @@
   }
 
   if (handled_should_show) {
+    // If the view that is currently handled should be shown, we immediately
+    // show it; if the other view should be shown as well, we make it invisible
+    // for the moment and start a cyclic animation that will show these two
+    // views alternatively.
     handled_view->SetVisible(true);
     if (other_should_show) {
       other_view->SetVisible(false);
       left_icon_->ScheduleAnimation(handled_view, other_view);
     }
   } else {
+    // If the view that is currently handled should now be invisible, we
+    // immediately hide it and we abort the cyclic animation if it was running.
+    // We also make the other view visible if needed, as its current state
+    // may depend on how long the animation has been running.
     left_icon_->AbortAnimationIfAny();
-    other_view->SetVisible(should_show_easy_unlock_);
+    other_view->SetVisible(other_should_show);
     handled_view->SetVisible(false);
   }
   password_row_->Layout();
diff --git a/ash/login/ui/login_password_view_test.cc b/ash/login/ui/login_password_view_test.cc
index 0b42156..23568c5 100644
--- a/ash/login/ui/login_password_view_test.cc
+++ b/ash/login/ui/login_password_view_test.cc
@@ -356,6 +356,12 @@
                            base::string16() /*accessibility_label*/);
   EXPECT_TRUE(test_api.easy_unlock_icon()->GetVisible());
   EXPECT_FALSE(test_api.capslock_icon()->GetVisible());
+
+  // Hide the easy unlock icon, the capslock icon should be shown.
+  view_->SetEasyUnlockIcon(EasyUnlockIconId::NONE,
+                           base::string16() /*accessibility_label*/);
+  EXPECT_FALSE(test_api.easy_unlock_icon()->GetVisible());
+  EXPECT_TRUE(test_api.capslock_icon()->GetVisible());
 }
 
 // Verifies that the password textfield clears after a delay when the display
diff --git a/ash/login/ui/login_user_menu_view.cc b/ash/login/ui/login_user_menu_view.cc
index c9fb416..2a993959 100644
--- a/ash/login/ui/login_user_menu_view.cc
+++ b/ash/login/ui/login_user_menu_view.cc
@@ -203,7 +203,8 @@
     user_manager::UserType type = user.basic_user_info.type;
     base::string16 part1 = l10n_util::GetStringUTF16(
         IDS_ASH_LOGIN_POD_NON_OWNER_USER_REMOVE_WARNING_PART_1);
-    if (type == user_manager::UserType::USER_TYPE_SUPERVISED) {
+    // TODO(crbug/1164090): Remove this section and the strings.
+    if (type == user_manager::UserType::USER_TYPE_SUPERVISED_DEPRECATED) {
       part1 = l10n_util::GetStringFUTF16(
           IDS_ASH_LOGIN_POD_LEGACY_SUPERVISED_USER_REMOVE_WARNING,
           base::UTF8ToUTF16(kLegacySupervisedUserManagementDisplayURL));
diff --git a/ash/login_status.h b/ash/login_status.h
index 6d835e65..560729a 100644
--- a/ash/login_status.h
+++ b/ash/login_status.h
@@ -13,7 +13,7 @@
   USER,           // A regular user is logged in
   GUEST,          // A guest is logged in (i.e. incognito)
   PUBLIC,         // A public account is logged in
-  SUPERVISED,     // A supervised user is logged in
+  CHILD,          // A Family Link user is logged in
   KIOSK_APP       // In kiosk mode for Chrome app, ARC, or PWA.
 };
 
diff --git a/ash/public/cpp/capture_mode_delegate.h b/ash/public/cpp/capture_mode_delegate.h
index 71a964c..7ef1a9f 100644
--- a/ash/public/cpp/capture_mode_delegate.h
+++ b/ash/public/cpp/capture_mode_delegate.h
@@ -85,6 +85,9 @@
   // Binds the given audio StreamFactory |receiver| to the audio service.
   virtual void BindAudioStreamFactory(
       mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) = 0;
+
+  // Called when a capture mode session starts or stops.
+  virtual void OnSessionStateChanged(bool started) = 0;
 };
 
 }  // namespace ash
diff --git a/ash/public/cpp/test/shell_test_api.h b/ash/public/cpp/test/shell_test_api.h
index a0d6891..5fa468ba 100644
--- a/ash/public/cpp/test/shell_test_api.h
+++ b/ash/public/cpp/test/shell_test_api.h
@@ -116,6 +116,14 @@
   // It returns nullptr when app-list is not shown.
   PaginationModel* GetAppListPaginationModel();
 
+  // Shows the context menu associated with the primary root window at point (1,
+  // 1).
+  void ShowContextMenu();
+
+  // Returns true if the context menu associated with the primary root window is
+  // shown.
+  bool IsContextMenuShown() const;
+
  private:
   Shell* shell_;  // not owned
 
diff --git a/ash/rotator/screen_rotation_animator.cc b/ash/rotator/screen_rotation_animator.cc
index 13f2cef0..5c28d86 100644
--- a/ash/rotator/screen_rotation_animator.cc
+++ b/ash/rotator/screen_rotation_animator.cc
@@ -360,11 +360,10 @@
 
 std::unique_ptr<ui::LayerTreeOwner> ScreenRotationAnimator::CopyLayerTree(
     std::unique_ptr<viz::CopyOutputResult> result) {
+  DCHECK_EQ(result->size(),
+            GetScreenRotationContainer(root_window_)->layer()->size());
   std::unique_ptr<ui::Layer> copy_layer =
       CreateLayerFromCopyOutputResult(std::move(result));
-  const gfx::Rect rect(
-      GetScreenRotationContainer(root_window_)->layer()->size());
-  copy_layer->SetBounds(rect);
   // TODO(crbug.com/1040279): This is a workaround and should be removed once
   // the issue is fixed.
   copy_layer->SetFillsBoundsOpaquely(false);
diff --git a/ash/rotator/screen_rotation_animator_unittest.cc b/ash/rotator/screen_rotation_animator_unittest.cc
index 6eec862cc..ed6e3cf 100644
--- a/ash/rotator/screen_rotation_animator_unittest.cc
+++ b/ash/rotator/screen_rotation_animator_unittest.cc
@@ -539,7 +539,12 @@
 // request callback called, it should stop rotating.
 TEST_F(ScreenRotationAnimatorSmoothAnimationTest,
        RemoveExternalSecondaryDisplayBeforeSecondCopyCallback) {
-  UpdateDisplay("640x480,800x600");
+  {
+    // Disable wallpaper animation on a secondary display.
+    ui::ScopedAnimationDurationScaleMode disable(
+        ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
+    UpdateDisplay("640x480,800x600");
+  }
   EXPECT_EQ(2U, display_manager()->GetNumDisplays());
 
   const int64_t primary_display_id = display_manager()->GetDisplayAt(0).id();
diff --git a/ash/session/session_controller_impl.cc b/ash/session/session_controller_impl.cc
index 2476f14..1c40040 100644
--- a/ash/session/session_controller_impl.cc
+++ b/ash/session/session_controller_impl.cc
@@ -158,23 +158,15 @@
   return (*it).get();
 }
 
-bool SessionControllerImpl::IsUserSupervised() const {
+bool SessionControllerImpl::IsUserChildOrDeprecatedSupervised() const {
   if (!IsActiveUserSessionStarted())
     return false;
 
   user_manager::UserType active_user_type = GetUserSession(0)->user_info.type;
-  return active_user_type == user_manager::USER_TYPE_SUPERVISED ||
+  return active_user_type == user_manager::USER_TYPE_SUPERVISED_DEPRECATED ||
          active_user_type == user_manager::USER_TYPE_CHILD;
 }
 
-bool SessionControllerImpl::IsUserLegacySupervised() const {
-  if (!IsActiveUserSessionStarted())
-    return false;
-
-  user_manager::UserType active_user_type = GetUserSession(0)->user_info.type;
-  return active_user_type == user_manager::USER_TYPE_SUPERVISED;
-}
-
 bool SessionControllerImpl::IsUserChild() const {
   if (!IsActiveUserSessionStarted())
     return false;
@@ -567,12 +559,10 @@
       return LoginStatus::GUEST;
     case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
       return LoginStatus::PUBLIC;
-    case user_manager::USER_TYPE_SUPERVISED:
-      return LoginStatus::SUPERVISED;
     case user_manager::USER_TYPE_KIOSK_APP:
       return LoginStatus::KIOSK_APP;
     case user_manager::USER_TYPE_CHILD:
-      return LoginStatus::SUPERVISED;
+      return LoginStatus::CHILD;
     case user_manager::USER_TYPE_ARC_KIOSK_APP:
       return LoginStatus::KIOSK_APP;
     case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
@@ -581,6 +571,7 @@
     case user_manager::USER_TYPE_WEB_KIOSK_APP:
       return LoginStatus::KIOSK_APP;
     case user_manager::NUM_USER_TYPES:
+    case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
       // Avoid having a "default" case so the compiler catches new enum values.
       NOTREACHED();
       return LoginStatus::USER;
diff --git a/ash/session/session_controller_impl.h b/ash/session/session_controller_impl.h
index 02cf66d..cc0c009 100644
--- a/ash/session/session_controller_impl.h
+++ b/ash/session/session_controller_impl.h
@@ -106,12 +106,10 @@
   // Gets the primary user session.
   const UserSession* GetPrimaryUserSession() const;
 
-  // Returns true if the current user is supervised: has legacy supervised
-  // account or kid account.
-  bool IsUserSupervised() const;
-
-  // Returns true if the current user is legacy supervised.
-  bool IsUserLegacySupervised() const;
+  // Returns true if the current user is supervised: has deprecated legacy
+  // supervised account or kid account.
+  // TODO(crbug/1155729): Remove and replace all calls with IsUserChild().
+  bool IsUserChildOrDeprecatedSupervised() const;
 
   // Returns true if the current user is a child account.
   bool IsUserChild() const;
diff --git a/ash/session/session_controller_impl_unittest.cc b/ash/session/session_controller_impl_unittest.cc
index 8904def..b3a5fa5 100644
--- a/ash/session/session_controller_impl_unittest.cc
+++ b/ash/session/session_controller_impl_unittest.cc
@@ -294,9 +294,8 @@
       {user_manager::USER_TYPE_REGULAR, LoginStatus::USER},
       {user_manager::USER_TYPE_GUEST, LoginStatus::GUEST},
       {user_manager::USER_TYPE_PUBLIC_ACCOUNT, LoginStatus::PUBLIC},
-      {user_manager::USER_TYPE_SUPERVISED, LoginStatus::SUPERVISED},
       {user_manager::USER_TYPE_KIOSK_APP, LoginStatus::KIOSK_APP},
-      {user_manager::USER_TYPE_CHILD, LoginStatus::SUPERVISED},
+      {user_manager::USER_TYPE_CHILD, LoginStatus::CHILD},
       {user_manager::USER_TYPE_ARC_KIOSK_APP, LoginStatus::KIOSK_APP},
       {user_manager::USER_TYPE_WEB_KIOSK_APP, LoginStatus::KIOSK_APP}
       // TODO: Add USER_TYPE_ACTIVE_DIRECTORY if we add a status for it.
@@ -408,15 +407,6 @@
   }
 }
 
-TEST_F(SessionControllerImplTest, IsUserSupervised) {
-  UserSession session;
-  session.session_id = 1u;
-  session.user_info.type = user_manager::USER_TYPE_SUPERVISED;
-  controller()->UpdateUserSession(session);
-
-  EXPECT_TRUE(controller()->IsUserSupervised());
-}
-
 TEST_F(SessionControllerImplTest, IsUserChild) {
   UserSession session;
   session.session_id = 1u;
@@ -426,7 +416,7 @@
   EXPECT_TRUE(controller()->IsUserChild());
 
   // Child accounts are supervised.
-  EXPECT_TRUE(controller()->IsUserSupervised());
+  EXPECT_TRUE(controller()->IsUserChildOrDeprecatedSupervised());
 }
 
 using SessionControllerImplPrefsTest = NoSessionAshTestBase;
diff --git a/ash/shell_test_api.cc b/ash/shell_test_api.cc
index d2b28d1..7ee941eb 100644
--- a/ash/shell_test_api.cc
+++ b/ash/shell_test_api.cc
@@ -252,4 +252,13 @@
   return view->GetAppsPaginationModel();
 }
 
+void ShellTestApi::ShowContextMenu() {
+  Shell::GetPrimaryRootWindowController()->ShowContextMenu(
+      gfx::Point(1, 1), ui::MENU_SOURCE_MOUSE);
+}
+
+bool ShellTestApi::IsContextMenuShown() const {
+  return Shell::GetPrimaryRootWindowController()->IsContextMenuShown();
+}
+
 }  // namespace ash
diff --git a/ash/system/bluetooth/bluetooth_power_controller.cc b/ash/system/bluetooth/bluetooth_power_controller.cc
index f34d9a1..64211ae 100644
--- a/ash/system/bluetooth/bluetooth_power_controller.cc
+++ b/ash/system/bluetooth/bluetooth_power_controller.cc
@@ -294,7 +294,7 @@
     user_manager::UserType user_type) const {
   return user_type == user_manager::USER_TYPE_REGULAR ||
          user_type == user_manager::USER_TYPE_CHILD ||
-         user_type == user_manager::USER_TYPE_SUPERVISED ||
+         user_type == user_manager::USER_TYPE_SUPERVISED_DEPRECATED ||
          user_type == user_manager::USER_TYPE_ACTIVE_DIRECTORY;
 }
 
diff --git a/ash/system/dark_mode/dark_mode_feature_pod_controller.cc b/ash/system/dark_mode/dark_mode_feature_pod_controller.cc
index a6b7734..fe20ab9 100644
--- a/ash/system/dark_mode/dark_mode_feature_pod_controller.cc
+++ b/ash/system/dark_mode/dark_mode_feature_pod_controller.cc
@@ -33,6 +33,7 @@
   button_->SetLabel(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DARK_THEME));
   button_->SetLabelTooltip(l10n_util::GetStringUTF16(
       IDS_ASH_STATUS_TRAY_DARK_THEME_SETTINGS_TOOLTIP));
+  button_->ShowDetailedViewArrow();
   // TODO(minch): Add the logic for login screen.
   button_->SetVisible(
       Shell::Get()->session_controller()->IsActiveUserSessionStarted());
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index 4cc28e53..61afa9e 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -97,11 +97,14 @@
 
     icon_ = AddChildView(std::make_unique<views::ImageView>());
     icon_->SetImage(delegate->GetBatteryImage());
-    label_ = AddChildView(std::make_unique<views::Label>(
-        l10n_util::GetStringUTF16(delegate->GetLabelIdForBatteryLevel())));
-    label_->SetEnabledColor(delegate->GetColorForBatteryLevel());
-    TrayPopupUtils::SetLabelFontList(label_,
-                                     TrayPopupUtils::FontStyle::kSmallTitle);
+
+    if (delegate->IsBatteryLevelLow()) {
+      label_ = AddChildView(std::make_unique<views::Label>(
+          l10n_util::GetStringUTF16(IDS_ASH_STYLUS_BATTERY_LOW_LABEL)));
+      label_->SetEnabledColor(delegate->GetColorForBatteryLevel());
+      TrayPopupUtils::SetLabelFontList(label_,
+                                       TrayPopupUtils::FontStyle::kSmallTitle);
+    }
   }
 
  private:
diff --git a/ash/system/palette/stylus_battery_delegate.cc b/ash/system/palette/stylus_battery_delegate.cc
index 7568066..92a27eb 100644
--- a/ash/system/palette/stylus_battery_delegate.cc
+++ b/ash/system/palette/stylus_battery_delegate.cc
@@ -15,10 +15,8 @@
 
 namespace ash {
 namespace {
-// Battery percentage thresholds to used to label the battery level
-// as Hi, Med or Low.
+// Battery percentage threshold used to label the battery level as Low.
 constexpr int kStylusLowBatteryThreshold = 24;
-constexpr int kStylusMediumBatteryThreshold = 71;
 }  // namespace
 
 StylusBatteryDelegate::StylusBatteryDelegate() {
@@ -34,17 +32,7 @@
         AshColorProvider::ContentLayerType::kIconColorAlert);
   }
   return AshColorProvider::Get()->GetContentLayerColor(
-      AshColorProvider::ContentLayerType::kIconColorPositive);
-}
-
-int StylusBatteryDelegate::GetLabelIdForBatteryLevel() const {
-  if (battery_level_ <= kStylusLowBatteryThreshold) {
-    return IDS_ASH_STYLUS_BATTERY_LOW_LABEL;
-  }
-  if (battery_level_ <= kStylusMediumBatteryThreshold) {
-    return IDS_ASH_STYLUS_BATTERY_MED_LABEL;
-  }
-  return IDS_ASH_STYLUS_BATTERY_HI_LABEL;
+      AshColorProvider::ContentLayerType::kIconColorPrimary);
 }
 
 gfx::ImageSkia StylusBatteryDelegate::GetBatteryImage() const {
@@ -58,6 +46,10 @@
                                       icon_fg_color);
 }
 
+bool StylusBatteryDelegate::IsBatteryLevelLow() const {
+  return battery_level_ <= kStylusLowBatteryThreshold;
+}
+
 void StylusBatteryDelegate::OnAddingBattery(
     const PeripheralBatteryListener::BatteryInfo& battery) {
   battery_level_ = battery.level;
diff --git a/ash/system/palette/stylus_battery_delegate.h b/ash/system/palette/stylus_battery_delegate.h
index 33280c5..75c1145 100644
--- a/ash/system/palette/stylus_battery_delegate.h
+++ b/ash/system/palette/stylus_battery_delegate.h
@@ -22,8 +22,8 @@
   ~StylusBatteryDelegate() override;
 
   SkColor GetColorForBatteryLevel() const;
-  int GetLabelIdForBatteryLevel() const;
   gfx::ImageSkia GetBatteryImage() const;
+  bool IsBatteryLevelLow() const;
 
   base::Optional<uint8_t> battery_level() const { return battery_level_; }
 
diff --git a/ash/system/supervised/supervised_icon_string.cc b/ash/system/supervised/supervised_icon_string.cc
index 356a4c76..a9ad29d 100644
--- a/ash/system/supervised/supervised_icon_string.cc
+++ b/ash/system/supervised/supervised_icon_string.cc
@@ -19,8 +19,7 @@
   SessionControllerImpl* session_controller =
       Shell::Get()->session_controller();
 
-  if (session_controller->IsUserSupervised() &&
-      session_controller->IsUserChild())
+  if (session_controller->IsUserChild())
     return kSystemMenuSupervisedUserIcon;
 
   return kSystemMenuLegacySupervisedUserIcon;
@@ -29,7 +28,7 @@
 base::string16 GetSupervisedUserMessage() {
   SessionControllerImpl* session_controller =
       Shell::Get()->session_controller();
-  DCHECK(session_controller->IsUserSupervised());
+  DCHECK(session_controller->IsUserChildOrDeprecatedSupervised());
   DCHECK(session_controller->IsActiveUserSessionStarted());
 
   // Get the active user session.
@@ -41,6 +40,7 @@
       UTF8ToUTF16(user_session->second_custodian_email);
 
   // Regular supervised user. The "manager" is the first custodian.
+  // TODO(crbug/1164090): Remove this section.
   if (!Shell::Get()->session_controller()->IsUserChild()) {
     return l10n_util::GetStringFUTF16(IDS_ASH_USER_IS_SUPERVISED_BY_NOTICE,
                                       first_custodian);
diff --git a/ash/system/supervised/supervised_notification_controller.cc b/ash/system/supervised/supervised_notification_controller.cc
deleted file mode 100644
index fac6fe4..0000000
--- a/ash/system/supervised/supervised_notification_controller.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2018 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/supervised/supervised_notification_controller.h"
-
-#include "ash/public/cpp/notification_utils.h"
-#include "ash/session/session_controller_impl.h"
-#include "ash/shell.h"
-#include "ash/strings/grit/ash_strings.h"
-#include "ash/system/supervised/supervised_icon_string.h"
-#include "chromeos/ui/vector_icons/vector_icons.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/message_center/message_center.h"
-#include "ui/message_center/public/cpp/notification.h"
-#include "ui/message_center/public/cpp/notification_delegate.h"
-
-using message_center::MessageCenter;
-using message_center::Notification;
-
-namespace ash {
-
-namespace {
-const char kNotifierSupervisedUser[] = "ash.locally-managed-user";
-}  // namespace
-
-const char SupervisedNotificationController::kNotificationId[] =
-    "chrome://user/locally-managed";
-
-SupervisedNotificationController::SupervisedNotificationController() = default;
-
-SupervisedNotificationController::~SupervisedNotificationController() = default;
-
-void SupervisedNotificationController::OnActiveUserSessionChanged(
-    const AccountId& account_id) {
-  OnUserSessionUpdated(account_id);
-}
-
-void SupervisedNotificationController::OnUserSessionAdded(
-    const AccountId& account_id) {
-  OnUserSessionUpdated(account_id);
-}
-
-void SupervisedNotificationController::OnUserSessionUpdated(
-    const AccountId& account_id) {
-  SessionControllerImpl* session_controller =
-      Shell::Get()->session_controller();
-  if (!session_controller->IsUserSupervised())
-    return;
-
-  // Get the active user session.
-  DCHECK(session_controller->IsActiveUserSessionStarted());
-  const UserSession* const user_session = session_controller->GetUserSession(0);
-  DCHECK(user_session);
-
-  // Only respond to updates for the active user.
-  if (user_session->user_info.account_id != account_id)
-    return;
-
-  // Show notifications when custodian data first becomes available on login
-  // and if the custodian data changes.
-  if (custodian_email_ == user_session->custodian_email &&
-      second_custodian_email_ == user_session->second_custodian_email) {
-    return;
-  }
-  custodian_email_ = user_session->custodian_email;
-  second_custodian_email_ = user_session->second_custodian_email;
-
-  CreateOrUpdateNotification();
-}
-
-// static
-void SupervisedNotificationController::CreateOrUpdateNotification() {
-  // No notification for the child user.
-  if (Shell::Get()->session_controller()->IsUserChild())
-    return;
-
-  // Regular supervised user.
-  std::unique_ptr<Notification> notification = CreateSystemNotification(
-      message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId,
-      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SUPERVISED_LABEL),
-      GetSupervisedUserMessage(), base::string16() /* display_source */, GURL(),
-      message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
-                                 kNotifierSupervisedUser),
-      message_center::RichNotificationData(), nullptr,
-      chromeos::kNotificationSupervisedUserIcon,
-      message_center::SystemNotificationWarningLevel::NORMAL);
-  notification->SetSystemPriority();
-  // AddNotification does an update if the notification already exists.
-  MessageCenter::Get()->AddNotification(std::move(notification));
-}
-
-}  // namespace ash
diff --git a/ash/system/supervised/supervised_notification_controller.h b/ash/system/supervised/supervised_notification_controller.h
deleted file mode 100644
index 989c051..0000000
--- a/ash/system/supervised/supervised_notification_controller.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2018 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_SUPERVISED_SUPERVISED_NOTIFICATION_CONTROLLER_H_
-#define ASH_SYSTEM_SUPERVISED_SUPERVISED_NOTIFICATION_CONTROLLER_H_
-
-#include <string>
-
-#include "ash/ash_export.h"
-#include "ash/public/cpp/session/session_observer.h"
-#include "base/strings/string16.h"
-
-namespace ash {
-
-// Controller class to manage supervised user notification.
-class ASH_EXPORT SupervisedNotificationController : public SessionObserver {
- public:
-  SupervisedNotificationController();
-  ~SupervisedNotificationController() override;
-
-  // SessionObserver:
-  void OnActiveUserSessionChanged(const AccountId& account_id) override;
-  void OnUserSessionAdded(const AccountId& account_id) override;
-  void OnUserSessionUpdated(const AccountId& account_id) override;
-
- private:
-  friend class SupervisedNotificationControllerTest;
-
-  static const char kNotificationId[];
-
-  static void CreateOrUpdateNotification();
-
-  std::string custodian_email_;
-  std::string second_custodian_email_;
-
-  ScopedSessionObserver scoped_session_observer_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(SupervisedNotificationController);
-};
-
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_SUPERVISED_SUPERVISED_NOTIFICATION_CONTROLLER_H_
diff --git a/ash/system/supervised/supervised_notification_controller_unittest.cc b/ash/system/supervised/supervised_notification_controller_unittest.cc
deleted file mode 100644
index c6bb3897..0000000
--- a/ash/system/supervised/supervised_notification_controller_unittest.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2018 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/supervised/supervised_notification_controller.h"
-
-#include "ash/session/session_controller_impl.h"
-#include "ash/session/test_session_controller_client.h"
-#include "ash/shell.h"
-#include "ash/test/ash_test_base.h"
-#include "base/strings/utf_string_conversions.h"
-#include "ui/message_center/message_center.h"
-#include "ui/message_center/notification_list.h"
-#include "ui/message_center/public/cpp/notification.h"
-#include "ui/message_center/public/cpp/notification_types.h"
-
-using base::UTF8ToUTF16;
-using message_center::NotificationList;
-
-namespace ash {
-
-// Tests handle creating their own sessions.
-class SupervisedNotificationControllerTest : public NoSessionAshTestBase {
- public:
-  SupervisedNotificationControllerTest() = default;
-  ~SupervisedNotificationControllerTest() override = default;
-
- protected:
-  message_center::Notification* GetPopup();
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SupervisedNotificationControllerTest);
-};
-
-message_center::Notification* SupervisedNotificationControllerTest::GetPopup() {
-  NotificationList::PopupNotifications popups =
-      message_center::MessageCenter::Get()->GetPopupNotifications();
-  for (NotificationList::PopupNotifications::const_iterator iter =
-           popups.begin();
-       iter != popups.end(); ++iter) {
-    if ((*iter)->id() == SupervisedNotificationController::kNotificationId)
-      return *iter;
-  }
-  return NULL;
-}
-
-// Verifies that when a supervised user logs in that a warning notification is
-// shown and ash does not crash.
-TEST_F(SupervisedNotificationControllerTest, SupervisedUserHasNotification) {
-  SessionControllerImpl* session = Shell::Get()->session_controller();
-  ASSERT_EQ(LoginStatus::NOT_LOGGED_IN, session->login_status());
-  ASSERT_FALSE(session->IsActiveUserSessionStarted());
-
-  // Simulate a supervised user logging in.
-  TestSessionControllerClient* client = GetSessionControllerClient();
-  client->Reset();
-  client->AddUserSession("child@test.com", user_manager::USER_TYPE_SUPERVISED);
-  client->SetSessionState(session_manager::SessionState::ACTIVE);
-
-  // No notification because custodian email not available yet.
-  message_center::Notification* notification = GetPopup();
-  EXPECT_FALSE(notification);
-
-  const std::string custodian_email = "parent1@test.com";
-  const std::string custodian_email2 = "parent2@test.com";
-
-  // Update the user session with the custodian data (which happens after the
-  // profile loads).
-  UserSession user_session = *session->GetUserSession(0);
-  user_session.custodian_email = custodian_email;
-  session->UpdateUserSession(std::move(user_session));
-
-  // Notification is shown.
-  notification = GetPopup();
-  ASSERT_TRUE(notification);
-  EXPECT_EQ(static_cast<int>(message_center::SYSTEM_PRIORITY),
-            notification->rich_notification_data().priority);
-  EXPECT_NE(base::string16::npos,
-            notification->message().find(UTF8ToUTF16(custodian_email)));
-
-  // Update the user session with new custodian data.
-  user_session = *session->GetUserSession(0);
-  user_session.custodian_email = custodian_email2;
-  session->UpdateUserSession(std::move(user_session));
-
-  // Notification is shown with updated message.
-  notification = GetPopup();
-  ASSERT_TRUE(notification);
-  EXPECT_EQ(base::string16::npos,
-            notification->message().find(UTF8ToUTF16(custodian_email)));
-  EXPECT_NE(base::string16::npos,
-            notification->message().find(UTF8ToUTF16(custodian_email2)));
-}
-
-}  // namespace ash
diff --git a/ash/system/system_notification_controller.cc b/ash/system/system_notification_controller.cc
index fdad6dc7..ce8dc644 100644
--- a/ash/system/system_notification_controller.cc
+++ b/ash/system/system_notification_controller.cc
@@ -12,7 +12,6 @@
 #include "ash/system/power/power_notification_controller.h"
 #include "ash/system/screen_security/screen_security_notification_controller.h"
 #include "ash/system/session/session_limit_notification_controller.h"
-#include "ash/system/supervised/supervised_notification_controller.h"
 #include "ash/system/tracing_notification_controller.h"
 #include "ash/system/update/update_notification_controller.h"
 #include "ui/message_center/message_center.h"
@@ -30,7 +29,6 @@
       screen_security_(
           std::make_unique<ScreenSecurityNotificationController>()),
       session_limit_(std::make_unique<SessionLimitNotificationController>()),
-      supervised_(std::make_unique<SupervisedNotificationController>()),
       tracing_(std::make_unique<TracingNotificationController>()),
       update_(std::make_unique<UpdateNotificationController>()),
       wifi_toggle_(std::make_unique<WifiToggleNotificationController>()) {}
diff --git a/ash/system/system_notification_controller.h b/ash/system/system_notification_controller.h
index cb36528f..0c087b3 100644
--- a/ash/system/system_notification_controller.h
+++ b/ash/system/system_notification_controller.h
@@ -19,7 +19,6 @@
 class PowerNotificationController;
 class ScreenSecurityNotificationController;
 class SessionLimitNotificationController;
-class SupervisedNotificationController;
 class TracingNotificationController;
 class UpdateNotificationController;
 class WifiToggleNotificationController;
@@ -41,7 +40,6 @@
   const std::unique_ptr<PowerNotificationController> power_;
   const std::unique_ptr<ScreenSecurityNotificationController> screen_security_;
   const std::unique_ptr<SessionLimitNotificationController> session_limit_;
-  const std::unique_ptr<SupervisedNotificationController> supervised_;
   const std::unique_ptr<TracingNotificationController> tracing_;
   const std::unique_ptr<UpdateNotificationController> update_;
   const std::unique_ptr<WifiToggleNotificationController> wifi_toggle_;
diff --git a/ash/system/unified/unified_managed_device_view.cc b/ash/system/unified/unified_managed_device_view.cc
index 7d2602d8..6751a00 100644
--- a/ash/system/unified/unified_managed_device_view.cc
+++ b/ash/system/unified/unified_managed_device_view.cc
@@ -106,7 +106,7 @@
     label_->SetText(managed_string);
     SetAccessibleName(managed_string);
     SetVisible(true);
-  } else if (session->IsUserSupervised()) {
+  } else if (session->IsUserChildOrDeprecatedSupervised()) {
     // Show supervised user UI (locally supervised or Family Link).
     icon_->SetImage(gfx::CreateVectorIcon(GetSupervisedUserIcon(), icon_color));
     label_->SetText(GetSupervisedUserMessage());
diff --git a/ash/system/unified/unified_managed_device_view_unittest.cc b/ash/system/unified/unified_managed_device_view_unittest.cc
index 61c09a8..d6e3e48 100644
--- a/ash/system/unified/unified_managed_device_view_unittest.cc
+++ b/ash/system/unified/unified_managed_device_view_unittest.cc
@@ -74,7 +74,7 @@
 
 using UnifiedManagedDeviceViewNoSessionTest = NoSessionAshTestBase;
 
-TEST_F(UnifiedManagedDeviceViewNoSessionTest, SupervisedUserDevice) {
+TEST_F(UnifiedManagedDeviceViewNoSessionTest, ChildUserDevice) {
   SessionControllerImpl* session = Shell::Get()->session_controller();
   ASSERT_FALSE(session->IsActiveUserSessionStarted());
 
@@ -91,7 +91,7 @@
   // Simulate a supervised user logging in.
   TestSessionControllerClient* client = GetSessionControllerClient();
   client->Reset();
-  client->AddUserSession("child@test.com", user_manager::USER_TYPE_SUPERVISED);
+  client->AddUserSession("child@test.com", user_manager::USER_TYPE_CHILD);
   client->SetSessionState(session_manager::SessionState::ACTIVE);
   UserSession user_session = *session->GetUserSession(0);
   user_session.custodian_email = "parent@test.com";
diff --git a/ash/system/unified/unified_system_info_view.cc b/ash/system/unified/unified_system_info_view.cc
index d3fd01fb..00004f70 100644
--- a/ash/system/unified/unified_system_info_view.cc
+++ b/ash/system/unified/unified_system_info_view.cc
@@ -377,8 +377,10 @@
     : ManagedStateView(PressedCallback(),
                        IDS_ASH_STATUS_TRAY_SUPERVISED_LABEL,
                        GetSupervisedUserIcon()) {
-  SetVisible(Shell::Get()->session_controller()->IsUserSupervised());
-  if (Shell::Get()->session_controller()->IsUserSupervised())
+  bool visible =
+      Shell::Get()->session_controller()->IsUserChildOrDeprecatedSupervised();
+  SetVisible(visible);
+  if (visible)
     SetTooltipText(GetSupervisedUserMessage());
 
   // TODO(crbug/1026821) Add SupervisedUserView::ButtonPress() overload
diff --git a/ash/system/unified/unified_system_info_view.h b/ash/system/unified/unified_system_info_view.h
index 7e6a77d..f27262e6 100644
--- a/ash/system/unified/unified_system_info_view.h
+++ b/ash/system/unified/unified_system_info_view.h
@@ -33,8 +33,7 @@
   FRIEND_TEST_ALL_PREFIXES(UnifiedSystemInfoViewTest, EnterpriseManagedVisible);
   FRIEND_TEST_ALL_PREFIXES(UnifiedSystemInfoViewTest,
                            EnterpriseManagedVisibleForActiveDirectory);
-  FRIEND_TEST_ALL_PREFIXES(UnifiedSystemInfoViewNoSessionTest,
-                           SupervisedVisible);
+  FRIEND_TEST_ALL_PREFIXES(UnifiedSystemInfoViewNoSessionTest, ChildVisible);
 
   // EnterpriseManagedView for unit testing. Owned by this view. Null if
   // kManagedDeviceUIRedesign is enabled.
diff --git a/ash/system/unified/unified_system_info_view_unittest.cc b/ash/system/unified/unified_system_info_view_unittest.cc
index 13c564d..9b327a5 100644
--- a/ash/system/unified/unified_system_info_view_unittest.cc
+++ b/ash/system/unified/unified_system_info_view_unittest.cc
@@ -83,7 +83,7 @@
 
 using UnifiedSystemInfoViewNoSessionTest = NoSessionAshTestBase;
 
-TEST_F(UnifiedSystemInfoViewNoSessionTest, SupervisedVisible) {
+TEST_F(UnifiedSystemInfoViewNoSessionTest, ChildVisible) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndDisableFeature(features::kManagedDeviceUIRedesign);
   std::unique_ptr<UnifiedSystemTrayModel> model_ =
@@ -103,7 +103,7 @@
   // Simulate a supervised user logging in.
   TestSessionControllerClient* client = GetSessionControllerClient();
   client->Reset();
-  client->AddUserSession("child@test.com", user_manager::USER_TYPE_SUPERVISED);
+  client->AddUserSession("child@test.com", user_manager::USER_TYPE_CHILD);
   client->SetSessionState(session_manager::SessionState::ACTIVE);
   UserSession user_session = *session->GetUserSession(0);
   user_session.custodian_email = "parent@test.com";
diff --git a/ash/utility/layer_util.cc b/ash/utility/layer_util.cc
index 45c2946..e4d30ad 100644
--- a/ash/utility/layer_util.cc
+++ b/ash/utility/layer_util.cc
@@ -65,6 +65,7 @@
 std::unique_ptr<ui::Layer> CreateLayerFromCopyOutputResult(
     std::unique_ptr<viz::CopyOutputResult> copy_result) {
   auto copy_layer = std::make_unique<ui::Layer>();
+  copy_layer->SetBounds(gfx::Rect(copy_result->size()));
   CopyCopyOutputResultToLayer(std::move(copy_result), copy_layer.get());
   return copy_layer;
 }
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc
index 9b3c691..4ff6600 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -1350,7 +1350,7 @@
   // everything resets.
   user_manager::UserType active_user_type = active_user_session->user_info.type;
   return active_user_type == user_manager::USER_TYPE_REGULAR ||
-         active_user_type == user_manager::USER_TYPE_SUPERVISED ||
+         active_user_type == user_manager::USER_TYPE_SUPERVISED_DEPRECATED ||
          active_user_type == user_manager::USER_TYPE_CHILD;
 }
 
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index d9f2eb8..b33dd81 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -496,7 +496,6 @@
     if (source == DesksMoveWindowFromActiveDeskSource::kDragAndDrop) {
       // Since a visible on all desks window is on all desks, prevent users from
       // moving them manually in overview.
-      // TODO(chinsenj): Add a UX indication for users.
       return false;
     } else if (source == DesksMoveWindowFromActiveDeskSource::kShortcut) {
       window->SetProperty(aura::client::kVisibleOnAllWorkspacesKey, false);
@@ -551,7 +550,7 @@
   DCHECK(added);
 }
 
-void DesksController::RemoveVisibleOnAllDesksWindow(aura::Window* window) {
+void DesksController::MaybeRemoveVisibleOnAllDesksWindow(aura::Window* window) {
   visible_on_all_desks_windows_.erase(window);
 }
 
diff --git a/ash/wm/desks/desks_controller.h b/ash/wm/desks/desks_controller.h
index 1ac014c8..ca36e92 100644
--- a/ash/wm/desks/desks_controller.h
+++ b/ash/wm/desks/desks_controller.h
@@ -165,7 +165,7 @@
   void AddVisibleOnAllDesksWindow(aura::Window* window);
 
   // Removes |window| if it is in |visible_on_all_desks_windows_|.
-  void RemoveVisibleOnAllDesksWindow(aura::Window* window);
+  void MaybeRemoveVisibleOnAllDesksWindow(aura::Window* window);
 
   // Reverts the name of the given |desk| to the default value (i.e. "Desk 1",
   // "Desk 2", ... etc.) according to its position in the |desks_| list, as if
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index db25e162..4afef72 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -4064,6 +4064,30 @@
   EXPECT_TRUE(base::Contains(desk_2->windows(), window.get()));
 }
 
+// Tests that when a window that is visible on all desks is destroyed it is
+// removed from DesksController.visible_on_all_desks_windows_.
+TEST_F(DesksBentoTest, VisibleOnAllDesksWindowDestruction) {
+  auto* controller = DesksController::Get();
+  NewDesk();
+  const Desk* desk_1 = controller->desks()[0].get();
+  auto* root = Shell::GetPrimaryRootWindow();
+
+  auto window = CreateAppWindow(gfx::Rect(0, 0, 100, 100));
+  auto* widget = views::Widget::GetWidgetForNativeWindow(window.get());
+
+  // Assign |window| to all desks.
+  widget->SetVisibleOnAllWorkspaces(true);
+  ASSERT_TRUE(window->GetProperty(aura::client::kVisibleOnAllWorkspacesKey));
+  EXPECT_EQ(1u, controller->visible_on_all_desks_windows().size());
+  EXPECT_EQ(1u, desk_1->GetDeskContainerForRoot(root)->children().size());
+
+  // Destroy |window|. It should be removed from
+  // DesksController.visible_on_all_desks_windows_.
+  window.reset();
+  EXPECT_EQ(0u, controller->visible_on_all_desks_windows().size());
+  EXPECT_EQ(0u, desk_1->GetDeskContainerForRoot(root)->children().size());
+}
+
 // TODO(afakhry): Add more tests:
 // - Always on top windows are not tracked by any desk.
 // - Reusing containers when desks are removed and created.
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index 573d4c2..701ed24 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -22,6 +22,8 @@
 #include "ash/rotator/screen_rotation_animator.h"
 #include "ash/screen_util.h"
 #include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/system/toast/toast_manager_impl.h"
 #include "ash/wallpaper/wallpaper_controller_impl.h"
 #include "ash/wm/desks/desk_mini_view.h"
 #include "ash/wm/desks/desk_name_view.h"
@@ -56,6 +58,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/throughput_tracker.h"
 #include "ui/gfx/geometry/vector2d_f.h"
@@ -90,6 +93,12 @@
 constexpr base::TimeDelta kOcclusionUnpauseDurationForRotation =
     base::TimeDelta::FromMilliseconds(300);
 
+// Toast id for the toast that is displayed when a user tries to move a window
+// that is visible on all desks to another desk.
+constexpr char kMoveVisibleOnAllDesksWindowToastId[] =
+    "ash.wm.overview.move_visible_on_all_desks_window_toast";
+constexpr int kToastDurationMs = 2500;
+
 // Histogram names for overview enter/exit smoothness in clamshell,
 // tablet mode and splitview.
 constexpr char kOverviewEnterClamshellHistogram[] =
@@ -1358,19 +1367,33 @@
     OverviewItem* drag_item) {
   DCHECK(desks_util::ShouldDesksBarBeCreated());
 
+  aura::Window* const dragged_window = drag_item->GetWindow();
+  const bool dragged_window_is_visible_on_all_desks =
+      dragged_window &&
+      dragged_window->GetProperty(aura::client::kVisibleOnAllWorkspacesKey);
   // End the drag for the DesksBarView.
   if (!IntersectsWithDesksBar(screen_location,
-                              /*update_desks_bar_drag_details=*/true,
+                              /*update_desks_bar_drag_details=*/
+                              !dragged_window_is_visible_on_all_desks,
                               /*for_drop=*/true)) {
     return false;
   }
 
+  if (dragged_window_is_visible_on_all_desks) {
+    // Show toast since items that are visible on all desks should not be able
+    // to be unassigned during overview.
+    Shell::Get()->toast_manager()->Show(ToastData(
+        kMoveVisibleOnAllDesksWindowToastId,
+        l10n_util::GetStringUTF16(IDS_ASH_OVERVIEW_VISIBLE_ON_ALL_DESKS_TOAST),
+        kToastDurationMs, base::nullopt));
+    return false;
+  }
+
   auto* desks_controller = DesksController::Get();
   for (auto* mini_view : desks_bar_view_->mini_views()) {
     if (!mini_view->IsPointOnMiniView(screen_location))
       continue;
 
-    aura::Window* const dragged_window = drag_item->GetWindow();
     Desk* const target_desk = mini_view->desk();
     if (target_desk == desks_controller->active_desk())
       return false;
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc
index e942991..818961a5 100644
--- a/ash/wm/overview/overview_window_drag_controller.cc
+++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -31,6 +31,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/numerics/ranges.h"
 #include "base/numerics/safe_conversions.h"
+#include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
 #include "ui/display/display.h"
@@ -79,6 +80,13 @@
          item->overview_grid()->IsDesksBarViewActive();
 }
 
+// Returns whether |item|'s window is visible on all desks.
+bool DraggedItemIsVisibleOnAllDesks(OverviewItem* item) {
+  aura::Window* const dragged_window = item->GetWindow();
+  return dragged_window &&
+         dragged_window->GetProperty(aura::client::kVisibleOnAllWorkspacesKey);
+}
+
 // Returns the scaled-down size of the dragged item that should be used when
 // it's dragged over the DesksBarView that belongs to |overview_grid|.
 // |window_original_size| is the size of the item's window before it was scaled
@@ -520,10 +528,12 @@
 
     if (desks_bar_data.shrink_bounds.Contains(location_in_screen)) {
       // Update the mini views borders by checking if |location_in_screen|
-      // intersects.
+      // intersects. Only update the borders if the dragged item is not visible
+      // on all desks.
       overview_grid->IntersectsWithDesksBar(
           gfx::ToRoundedPoint(location_in_screen),
-          /*update_desks_bar_drag_details=*/true, /*for_drop=*/false);
+          /*update_desks_bar_drag_details=*/
+          !DraggedItemIsVisibleOnAllDesks(item_), /*for_drop=*/false);
 
       float value = 0.f;
       if (centerpoint.y() < desks_bar_data.desks_bar_bounds.y() ||
diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc
index a9725b6..3d1b6041 100644
--- a/ash/wm/workspace/workspace_layout_manager.cc
+++ b/ash/wm/workspace/workspace_layout_manager.cc
@@ -329,7 +329,7 @@
     if (window->GetProperty(aura::client::kVisibleOnAllWorkspacesKey))
       desks_controller->AddVisibleOnAllDesksWindow(window);
     else
-      desks_controller->RemoveVisibleOnAllDesksWindow(window);
+      desks_controller->MaybeRemoveVisibleOnAllDesksWindow(window);
   }
 }
 
@@ -348,6 +348,7 @@
     settings_bubble_container_ = nullptr;
   if (accessibility_bubble_container_ == window)
     accessibility_bubble_container_ = nullptr;
+  Shell::Get()->desks_controller()->MaybeRemoveVisibleOnAllDesksWindow(window);
 }
 
 void WorkspaceLayoutManager::OnWindowBoundsChanged(
diff --git a/base/allocator/partition_allocator/partition_ref_count.h b/base/allocator/partition_allocator/partition_ref_count.h
index 310a9d6..ef7ad7e 100644
--- a/base/allocator/partition_allocator/partition_ref_count.h
+++ b/base/allocator/partition_allocator/partition_ref_count.h
@@ -85,6 +85,16 @@
   }
 
  private:
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-private-field"
+#endif
+  void* padding_;  // TODO(crbug.com/1164636): This "workaround" is meant to
+                   // reduce the number of freelist corruption crashes we see in
+                   // experiments. Remove once root cause has been found.
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
   std::atomic<int32_t> count_{1};
 };
 
diff --git a/base/android/java/src/org/chromium/base/BundleUtils.java b/base/android/java/src/org/chromium/base/BundleUtils.java
index 00fc6c5..c41b70b 100644
--- a/base/android/java/src/org/chromium/base/BundleUtils.java
+++ b/base/android/java/src/org/chromium/base/BundleUtils.java
@@ -5,17 +5,22 @@
 package org.chromium.base;
 
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.Build;
 
 import androidx.annotation.Nullable;
+import androidx.collection.SimpleArrayMap;
 
 import dalvik.system.BaseDexClassLoader;
+import dalvik.system.PathClassLoader;
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.compat.ApiHelperForO;
+import org.chromium.base.metrics.RecordHistogram;
 
+import java.lang.reflect.Field;
 import java.util.Arrays;
 
 /**
@@ -38,6 +43,11 @@
 public final class BundleUtils {
     private static Boolean sIsBundle;
 
+    // This cache is needed to support the workaround for b/172602571, see
+    // createIsolatedSplitContext() for more info.
+    private static final SimpleArrayMap<String, ClassLoader> sCachedClassLoaders =
+            new SimpleArrayMap<>();
+
     /**
      * {@link BundleUtils#isBundle()}  is not called directly by native because
      * {@link CalledByNative} prevents inlining, causing the bundle support lib to not be
@@ -98,12 +108,56 @@
         }
 
         try {
-            return ApiHelperForO.createContextForSplit(base, splitName);
+            Context context = ApiHelperForO.createContextForSplit(base, splitName);
+            ClassLoader parent = context.getClassLoader().getParent();
+            Context appContext = ContextUtils.getApplicationContext();
+            // If the ClassLoader from the newly created context does not equal either the
+            // BundleUtils ClassLoader (the base module ClassLoader) or the app context ClassLoader
+            // (the chrome module ClassLoader) there must be something messed up in the ClassLoader
+            // cache, see b/172602571. This should be solved for the chrome ClassLoader by
+            // SplitCompatAppComponentFactory, but modules which depend on the chrome module need
+            // special handling here to make sure they have the correct parent.
+            boolean shouldReplaceClassLoader = isolatedSplitsEnabled()
+                    && !parent.equals(BundleUtils.class.getClassLoader()) && appContext != null
+                    && !parent.equals(appContext.getClassLoader());
+            if (shouldReplaceClassLoader) {
+                if (!sCachedClassLoaders.containsKey(splitName)) {
+                    String[] splitNames = ApiHelperForO.getSplitNames(context.getApplicationInfo());
+                    int idx = Arrays.binarySearch(splitNames, splitName);
+                    assert idx >= 0;
+                    // The librarySearchPath argument to PathClassLoader is not needed here because
+                    // the framework doesn't pass it either, see b/171269960.
+                    sCachedClassLoaders.put(splitName,
+                            new PathClassLoader(context.getApplicationInfo().splitSourceDirs[idx],
+                                    appContext.getClassLoader()));
+                }
+                replaceClassLoader(context, sCachedClassLoaders.get(splitName));
+            }
+            RecordHistogram.recordBooleanHistogram(
+                    "Android.IsolatedSplits.ClassLoaderReplaced." + splitName,
+                    shouldReplaceClassLoader);
+            return context;
         } catch (PackageManager.NameNotFoundException e) {
             throw new RuntimeException(e);
         }
     }
 
+    /** Replaces the ClassLoader of the passed in Context. */
+    public static void replaceClassLoader(Context baseContext, ClassLoader classLoader) {
+        while (baseContext instanceof ContextWrapper) {
+            baseContext = ((ContextWrapper) baseContext).getBaseContext();
+        }
+
+        try {
+            // baseContext should now be an instance of ContextImpl.
+            Field classLoaderField = baseContext.getClass().getDeclaredField("mClassLoader");
+            classLoaderField.setAccessible(true);
+            classLoaderField.set(baseContext, classLoader);
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException("Error setting ClassLoader.", e);
+        }
+    }
+
     /* Returns absolute path to a native library in a feature module. */
     @CalledByNative
     @Nullable
diff --git a/base/metrics/field_trial_param_associator.cc b/base/metrics/field_trial_param_associator.cc
index 3dbc5516..ab3bcf4 100644
--- a/base/metrics/field_trial_param_associator.cc
+++ b/base/metrics/field_trial_param_associator.cc
@@ -60,10 +60,11 @@
   AutoLock scoped_lock(lock_);
 
   const FieldTrialRefKey key(trial_name, group_name);
-  if (!Contains(field_trial_params_, key))
+  auto it = field_trial_params_.find(key);
+  if (it == field_trial_params_.end())
     return false;
 
-  *params = field_trial_params_[key];
+  *params = it->second;
   return true;
 }
 
diff --git a/base/metrics/field_trial_params.cc b/base/metrics/field_trial_params.cc
index 608d5d0..71b7248 100644
--- a/base/metrics/field_trial_params.cc
+++ b/base/metrics/field_trial_params.cc
@@ -114,14 +114,13 @@
 
 std::string GetFieldTrialParamValueByFeature(const Feature& feature,
                                              const std::string& param_name) {
-  if (!FeatureList::IsEnabled(feature))
-    return std::string();
-
-  FieldTrial* trial = FeatureList::GetFieldTrial(feature);
-  if (!trial)
-    return std::string();
-
-  return GetFieldTrialParamValue(trial->trial_name(), param_name);
+  FieldTrialParams params;
+  if (GetFieldTrialParamsByFeature(feature, &params)) {
+    auto it = params.find(param_name);
+    if (it != params.end())
+      return it->second;
+  }
+  return std::string();
 }
 
 int GetFieldTrialParamByFeatureAsInt(const Feature& feature,
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index ff933a7..53a0cf3 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -658,10 +658,9 @@
 }
 
 bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
-  // Synchronously reading files in /proc is safe.
-  ThreadRestrictions::ScopedAllowIO allow_io;
-
   // Used memory is: total - free - buffers - caches
+  // ReadFileToStringNonBlocking doesn't require ScopedAllowIO, and reading
+  // /proc/meminfo is fast. See crbug.com/1160988 for details.
   FilePath meminfo_file("/proc/meminfo");
   std::string meminfo_data;
   if (!ReadFileToStringNonBlocking(meminfo_file, &meminfo_data)) {
diff --git a/base/profiler/module_cache.cc b/base/profiler/module_cache.cc
index b36ba46..bfe878a 100644
--- a/base/profiler/module_cache.cc
+++ b/base/profiler/module_cache.cc
@@ -31,15 +31,21 @@
 }  // namespace
 
 ModuleCache::ModuleCache() = default;
-ModuleCache::~ModuleCache() = default;
+
+ModuleCache::~ModuleCache() {
+  DCHECK_EQ(auxiliary_module_provider_, nullptr);
+}
 
 const ModuleCache::Module* ModuleCache::GetModuleForAddress(uintptr_t address) {
   if (const ModuleCache::Module* module = GetExistingModuleForAddress(address))
     return module;
 
   std::unique_ptr<const Module> new_module = CreateModuleForAddress(address);
+  if (!new_module && auxiliary_module_provider_)
+    new_module = auxiliary_module_provider_->TryCreateModuleForAddress(address);
   if (!new_module)
     return nullptr;
+
   const auto result = native_modules_.insert(std::move(new_module));
   // TODO(https://crbug.com/1131769): Reintroduce DCHECK(result.second) after
   // fixing the issue that is causing it to fail.
@@ -126,6 +132,18 @@
   return nullptr;
 }
 
+void ModuleCache::RegisterAuxiliaryModuleProvider(
+    AuxiliaryModuleProvider* auxiliary_module_provider) {
+  DCHECK(!auxiliary_module_provider_);
+  auxiliary_module_provider_ = auxiliary_module_provider;
+}
+
+void ModuleCache::UnregisterAuxiliaryModuleProvider(
+    AuxiliaryModuleProvider* auxiliary_module_provider) {
+  DCHECK_EQ(auxiliary_module_provider_, auxiliary_module_provider);
+  auxiliary_module_provider_ = nullptr;
+}
+
 bool ModuleCache::ModuleAndAddressCompare::operator()(
     const std::unique_ptr<const Module>& m1,
     const std::unique_ptr<const Module>& m2) const {
diff --git a/base/profiler/module_cache.h b/base/profiler/module_cache.h
index fde60e7f..71f7d78 100644
--- a/base/profiler/module_cache.h
+++ b/base/profiler/module_cache.h
@@ -66,6 +66,21 @@
     virtual bool IsNative() const = 0;
   };
 
+  // Interface for lazily creating a native module for a given |address|. The
+  // provider is registered with RegisterAuxiliaryModuleProvider().
+  class AuxiliaryModuleProvider {
+   public:
+    AuxiliaryModuleProvider() = default;
+    AuxiliaryModuleProvider(const AuxiliaryModuleProvider&) = delete;
+    AuxiliaryModuleProvider& operator=(const AuxiliaryModuleProvider&) = delete;
+
+    virtual std::unique_ptr<const Module> TryCreateModuleForAddress(
+        uintptr_t address) = 0;
+
+   protected:
+    ~AuxiliaryModuleProvider() = default;
+  };
+
   ModuleCache();
   ~ModuleCache();
 
@@ -103,6 +118,19 @@
   // ModuleCache.
   void AddCustomNativeModule(std::unique_ptr<const Module> module);
 
+  // Registers a custom module provider for lazily creating native modules. At
+  // most one provider can be registered at any time, and the provider must be
+  // unregistered before being destroyed. This is intended to support native
+  // modules that require custom handling. In general, native modules will be
+  // found and added automatically when invoking GetModuleForAddress(). If no
+  // module is found, this provider will be used as fallback.
+  void RegisterAuxiliaryModuleProvider(
+      AuxiliaryModuleProvider* auxiliary_module_provider);
+
+  // Unregisters the custom module provider.
+  void UnregisterAuxiliaryModuleProvider(
+      AuxiliaryModuleProvider* auxiliary_module_provider);
+
   // Gets the module containing |address| if one already exists, or nullptr
   // otherwise. The returned module remains owned by and has the same lifetime
   // as the ModuleCache object.
@@ -156,6 +184,9 @@
   // because it can contain multiple modules that were loaded (then subsequently
   // unloaded) at the same base address.
   std::vector<std::unique_ptr<const Module>> inactive_non_native_modules_;
+
+  // Auxiliary module provider, for lazily creating native modules.
+  AuxiliaryModuleProvider* auxiliary_module_provider_ = nullptr;
 };
 
 }  // namespace base
diff --git a/base/profiler/module_cache_unittest.cc b/base/profiler/module_cache_unittest.cc
index 57986440..cefae68c 100644
--- a/base/profiler/module_cache_unittest.cc
+++ b/base/profiler/module_cache_unittest.cc
@@ -411,5 +411,68 @@
 }
 #endif
 
+// Module provider that always return a fake module of size 1 for a given
+// |address|.
+class MockModuleProvider : public ModuleCache::AuxiliaryModuleProvider {
+ public:
+  explicit MockModuleProvider(size_t module_size = 1)
+      : module_size_(module_size) {}
+
+  std::unique_ptr<const ModuleCache::Module> TryCreateModuleForAddress(
+      uintptr_t address) override {
+    return std::make_unique<FakeModule>(address, module_size_);
+  }
+
+ private:
+  size_t module_size_;
+};
+
+// Check that auxiliary provider can inject new modules when registered.
+TEST(ModuleCacheTest, RegisterAuxiliaryModuleProvider) {
+  ModuleCache cache;
+  EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
+
+  MockModuleProvider auxiliary_provider;
+  cache.RegisterAuxiliaryModuleProvider(&auxiliary_provider);
+  auto* module = cache.GetModuleForAddress(1);
+  EXPECT_NE(nullptr, module);
+  EXPECT_EQ(1U, module->GetBaseAddress());
+  cache.UnregisterAuxiliaryModuleProvider(&auxiliary_provider);
+
+  // Even when unregistered, the module remains in the cache.
+  EXPECT_EQ(module, cache.GetModuleForAddress(1));
+}
+
+// Check that ModuleCache's own module creator is used preferentially over
+// auxiliary provider if possible.
+MAYBE_TEST(ModuleCacheTest, NativeModuleOverAuxiliaryModuleProvider) {
+  ModuleCache cache;
+
+  MockModuleProvider auxiliary_provider(/*module_size=*/100);
+  cache.RegisterAuxiliaryModuleProvider(&auxiliary_provider);
+
+  const ModuleCache::Module* module =
+      cache.GetModuleForAddress(reinterpret_cast<uintptr_t>(&AFunctionForTest));
+  ASSERT_NE(nullptr, module);
+
+  // The module should be a native module, which will have size greater than 100
+  // bytes.
+  EXPECT_NE(100u, module->GetSize());
+  cache.UnregisterAuxiliaryModuleProvider(&auxiliary_provider);
+}
+
+// Check that auxiliary provider is no longer used after being unregistered.
+TEST(ModuleCacheTest, UnregisterAuxiliaryModuleProvider) {
+  ModuleCache cache;
+
+  EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
+
+  MockModuleProvider auxiliary_provider;
+  cache.RegisterAuxiliaryModuleProvider(&auxiliary_provider);
+  cache.UnregisterAuxiliaryModuleProvider(&auxiliary_provider);
+
+  EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
+}
+
 }  // namespace
 }  // namespace base
diff --git a/base/profiler/native_unwinder_android.cc b/base/profiler/native_unwinder_android.cc
index efba835..c5f12af 100644
--- a/base/profiler/native_unwinder_android.cc
+++ b/base/profiler/native_unwinder_android.cc
@@ -127,10 +127,14 @@
       process_memory_(process_memory),
       exclude_module_with_base_address_(exclude_module_with_base_address) {}
 
-NativeUnwinderAndroid::~NativeUnwinderAndroid() = default;
+NativeUnwinderAndroid::~NativeUnwinderAndroid() {
+  if (module_cache_)
+    module_cache_->UnregisterAuxiliaryModuleProvider(this);
+}
 
-void NativeUnwinderAndroid::AddInitialModules(ModuleCache* module_cache) {
-  AddInitialModulesFromMaps(*memory_regions_map_, module_cache);
+void NativeUnwinderAndroid::InitializeModules(ModuleCache* module_cache) {
+  module_cache_ = module_cache;
+  module_cache_->RegisterAuxiliaryModuleProvider(this);
 }
 
 bool NativeUnwinderAndroid::CanUnwindFrom(const Frame& current_frame) const {
@@ -215,34 +219,14 @@
   return UnwindResult::UNRECOGNIZED_FRAME;
 }
 
-// static
-void NativeUnwinderAndroid::AddInitialModulesFromMaps(
-    const unwindstack::Maps& memory_regions_map,
-    ModuleCache* module_cache) {
-  // The effect of this loop is to create modules for the executable regions in
-  // the memory map. Regions composing a mapped ELF file are handled specially
-  // however: for just one module extending from the ELF base address to the
-  // *last* executable region backed by the file is implicitly created by
-  // ModuleCache. This avoids duplicate module instances covering the same
-  // in-memory module in the case that a module has multiple mmapped executable
-  // regions.
-  for (const std::unique_ptr<unwindstack::MapInfo>& region :
-       memory_regions_map) {
-    if (!(region->flags & PROT_EXEC))
-      continue;
-
-    // Use the standard ModuleCache POSIX module representation for ELF files.
-    // This call returns the containing ELF module for the region, creating it
-    // if it doesn't exist.
-    if (module_cache->GetModuleForAddress(
-            static_cast<uintptr_t>(region->start))) {
-      continue;
-    }
-
-    // Non-ELF modules are represented with NonElfModule.
-    module_cache->AddCustomNativeModule(
-        std::make_unique<NonElfModule>(region.get()));
+std::unique_ptr<const ModuleCache::Module>
+NativeUnwinderAndroid::TryCreateModuleForAddress(uintptr_t address) {
+  unwindstack::MapInfo* map_info = memory_regions_map_->Find(address);
+  if (map_info == nullptr || !(map_info->flags & PROT_EXEC) ||
+      map_info->flags & unwindstack::MAPS_FLAGS_DEVICE_MAP) {
+    return nullptr;
   }
+  return std::make_unique<NonElfModule>(map_info);
 }
 
 void NativeUnwinderAndroid::EmitDexFrame(uintptr_t dex_pc,
diff --git a/base/profiler/native_unwinder_android.h b/base/profiler/native_unwinder_android.h
index 62774e6..30aa101 100644
--- a/base/profiler/native_unwinder_android.h
+++ b/base/profiler/native_unwinder_android.h
@@ -27,7 +27,8 @@
 };
 
 // Native unwinder implementation for Android, using libunwindstack.
-class NativeUnwinderAndroid : public Unwinder {
+class NativeUnwinderAndroid : public Unwinder,
+                              public ModuleCache::AuxiliaryModuleProvider {
  public:
   // Creates maps object from /proc/self/maps for use by NativeUnwinderAndroid.
   // Since this is an expensive call, the maps object should be re-used across
@@ -35,8 +36,9 @@
   static std::unique_ptr<unwindstack::Maps> CreateMaps();
   static std::unique_ptr<unwindstack::Memory> CreateProcessMemory();
 
-  // |exclude_module_with_base_address| is used to exclude a specific module
-  // and let another unwinder take control. TryUnwind() will exit with
+  // |memory_regions_map| and |process_memory| must outlive this unwinder.
+  // |exclude_module_with_base_address| is used to exclude a specific module and
+  // let another unwinder take control. TryUnwind() will exit with
   // UNRECOGNIZED_FRAME and CanUnwindFrom() will return false when a frame is
   // encountered in that module.
   NativeUnwinderAndroid(unwindstack::Maps* memory_regions_map,
@@ -48,24 +50,25 @@
   NativeUnwinderAndroid& operator=(const NativeUnwinderAndroid&) = delete;
 
   // Unwinder
-  void AddInitialModules(ModuleCache* module_cache) override;
+  void InitializeModules(ModuleCache* module_cache) override;
   bool CanUnwindFrom(const Frame& current_frame) const override;
   UnwindResult TryUnwind(RegisterContext* thread_context,
                          uintptr_t stack_top,
                          ModuleCache* module_cache,
                          std::vector<Frame>* stack) const override;
 
-  // Adds modules found from executable loaded memory regions to |module_cache|.
-  // Public for test access.
-  static void AddInitialModulesFromMaps(
-      const unwindstack::Maps& memory_regions_map,
-      ModuleCache* module_cache);
+  // ModuleCache::AuxiliaryModuleProvider
+  std::unique_ptr<const ModuleCache::Module> TryCreateModuleForAddress(
+      uintptr_t address) override;
 
  private:
   void EmitDexFrame(uintptr_t dex_pc,
                     ModuleCache* module_cache,
                     std::vector<Frame>* stack) const;
 
+  // InitializeModules() registers self as an AuxiliaryModuleProvider. A pointer
+  // to the ModuleCache is saved to unregister self in destructor.
+  ModuleCache* module_cache_ = nullptr;
   unwindstack::Maps* const memory_regions_map_;
   unwindstack::Memory* const process_memory_;
   const uintptr_t exclude_module_with_base_address_;
diff --git a/base/profiler/native_unwinder_android_unittest.cc b/base/profiler/native_unwinder_android_unittest.cc
index 5972c77..b3e6db23 100644
--- a/base/profiler/native_unwinder_android_unittest.cc
+++ b/base/profiler/native_unwinder_android_unittest.cc
@@ -34,11 +34,6 @@
 
 namespace {
 
-bool CompareModulesByBaseAddress(const ModuleCache::Module* a,
-                                 const ModuleCache::Module* b) {
-  return a->GetBaseAddress() < b->GetBaseAddress();
-}
-
 // Add a MapInfo with the provided values to |maps|.
 void AddMapInfo(uint64_t start,
                 uint64_t end,
@@ -114,11 +109,12 @@
   std::unique_ptr<unwindstack::Maps> maps = NativeUnwinderAndroid::CreateMaps();
   std::unique_ptr<unwindstack::Memory> memory =
       NativeUnwinderAndroid::CreateProcessMemory();
+
+  ModuleCache module_cache;
   auto unwinder =
       std::make_unique<NativeUnwinderAndroid>(maps.get(), memory.get(), 0);
 
-  ModuleCache module_cache;
-  unwinder->AddInitialModules(&module_cache);
+  unwinder->InitializeModules(&module_cache);
   std::vector<Frame> sample =
       CaptureScenario(&scenario, &module_cache,
                       BindLambdaForTesting([&](RegisterContext* thread_context,
@@ -154,11 +150,12 @@
   std::unique_ptr<unwindstack::Maps> maps = NativeUnwinderAndroid::CreateMaps();
   std::unique_ptr<unwindstack::Memory> memory =
       NativeUnwinderAndroid::CreateProcessMemory();
+
+  ModuleCache module_cache;
   auto unwinder =
       std::make_unique<NativeUnwinderAndroid>(maps.get(), memory.get(), 0);
 
-  ModuleCache module_cache;
-  unwinder->AddInitialModules(&module_cache);
+  unwinder->InitializeModules(&module_cache);
   std::vector<Frame> sample =
       CaptureScenario(&scenario, &module_cache,
                       BindLambdaForTesting([&](RegisterContext* thread_context,
@@ -196,11 +193,12 @@
   std::unique_ptr<unwindstack::Maps> maps = NativeUnwinderAndroid::CreateMaps();
   std::unique_ptr<unwindstack::Memory> memory =
       NativeUnwinderAndroid::CreateProcessMemory();
+
+  ModuleCache module_cache;
   auto unwinder =
       std::make_unique<NativeUnwinderAndroid>(maps.get(), memory.get(), 0);
 
-  ModuleCache module_cache;
-  unwinder->AddInitialModules(&module_cache);
+  unwinder->InitializeModules(&module_cache);
   std::vector<Frame> sample =
       CaptureScenario(&scenario, &module_cache,
                       BindLambdaForTesting([&](RegisterContext* thread_context,
@@ -228,12 +226,13 @@
   std::unique_ptr<unwindstack::Memory> memory =
       NativeUnwinderAndroid::CreateProcessMemory();
   ModuleCache module_cache;
-  NativeUnwinderAndroid::AddInitialModulesFromMaps(*maps, &module_cache);
-
+  unwindstack::MapInfo* other_library_map =
+      maps->Find(GetAddressInOtherLibrary(other_library));
+  ASSERT_NE(nullptr, other_library_map);
   auto unwinder = std::make_unique<NativeUnwinderAndroid>(
-      maps.get(), memory.get(),
-      module_cache.GetModuleForAddress(GetAddressInOtherLibrary(other_library))
-          ->GetBaseAddress());
+      maps.get(), memory.get(), other_library_map->start);
+  unwinder->InitializeModules(&module_cache);
+
   std::vector<Frame> sample =
       CaptureScenario(&scenario, &module_cache,
                       BindLambdaForTesting([&](RegisterContext* thread_context,
@@ -266,34 +265,44 @@
   std::unique_ptr<unwindstack::Maps> maps = NativeUnwinderAndroid::CreateMaps();
   std::unique_ptr<unwindstack::Memory> memory =
       NativeUnwinderAndroid::CreateProcessMemory();
-  ModuleCache module_cache;
-  NativeUnwinderAndroid::AddInitialModulesFromMaps(*maps, &module_cache);
 
-  // Several unwinders are used to unwind different portion of the stack. This
-  // tests that NativeUnwinderAndroid can pick up from a state in the middle of
-  // the stack. This emulates having NativeUnwinderAndroid work with other
-  // unwinders, but doesn't reproduce what happens in production.
+  // Several unwinders are used to unwind different portion of the stack. Since
+  // only 1 unwinder can be registered as a module provider, each unwinder uses
+  // a distinct ModuleCache. This tests that NativeUnwinderAndroid can pick up
+  // from a state in the middle of the stack. This emulates having
+  // NativeUnwinderAndroid work with other unwinders, but doesn't reproduce what
+  // happens in production.
+  ModuleCache module_cache_for_all;
   auto unwinder_for_all =
       std::make_unique<NativeUnwinderAndroid>(maps.get(), memory.get(), 0);
+  unwinder_for_all->InitializeModules(&module_cache_for_all);
+
+  ModuleCache module_cache_for_native;
   auto unwinder_for_native = std::make_unique<NativeUnwinderAndroid>(
       maps.get(), memory.get(),
       reinterpret_cast<uintptr_t>(&__executable_start));
+  unwinder_for_native->InitializeModules(&module_cache_for_native);
+
+  ModuleCache module_cache_for_chrome;
+  unwindstack::MapInfo* other_library_map =
+      maps->Find(GetAddressInOtherLibrary(other_library));
+  ASSERT_NE(nullptr, other_library_map);
   auto unwinder_for_chrome = std::make_unique<NativeUnwinderAndroid>(
-      maps.get(), memory.get(),
-      module_cache.GetModuleForAddress(GetAddressInOtherLibrary(other_library))
-          ->GetBaseAddress());
+      maps.get(), memory.get(), other_library_map->start);
+  unwinder_for_chrome->InitializeModules(&module_cache_for_chrome);
 
   std::vector<Frame> sample = CaptureScenario(
-      &scenario, &module_cache,
+      &scenario, &module_cache_for_native,
       BindLambdaForTesting([&](RegisterContext* thread_context,
                                uintptr_t stack_top,
                                std::vector<Frame>* sample) {
         // |unwinder_for_native| unwinds through native frames, but stops at
         // chrome frames. It might not contain SampleAddressRange.
         ASSERT_TRUE(unwinder_for_native->CanUnwindFrom(sample->back()));
-        EXPECT_EQ(UnwindResult::UNRECOGNIZED_FRAME,
-                  unwinder_for_native->TryUnwind(thread_context, stack_top,
-                                                 &module_cache, sample));
+        EXPECT_EQ(
+            UnwindResult::UNRECOGNIZED_FRAME,
+            unwinder_for_native->TryUnwind(thread_context, stack_top,
+                                           &module_cache_for_native, sample));
         EXPECT_FALSE(unwinder_for_native->CanUnwindFrom(sample->back()));
 
         ExpectStackDoesNotContain(*sample,
@@ -304,9 +313,10 @@
         // |unwinder_for_chrome| unwinds through Chrome frames, but stops at
         // |other_library|. It won't contain SetupFunctionAddressRange.
         ASSERT_TRUE(unwinder_for_chrome->CanUnwindFrom(sample->back()));
-        EXPECT_EQ(UnwindResult::UNRECOGNIZED_FRAME,
-                  unwinder_for_chrome->TryUnwind(thread_context, stack_top,
-                                                 &module_cache, sample));
+        EXPECT_EQ(
+            UnwindResult::UNRECOGNIZED_FRAME,
+            unwinder_for_chrome->TryUnwind(thread_context, stack_top,
+                                           &module_cache_for_chrome, sample));
         EXPECT_FALSE(unwinder_for_chrome->CanUnwindFrom(sample->back()));
         EXPECT_LT(prior_stack_size, sample->size());
         ExpectStackContains(*sample, {scenario.GetWaitForSampleAddressRange()});
@@ -318,7 +328,7 @@
         ASSERT_TRUE(unwinder_for_all->CanUnwindFrom(sample->back()));
         EXPECT_EQ(UnwindResult::COMPLETED,
                   unwinder_for_all->TryUnwind(thread_context, stack_top,
-                                              &module_cache, sample));
+                                              &module_cache_for_all, sample));
       }));
 
   // The stack should contain a full unwind.
@@ -370,7 +380,7 @@
       std::make_unique<NativeUnwinderAndroid>(maps.get(), memory.get(), 0);
 
   ModuleCache module_cache;
-  unwinder->AddInitialModules(&module_cache);
+  unwinder->InitializeModules(&module_cache);
   std::vector<Frame> sample =
       CaptureScenario(&scenario, &module_cache,
                       BindLambdaForTesting([&](RegisterContext* thread_context,
@@ -431,12 +441,17 @@
              maps);
 
   ModuleCache module_cache;
-  NativeUnwinderAndroid::AddInitialModulesFromMaps(maps, &module_cache);
 
-  std::vector<const ModuleCache::Module*> modules = module_cache.GetModules();
+  std::unique_ptr<unwindstack::Memory> memory =
+      NativeUnwinderAndroid::CreateProcessMemory();
+  auto unwinder =
+      std::make_unique<NativeUnwinderAndroid>(&maps, memory.get(), 0);
+  unwinder->InitializeModules(&module_cache);
 
-  ASSERT_EQ(1u, modules.size());
-  EXPECT_EQ("[foo / bar]", modules[0]->GetDebugBasename().value());
+  const ModuleCache::Module* module = module_cache.GetModuleForAddress(0x1000u);
+
+  ASSERT_TRUE(module);
+  EXPECT_EQ("[foo / bar]", module->GetDebugBasename().value());
 }
 
 // Checks that modules are only created for executable memory regions.
@@ -446,113 +461,26 @@
   AddMapInfo(0x2000u, 0x3000u, 0u, PROT_READ, "[b]", {0xAB}, maps);
   AddMapInfo(0x3000u, 0x4000u, 0u, PROT_READ | PROT_EXEC, "[c]", {0xAC}, maps);
 
+  std::unique_ptr<unwindstack::Memory> memory =
+      NativeUnwinderAndroid::CreateProcessMemory();
+
   ModuleCache module_cache;
-  NativeUnwinderAndroid::AddInitialModulesFromMaps(maps, &module_cache);
+  auto unwinder =
+      std::make_unique<NativeUnwinderAndroid>(&maps, memory.get(), 0);
+  unwinder->InitializeModules(&module_cache);
 
-  std::vector<const ModuleCache::Module*> modules = module_cache.GetModules();
-  std::sort(modules.begin(), modules.end(), CompareModulesByBaseAddress);
+  const ModuleCache::Module* module1 =
+      module_cache.GetModuleForAddress(0x1000u);
+  const ModuleCache::Module* module2 =
+      module_cache.GetModuleForAddress(0x2000u);
+  const ModuleCache::Module* module3 =
+      module_cache.GetModuleForAddress(0x3000u);
 
-  ASSERT_EQ(2u, modules.size());
-  EXPECT_EQ(0x1000u, modules[0]->GetBaseAddress());
-  EXPECT_EQ(0x3000u, modules[1]->GetBaseAddress());
-}
-
-// Checks that module address ranges don't overlap.
-TEST(NativeUnwinderAndroidTest, NonOverlappingModules) {
-  ModuleCache module_cache;
-  std::unique_ptr<unwindstack::Maps> maps = NativeUnwinderAndroid::CreateMaps();
-  NativeUnwinderAndroid::AddInitialModulesFromMaps(*maps, &module_cache);
-
-  std::vector<const ModuleCache::Module*> modules = module_cache.GetModules();
-  std::sort(modules.begin(), modules.end(), CompareModulesByBaseAddress);
-  auto loc = std::adjacent_find(
-      modules.begin(), modules.end(),
-      [](const ModuleCache::Module* m1, const ModuleCache::Module* m2) {
-        return m2->GetBaseAddress() < m1->GetBaseAddress() + m1->GetSize();
-      });
-
-  const auto describe_module = [](const ModuleCache::Module* module) {
-    return StringPrintf(
-        "id \"%s\", debug basename \"%s\" at [0x%" PRIxPTR ", 0x%" PRIxPTR ")",
-        module->GetId().c_str(), module->GetDebugBasename().value().c_str(),
-        module->GetBaseAddress(), module->GetBaseAddress() + module->GetSize());
-  };
-
-  EXPECT_EQ(modules.end(), loc) << "module overlap found between\n"
-                                << "  " << describe_module(*loc) << " and \n"
-                                << "  " << describe_module(*std::next(loc));
-}
-
-// ModuleCache::GetModuleForAddress() is not implemented for 64-bit arm.
-#if defined(ARCH_CPU_ARM64)
-#define MAYBE_ModuleState_SystemLibrary DISABLED_ModuleState_SystemLibrary
-#else
-#define MAYBE_ModuleState_SystemLibrary ModuleState_SystemLibrary
-#endif
-// Checks that the module state created by the unwinder is consistent with the
-// state created by the ModuleCache. Checks the module for a system library.
-TEST(NativeUnwinderAndroidTest, MAYBE_ModuleState_SystemLibrary) {
-  ModuleCache unwinder_module_cache;
-  std::unique_ptr<unwindstack::Maps> maps = NativeUnwinderAndroid::CreateMaps();
-  NativeUnwinderAndroid::AddInitialModulesFromMaps(*maps,
-                                                   &unwinder_module_cache);
-
-  const uintptr_t c_library_function_address =
-      reinterpret_cast<uintptr_t>(&printf);
-
-  const ModuleCache::Module* unwinder_module =
-      unwinder_module_cache.GetExistingModuleForAddress(
-          c_library_function_address);
-  ASSERT_NE(nullptr, unwinder_module);
-
-  ModuleCache reference_module_cache;
-  const ModuleCache::Module* reference_module =
-      reference_module_cache.GetModuleForAddress(c_library_function_address);
-  ASSERT_NE(nullptr, reference_module);
-
-  EXPECT_EQ(reference_module->GetBaseAddress(),
-            unwinder_module->GetBaseAddress());
-  EXPECT_EQ(reference_module->GetId(), unwinder_module->GetId());
-  EXPECT_EQ(reference_module->GetDebugBasename(),
-            unwinder_module->GetDebugBasename());
-  EXPECT_EQ(unwinder_module->GetSize(), reference_module->GetSize());
-}
-
-// ModuleCache::GetModuleForAddress() is not implemented for 64-bit arm.
-#if defined(ARCH_CPU_ARM64)
-#define MAYBE_ModuleState_ChromeLibrary DISABLED_ModuleState_ChromeLibrary
-#else
-#define MAYBE_ModuleState_ChromeLibrary ModuleState_ChromeLibrary
-#endif
-// Checks that the module state created by the unwinder is consistent with the
-// state created by the ModuleCache. Checks the module for a Chrome-compiled
-// library.
-TEST(NativeUnwinderAndroidTest, MAYBE_ModuleState_ChromeLibrary) {
-  ModuleCache unwinder_module_cache;
-  std::unique_ptr<unwindstack::Maps> maps = NativeUnwinderAndroid::CreateMaps();
-  NativeUnwinderAndroid::AddInitialModulesFromMaps(*maps,
-                                                   &unwinder_module_cache);
-
-  const uintptr_t chrome_function_address =
-      reinterpret_cast<uintptr_t>(&CaptureScenario);
-
-  const ModuleCache::Module* unwinder_module =
-      unwinder_module_cache.GetExistingModuleForAddress(
-          chrome_function_address);
-  ASSERT_NE(nullptr, unwinder_module);
-
-  ModuleCache reference_module_cache;
-  const ModuleCache::Module* reference_module =
-      reference_module_cache.GetModuleForAddress(chrome_function_address);
-  ASSERT_NE(nullptr, reference_module);
-
-  EXPECT_EQ(reference_module->GetBaseAddress(),
-            unwinder_module->GetBaseAddress());
-  EXPECT_NE("", unwinder_module->GetId());
-  EXPECT_EQ(reference_module->GetId(), unwinder_module->GetId());
-  EXPECT_EQ(reference_module->GetDebugBasename(),
-            unwinder_module->GetDebugBasename());
-  EXPECT_EQ(unwinder_module->GetSize(), reference_module->GetSize());
+  ASSERT_TRUE(module1);
+  EXPECT_EQ(0x1000u, module1->GetBaseAddress());
+  EXPECT_EQ(nullptr, module2);
+  ASSERT_TRUE(module3);
+  EXPECT_EQ(0x3000u, module3->GetBaseAddress());
 }
 
 }  // namespace base
diff --git a/base/profiler/stack_sampler_impl.cc b/base/profiler/stack_sampler_impl.cc
index bbe91e4..6a214d6 100644
--- a/base/profiler/stack_sampler_impl.cc
+++ b/base/profiler/stack_sampler_impl.cc
@@ -92,17 +92,17 @@
                     std::make_move_iterator(unwinders.rend()));
 
   for (const auto& unwinder : unwinders_)
-    unwinder->AddInitialModules(module_cache_);
+    unwinder->InitializeModules(module_cache_);
 
   was_initialized_ = true;
 }
 
 void StackSamplerImpl::AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder) {
-  // Initialize() invokes AddInitialModules() on the unwinders that are present
+  // Initialize() invokes InitializeModules() on the unwinders that are present
   // at the time. If it hasn't occurred yet, we allow it to add the initial
   // modules, otherwise we do it here.
   if (was_initialized_)
-    unwinder->AddInitialModules(module_cache_);
+    unwinder->InitializeModules(module_cache_);
   unwinders_.push_front(std::move(unwinder));
 }
 
diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc
index 2bd80be9f..0c443e2 100644
--- a/base/profiler/stack_sampling_profiler.cc
+++ b/base/profiler/stack_sampling_profiler.cc
@@ -126,8 +126,8 @@
         : collection_id(next_collection_id.GetNext()),
           params(params),
           finished(finished),
-          sampler(std::move(sampler)),
-          profile_builder(std::move(profile_builder)) {}
+          profile_builder(std::move(profile_builder)),
+          sampler(std::move(sampler)) {}
     ~CollectionContext() = default;
 
     // An identifier for this collection, used to uniquely identify the
@@ -137,12 +137,12 @@
     const SamplingParams params;    // Information about how to sample.
     WaitableEvent* const finished;  // Signaled when all sampling complete.
 
-    // Platform-specific module that does the actual sampling.
-    std::unique_ptr<StackSampler> sampler;
-
     // Receives the sampling data and builds a CallStackProfile.
     std::unique_ptr<ProfileBuilder> profile_builder;
 
+    // Platform-specific module that does the actual sampling.
+    std::unique_ptr<StackSampler> sampler;
+
     // The absolute time for the next sample.
     TimeTicks next_sample_time;
 
@@ -217,7 +217,7 @@
   // signalled. The |collection| should already have been removed from
   // |active_collections_| by the caller, as this is needed to avoid flakiness
   // in unit tests.
-  void FinishCollection(CollectionContext* collection);
+  void FinishCollection(std::unique_ptr<CollectionContext> collection);
 
   // Check if the sampling thread is idle and begin a shutdown if it is.
   void ScheduleShutdownIfIdle();
@@ -506,7 +506,7 @@
 }
 
 void StackSamplingProfiler::SamplingThread::FinishCollection(
-    CollectionContext* collection) {
+    std::unique_ptr<CollectionContext> collection) {
   DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
   DCHECK_EQ(0u, active_collections_.count(collection->collection_id));
 
@@ -518,7 +518,11 @@
       profile_duration, collection->params.sampling_interval);
 
   // Signal that this collection is finished.
-  collection->finished->Signal();
+  WaitableEvent* collection_finished = collection->finished;
+  // Ensure the collection is destroyed before signaling, so that it may
+  // not outlive StackSamplingProfiler.
+  collection.reset();
+  collection_finished->Signal();
 
   ScheduleShutdownIfIdle();
 }
@@ -612,7 +616,7 @@
   size_t count = active_collections_.erase(collection_id);
   DCHECK_EQ(1U, count);
 
-  FinishCollection(collection.get());
+  FinishCollection(std::move(collection));
 }
 
 void StackSamplingProfiler::SamplingThread::RecordSampleTask(
@@ -658,7 +662,7 @@
   DCHECK_EQ(1U, count);
 
   // All capturing has completed so finish the collection.
-  FinishCollection(collection);
+  FinishCollection(std::move(owned_collection));
 }
 
 void StackSamplingProfiler::SamplingThread::ShutdownTask(int add_events) {
diff --git a/base/profiler/stack_sampling_profiler_unittest.cc b/base/profiler/stack_sampling_profiler_unittest.cc
index 6c8c3a32..e686fd20 100644
--- a/base/profiler/stack_sampling_profiler_unittest.cc
+++ b/base/profiler/stack_sampling_profiler_unittest.cc
@@ -230,22 +230,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestProfilerInfo);
 };
 
-// Creates multiple profilers based on a vector of parameters.
-std::vector<std::unique_ptr<TestProfilerInfo>> CreateProfilers(
-    SamplingProfilerThreadToken target_thread_token,
-    const std::vector<SamplingParams>& params,
-    ModuleCache* module_cache) {
-  DCHECK(!params.empty());
-
-  std::vector<std::unique_ptr<TestProfilerInfo>> profilers;
-  for (const auto& i : params) {
-    profilers.push_back(std::make_unique<TestProfilerInfo>(target_thread_token,
-                                                           i, module_cache));
-  }
-
-  return profilers;
-}
-
 // Captures samples as specified by |params| on the TargetThread, and returns
 // them. Waits up to |profiler_wait_time| for the profiler to complete.
 std::vector<std::vector<Frame>> CaptureSamples(const SamplingParams& params,
@@ -483,7 +467,7 @@
   TestAuxUnwinder(const TestAuxUnwinder&) = delete;
   TestAuxUnwinder& operator=(const TestAuxUnwinder&) = delete;
 
-  void AddInitialModules(ModuleCache* module_cache) override {
+  void InitializeModules(ModuleCache* module_cache) override {
     if (add_initial_modules_callback_)
       add_initial_modules_callback_.Run();
   }
@@ -631,8 +615,8 @@
     size_t count_ = 0;
   };
 
-  WithTargetThread(BindLambdaForTesting(
-      [this](SamplingProfilerThreadToken target_thread_token) {
+  WithTargetThread(
+      BindLambdaForTesting([](SamplingProfilerThreadToken target_thread_token) {
         SamplingParams params[2];
 
         // Providing an initial delay makes it more likely that both will be
@@ -648,11 +632,11 @@
         params[1].samples_per_profile = 100000;
 
         SampleRecordedCounter samples_recorded[size(params)];
-
+        ModuleCache module_cache1, module_cache2;
         TestProfilerInfo profiler_info0(target_thread_token, params[0],
-                                        module_cache(), &samples_recorded[0]);
+                                        &module_cache1, &samples_recorded[0]);
         TestProfilerInfo profiler_info1(target_thread_token, params[1],
-                                        module_cache(), &samples_recorded[1]);
+                                        &module_cache2, &samples_recorded[1]);
 
         profiler_info0.profiler.Start();
         profiler_info1.profiler.Start();
@@ -848,23 +832,26 @@
 
 // Checks that a sampler can be started while another is running.
 PROFILER_TEST_F(StackSamplingProfilerTest, MultipleStart) {
-  WithTargetThread(BindLambdaForTesting(
-      [this](SamplingProfilerThreadToken target_thread_token) {
-        std::vector<SamplingParams> params(2);
+  WithTargetThread(
+      BindLambdaForTesting([](SamplingProfilerThreadToken target_thread_token) {
+        SamplingParams params1;
+        params1.initial_delay = AVeryLongTimeDelta();
+        params1.samples_per_profile = 1;
+        ModuleCache module_cache1;
+        TestProfilerInfo profiler_info1(target_thread_token, params1,
+                                        &module_cache1);
 
-        params[0].initial_delay = AVeryLongTimeDelta();
-        params[0].samples_per_profile = 1;
+        SamplingParams params2;
+        params2.sampling_interval = TimeDelta::FromMilliseconds(1);
+        params2.samples_per_profile = 1;
+        ModuleCache module_cache2;
+        TestProfilerInfo profiler_info2(target_thread_token, params2,
+                                        &module_cache2);
 
-        params[1].sampling_interval = TimeDelta::FromMilliseconds(1);
-        params[1].samples_per_profile = 1;
-
-        std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos =
-            CreateProfilers(target_thread_token, params, module_cache());
-
-        profiler_infos[0]->profiler.Start();
-        profiler_infos[1]->profiler.Start();
-        profiler_infos[1]->completed.Wait();
-        EXPECT_EQ(1u, profiler_infos[1]->profile.samples.size());
+        profiler_info1.profiler.Start();
+        profiler_info2.profiler.Start();
+        profiler_info2.completed.Wait();
+        EXPECT_EQ(1u, profiler_info2.profile.samples.size());
       }));
 }
 
@@ -981,20 +968,26 @@
 // started.
 PROFILER_TEST_F(StackSamplingProfilerTest,
                 ProfileBeforeAndAfterSamplingThreadRunning) {
-  WithTargetThread(BindLambdaForTesting(
-      [this](SamplingProfilerThreadToken target_thread_token) {
-        std::vector<SamplingParams> params(2);
+  WithTargetThread(
+      BindLambdaForTesting([](SamplingProfilerThreadToken target_thread_token) {
+        ModuleCache module_cache1;
+        ModuleCache module_cache2;
 
-        params[0].initial_delay = AVeryLongTimeDelta();
-        params[0].sampling_interval = TimeDelta::FromMilliseconds(1);
-        params[0].samples_per_profile = 1;
-
-        params[1].initial_delay = TimeDelta::FromMilliseconds(0);
-        params[1].sampling_interval = TimeDelta::FromMilliseconds(1);
-        params[1].samples_per_profile = 1;
-
-        std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos =
-            CreateProfilers(target_thread_token, params, module_cache());
+        std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos;
+        profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
+            target_thread_token,
+            SamplingParams{
+                /*initial_delay=*/AVeryLongTimeDelta(),
+                /*samples_per_profile=*/1,
+                /*sampling_interval=*/TimeDelta::FromMilliseconds(1)},
+            &module_cache1));
+        profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
+            target_thread_token,
+            SamplingParams{
+                /*initial_delay=*/TimeDelta::FromMilliseconds(0),
+                /*samples_per_profile=*/1,
+                /*sampling_interval=*/TimeDelta::FromMilliseconds(1)},
+            &module_cache2));
 
         // First profiler is started when there has never been a sampling
         // thread.
@@ -1053,9 +1046,9 @@
 
 // Checks that synchronized multiple sampling requests execute in parallel.
 PROFILER_TEST_F(StackSamplingProfilerTest, ConcurrentProfiling_InSync) {
-  WithTargetThread(BindLambdaForTesting(
-      [this](SamplingProfilerThreadToken target_thread_token) {
-        std::vector<SamplingParams> params(2);
+  WithTargetThread(
+      BindLambdaForTesting([](SamplingProfilerThreadToken target_thread_token) {
+        std::vector<ModuleCache> module_caches(2);
 
         // Providing an initial delay makes it more likely that both will be
         // scheduled before either starts to run. Once started, samples will
@@ -1063,16 +1056,21 @@
         // whatever interval the thread wakes up. Thus, total execution time
         // will be 10ms (delay) + 10x1ms (sampling) + 1/2 timer minimum
         // interval.
-        params[0].initial_delay = TimeDelta::FromMilliseconds(10);
-        params[0].sampling_interval = TimeDelta::FromMilliseconds(1);
-        params[0].samples_per_profile = 9;
-
-        params[1].initial_delay = TimeDelta::FromMilliseconds(11);
-        params[1].sampling_interval = TimeDelta::FromMilliseconds(1);
-        params[1].samples_per_profile = 8;
-
-        std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos =
-            CreateProfilers(target_thread_token, params, module_cache());
+        std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos;
+        profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
+            target_thread_token,
+            SamplingParams{
+                /*initial_delay=*/TimeDelta::FromMilliseconds(10),
+                /*samples_per_profile=*/9,
+                /*sampling_interval=*/TimeDelta::FromMilliseconds(1)},
+            &module_caches[0]));
+        profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
+            target_thread_token,
+            SamplingParams{
+                /*initial_delay=*/TimeDelta::FromMilliseconds(11),
+                /*samples_per_profile=*/8,
+                /*sampling_interval=*/TimeDelta::FromMilliseconds(1)},
+            &module_caches[1]));
 
         profiler_infos[0]->profiler.Start();
         profiler_infos[1]->profiler.Start();
@@ -1092,39 +1090,43 @@
 
 // Checks that several mixed sampling requests execute in parallel.
 PROFILER_TEST_F(StackSamplingProfilerTest, ConcurrentProfiling_Mixed) {
-  WithTargetThread(BindLambdaForTesting(
-      [this](SamplingProfilerThreadToken target_thread_token) {
-        std::vector<SamplingParams> params(3);
+  WithTargetThread(BindLambdaForTesting([](SamplingProfilerThreadToken
+                                               target_thread_token) {
+    std::vector<ModuleCache> module_caches(3);
 
-        params[0].initial_delay = TimeDelta::FromMilliseconds(8);
-        params[0].sampling_interval = TimeDelta::FromMilliseconds(4);
-        params[0].samples_per_profile = 10;
+    std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos;
+    profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
+        target_thread_token,
+        SamplingParams{/*initial_delay=*/TimeDelta::FromMilliseconds(8),
+                       /*samples_per_profile=*/10,
+                       /*sampling_interval=*/TimeDelta::FromMilliseconds(4)},
+        &module_caches[0]));
+    profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
+        target_thread_token,
+        SamplingParams{/*initial_delay=*/TimeDelta::FromMilliseconds(9),
+                       /*samples_per_profile=*/10,
+                       /*sampling_interval=*/TimeDelta::FromMilliseconds(3)},
+        &module_caches[1]));
+    profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
+        target_thread_token,
+        SamplingParams{/*initial_delay=*/TimeDelta::FromMilliseconds(10),
+                       /*samples_per_profile=*/10,
+                       /*sampling_interval=*/TimeDelta::FromMilliseconds(2)},
+        &module_caches[2]));
 
-        params[1].initial_delay = TimeDelta::FromMilliseconds(9);
-        params[1].sampling_interval = TimeDelta::FromMilliseconds(3);
-        params[1].samples_per_profile = 10;
+    for (auto& i : profiler_infos)
+      i->profiler.Start();
 
-        params[2].initial_delay = TimeDelta::FromMilliseconds(10);
-        params[2].sampling_interval = TimeDelta::FromMilliseconds(2);
-        params[2].samples_per_profile = 10;
-
-        std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos =
-            CreateProfilers(target_thread_token, params, module_cache());
-
-        for (auto& i : profiler_infos)
-          i->profiler.Start();
-
-        // Wait for one profiler to finish.
-        size_t completed_profiler = WaitForSamplingComplete(profiler_infos);
-        EXPECT_EQ(10u,
-                  profiler_infos[completed_profiler]->profile.samples.size());
-        // Stop and destroy all profilers, always in the same order. Don't
-        // crash.
-        for (auto& i : profiler_infos)
-          i->profiler.Stop();
-        for (auto& i : profiler_infos)
-          i.reset();
-      }));
+    // Wait for one profiler to finish.
+    size_t completed_profiler = WaitForSamplingComplete(profiler_infos);
+    EXPECT_EQ(10u, profiler_infos[completed_profiler]->profile.samples.size());
+    // Stop and destroy all profilers, always in the same order. Don't
+    // crash.
+    for (auto& i : profiler_infos)
+      i->profiler.Stop();
+    for (auto& i : profiler_infos)
+      i.reset();
+  }));
 }
 
 // Checks that different threads can be sampled in parallel.
@@ -1161,6 +1163,7 @@
   params2.samples_per_profile = 8;
 
   Profile profile1, profile2;
+  ModuleCache module_cache1, module_cache2;
 
   WaitableEvent sampling_thread_completed1(
       WaitableEvent::ResetPolicy::MANUAL,
@@ -1168,13 +1171,13 @@
   StackSamplingProfiler profiler1(
       target_thread1.thread_token(), params1,
       std::make_unique<TestProfileBuilder>(
-          module_cache(),
+          &module_cache1,
           BindLambdaForTesting(
               [&profile1, &sampling_thread_completed1](Profile result_profile) {
                 profile1 = std::move(result_profile);
                 sampling_thread_completed1.Signal();
               })),
-      CreateCoreUnwindersFactoryForTesting(module_cache()));
+      CreateCoreUnwindersFactoryForTesting(&module_cache1));
 
   WaitableEvent sampling_thread_completed2(
       WaitableEvent::ResetPolicy::MANUAL,
@@ -1182,13 +1185,13 @@
   StackSamplingProfiler profiler2(
       target_thread2.thread_token(), params2,
       std::make_unique<TestProfileBuilder>(
-          module_cache(),
+          &module_cache2,
           BindLambdaForTesting(
               [&profile2, &sampling_thread_completed2](Profile result_profile) {
                 profile2 = std::move(result_profile);
                 sampling_thread_completed2.Signal();
               })),
-      CreateCoreUnwindersFactoryForTesting(module_cache()));
+      CreateCoreUnwindersFactoryForTesting(&module_cache2));
 
   // Finally the real work.
   profiler1.Start();
diff --git a/base/profiler/unwinder.h b/base/profiler/unwinder.h
index 06f70021..864f29ff 100644
--- a/base/profiler/unwinder.h
+++ b/base/profiler/unwinder.h
@@ -36,9 +36,12 @@
  public:
   virtual ~Unwinder() = default;
 
-  // Invoked to allow the unwinder to add any modules it recognizes to the
-  // ModuleCache.
-  virtual void AddInitialModules(ModuleCache* module_cache) {}
+  // Invoked to allow the unwinder to add any modules it recognizes or register
+  // a module factory to the ModuleCache. This associates this Unwinder with
+  // |module_cache| for the remaining of its lifetime, which is reused in
+  // subsequent methods UpdateModules() and TryUnwinder(). Thus, |module_cache|
+  // must outlive this Unwinder.
+  virtual void InitializeModules(ModuleCache* module_cache) {}
 
   // Invoked at the time the stack is captured. IMPORTANT NOTE: this function is
   // invoked while the target thread is suspended. To avoid deadlock it must not
diff --git a/base/trace_event/common/trace_event_common.h b/base/trace_event/common/trace_event_common.h
index 120481f3..684c8a6 100644
--- a/base/trace_event/common/trace_event_common.h
+++ b/base/trace_event/common/trace_event_common.h
@@ -861,109 +861,6 @@
                                    category_group, name, id,             \
                                    TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
 
-// Records a single FLOW_BEGIN event called "name" immediately, with 0, 1 or 2
-// associated arguments. If the category is not enabled, then this
-// does nothing.
-// - category and name strings must have application lifetime (statics or
-//   literals). They may not include " chars.
-// - |id| is used to match the FLOW_BEGIN event with the FLOW_END event. FLOW
-//   events are considered to match if their category_group, name and id values
-//   all match. |id| must either be a pointer or an integer value up to 64 bits.
-//   If it's a pointer, the bits will be xored with a hash of the process ID so
-//   that the same pointer on two different processes will not collide.
-// FLOW events are different from ASYNC events in how they are drawn by the
-// tracing UI. A FLOW defines asynchronous data flow, such as posting a task
-// (FLOW_BEGIN) and later executing that task (FLOW_END). Expect FLOWs to be
-// drawn as lines or arrows from FLOW_BEGIN scopes to FLOW_END scopes. Similar
-// to ASYNC, a FLOW can consist of multiple phases. The first phase is defined
-// by the FLOW_BEGIN calls. Additional phases can be defined using the FLOW_STEP
-// macros. When the operation completes, call FLOW_END. An async operation can
-// span threads and processes, but all events in that operation must use the
-// same |name| and |id|. Each event can have its own args.
-#define TRACE_EVENT_FLOW_BEGIN0(category_group, name, id)        \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
-                                   category_group, name, id,     \
-                                   TRACE_EVENT_FLAG_NONE)
-#define TRACE_EVENT_FLOW_BEGIN1(category_group, name, id, arg1_name, arg1_val) \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN,               \
-                                   category_group, name, id,                   \
-                                   TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
-#define TRACE_EVENT_FLOW_BEGIN2(category_group, name, id, arg1_name, arg1_val, \
-                                arg2_name, arg2_val)                           \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                            \
-      TRACE_EVENT_PHASE_FLOW_BEGIN, category_group, name, id,                  \
-      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
-#define TRACE_EVENT_COPY_FLOW_BEGIN0(category_group, name, id)   \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
-                                   category_group, name, id,     \
-                                   TRACE_EVENT_FLAG_COPY)
-#define TRACE_EVENT_COPY_FLOW_BEGIN1(category_group, name, id, arg1_name, \
-                                     arg1_val)                            \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN,          \
-                                   category_group, name, id,              \
-                                   TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
-#define TRACE_EVENT_COPY_FLOW_BEGIN2(category_group, name, id, arg1_name, \
-                                     arg1_val, arg2_name, arg2_val)       \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                       \
-      TRACE_EVENT_PHASE_FLOW_BEGIN, category_group, name, id,             \
-      TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, arg2_name, arg2_val)
-
-// Records a single FLOW_STEP event for |step| immediately. If the category
-// is not enabled, then this does nothing. The |name| and |id| must match the
-// FLOW_BEGIN event above. The |step| param identifies this step within the
-// async event. This should be called at the beginning of the next phase of an
-// asynchronous operation.
-#define TRACE_EVENT_FLOW_STEP0(category_group, name, id, step)  \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
-                                   category_group, name, id,    \
-                                   TRACE_EVENT_FLAG_NONE, "step", step)
-#define TRACE_EVENT_FLOW_STEP1(category_group, name, id, step, arg1_name, \
-                               arg1_val)                                  \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                       \
-      TRACE_EVENT_PHASE_FLOW_STEP, category_group, name, id,              \
-      TRACE_EVENT_FLAG_NONE, "step", step, arg1_name, arg1_val)
-#define TRACE_EVENT_COPY_FLOW_STEP0(category_group, name, id, step) \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP,     \
-                                   category_group, name, id,        \
-                                   TRACE_EVENT_FLAG_COPY, "step", step)
-#define TRACE_EVENT_COPY_FLOW_STEP1(category_group, name, id, step, arg1_name, \
-                                    arg1_val)                                  \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                            \
-      TRACE_EVENT_PHASE_FLOW_STEP, category_group, name, id,                   \
-      TRACE_EVENT_FLAG_COPY, "step", step, arg1_name, arg1_val)
-
-// Records a single FLOW_END event for "name" immediately. If the category
-// is not enabled, then this does nothing.
-#define TRACE_EVENT_FLOW_END0(category_group, name, id)                        \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
-                                   name, id, TRACE_EVENT_FLAG_NONE)
-#define TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0(category_group, name, id)      \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
-                                   name, id,                                   \
-                                   TRACE_EVENT_FLAG_BIND_TO_ENCLOSING)
-#define TRACE_EVENT_FLOW_END1(category_group, name, id, arg1_name, arg1_val)   \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
-                                   name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \
-                                   arg1_val)
-#define TRACE_EVENT_FLOW_END2(category_group, name, id, arg1_name, arg1_val,   \
-                              arg2_name, arg2_val)                             \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
-                                   name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \
-                                   arg1_val, arg2_name, arg2_val)
-#define TRACE_EVENT_COPY_FLOW_END0(category_group, name, id)                   \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
-                                   name, id, TRACE_EVENT_FLAG_COPY)
-#define TRACE_EVENT_COPY_FLOW_END1(category_group, name, id, arg1_name,        \
-                                   arg1_val)                                   \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
-                                   name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \
-                                   arg1_val)
-#define TRACE_EVENT_COPY_FLOW_END2(category_group, name, id, arg1_name,        \
-                                   arg1_val, arg2_name, arg2_val)              \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
-                                   name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \
-                                   arg1_val, arg2_name, arg2_val)
-
 // Special trace event macro to trace task execution with the location where it
 // was posted from.
 #define TRACE_TASK_EXECUTION(run_function, task) \
diff --git a/base/trace_event/trace_event_unittest.cc b/base/trace_event/trace_event_unittest.cc
index 657c53f..457bcdf 100644
--- a/base/trace_event/trace_event_unittest.cc
+++ b/base/trace_event/trace_event_unittest.cc
@@ -64,8 +64,6 @@
 const char kAsyncIdStr[] = "0x5";
 const int kAsyncId2 = 6;
 const char kAsyncId2Str[] = "0x6";
-const int kFlowId = 7;
-const char kFlowIdStr[] = "0x7";
 
 constexpr const char kRecordAllCategoryFilter[] = "*";
 constexpr const char kAllCategory[] = "all";
@@ -445,12 +443,6 @@
                            "name1", "value1",
                            "name2", "value2");
 
-    TRACE_EVENT_FLOW_BEGIN0("all", "TRACE_EVENT_FLOW_BEGIN0 call", kFlowId);
-    TRACE_EVENT_FLOW_STEP0("all", "TRACE_EVENT_FLOW_STEP0 call",
-                           kFlowId, "step1");
-    TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0("all",
-        "TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0 call", kFlowId);
-
     TRACE_COUNTER1("all", "TRACE_COUNTER1 call", 31415);
     TRACE_COUNTER2("all", "TRACE_COUNTER2 call",
                    "a", 30000,
@@ -643,17 +635,6 @@
   EXPECT_SUB_FIND_("name2");
   EXPECT_SUB_FIND_("value2");
 
-  EXPECT_FIND_("TRACE_EVENT_FLOW_BEGIN0 call");
-  EXPECT_SUB_FIND_("id");
-  EXPECT_SUB_FIND_(kFlowIdStr);
-  EXPECT_FIND_("TRACE_EVENT_FLOW_STEP0 call");
-  EXPECT_SUB_FIND_("id");
-  EXPECT_SUB_FIND_(kFlowIdStr);
-  EXPECT_SUB_FIND_("step1");
-  EXPECT_FIND_("TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0 call");
-  EXPECT_SUB_FIND_("id");
-  EXPECT_SUB_FIND_(kFlowIdStr);
-
   EXPECT_FIND_("TRACE_COUNTER1 call");
   {
     std::string ph;
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index a0f2152..622c2f0 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20210111.1.1
+0.20210111.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index a0f2152..622c2f0 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20210111.1.1
+0.20210111.3.1
diff --git a/buildtools/DEPS b/buildtools/DEPS
index 56de1b6..2dd38b6 100644
--- a/buildtools/DEPS
+++ b/buildtools/DEPS
@@ -14,7 +14,7 @@
   #
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:0d67e272bdb8145f87d238bc0b2cb8bf80ccec90',
+  'gn_version': 'git_revision:595e3be7c8381d4eeefce62a63ec12bae9ce5140',
 
   # By default, do not checkout the re-client binaries.
   'checkout_reclient': False,
diff --git a/cc/animation/scroll_offset_animation_curve.cc b/cc/animation/scroll_offset_animation_curve.cc
index bc1a33e..52bb2a8 100644
--- a/cc/animation/scroll_offset_animation_curve.cc
+++ b/cc/animation/scroll_offset_animation_curve.cc
@@ -361,6 +361,11 @@
   DCHECK_NE(animation_type_, AnimationType::kLinear)
       << "UpdateTarget is not supported on linear scroll animations.";
 
+  // UpdateTarget is still called for linear animations occasionally. This is
+  // tracked via crbug.com/1164008.
+  if (animation_type_ == AnimationType::kLinear)
+    return;
+
   // If the new UpdateTarget actually happened before the previous one, keep
   // |t| as the most recent, but reduce the duration of any generated
   // animation.
diff --git a/cc/animation/scroll_timeline.h b/cc/animation/scroll_timeline.h
index c79001f..fbdbc46783 100644
--- a/cc/animation/scroll_timeline.h
+++ b/cc/animation/scroll_timeline.h
@@ -5,6 +5,7 @@
 #ifndef CC_ANIMATION_SCROLL_TIMELINE_H_
 #define CC_ANIMATION_SCROLL_TIMELINE_H_
 
+#include <vector>
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "cc/animation/animation_export.h"
@@ -124,14 +125,22 @@
 template <typename T>
 double ComputeProgress(double current_offset, const T& resolved_offsets) {
   DCHECK_GE(resolved_offsets.size(), 2u);
+  // When start offset is greater than end offset, current time is calculated
+  // outside of this method.
+  DCHECK(resolved_offsets[0] < resolved_offsets[resolved_offsets.size() - 1]);
   DCHECK(current_offset < resolved_offsets[resolved_offsets.size() - 1]);
-  // Look for scroll offset that contains the current offset.
+  // Traverse scroll offsets from the back to find first interval that
+  // contains the current offset. In case of overlapping offsets, last matching
+  // interval in the list is used to calculate the current time. The rational
+  // for choosing last matching offset is to be consistent with CSS property
+  // overrides.
   unsigned int offset_id;
-  for (offset_id = 1; offset_id < resolved_offsets.size() &&
-                      resolved_offsets[offset_id] <= current_offset;
-       offset_id++) {
+  for (offset_id = resolved_offsets.size() - 1;
+       offset_id > 0 && !(resolved_offsets[offset_id - 1] <= current_offset &&
+                          current_offset < resolved_offsets[offset_id]);
+       offset_id--) {
   }
-  DCHECK(offset_id < resolved_offsets.size());
+  DCHECK_GE(offset_id, 1u);
   // Weight of each offset within time range is distributed equally.
   double offset_distance = 1.0 / (resolved_offsets.size() - 1);
   // Progress of the current offset within its offset range.
diff --git a/cc/animation/scroll_timeline_unittest.cc b/cc/animation/scroll_timeline_unittest.cc
index 15589ae..4ee60fc 100644
--- a/cc/animation/scroll_timeline_unittest.cc
+++ b/cc/animation/scroll_timeline_unittest.cc
@@ -205,6 +205,51 @@
       time_range, vertical_timeline->CurrentTime(scroll_tree(), false));
 }
 
+TEST_F(ScrollTimelineTest, OverlappingScrollOffsets) {
+  double time_range = 100.0;
+
+  // Start offset is greater than end offset ==> animation progress is
+  // either 0% or 100%.
+  std::vector<double> scroll_offsets = {350.0, 200.0, 50.0};
+
+  scoped_refptr<ScrollTimeline> vertical_timeline = ScrollTimeline::Create(
+      scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets, time_range);
+
+  // Offset is less than start offset ==> current time is 0.
+  SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 300));
+  EXPECT_SCROLL_TIMELINE_TIME_NEAR(
+      0, vertical_timeline->CurrentTime(scroll_tree(), false));
+
+  // Offset is greater than end offset ==> current time is time_range.
+  SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 360));
+  EXPECT_SCROLL_TIMELINE_TIME_NEAR(
+      time_range, vertical_timeline->CurrentTime(scroll_tree(), false));
+
+  scroll_offsets = {0.0, 400.0, 200.0};
+
+  vertical_timeline = ScrollTimeline::Create(
+      scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets, time_range);
+
+  SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 100));
+  // Scroll offset is 25% of [0, 400) range, which maps to [0% 50%) of the
+  // entire scroll range.
+  EXPECT_SCROLL_TIMELINE_TIME_NEAR(
+      time_range * 0.5 * 0.25,
+      vertical_timeline->CurrentTime(scroll_tree(), false));
+
+  scroll_offsets = {200.0, 0.0, 400.0};
+
+  vertical_timeline = ScrollTimeline::Create(
+      scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets, time_range);
+
+  SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 300));
+  // Scroll offset is 75% of [0, 400) range, which maps to [50% 100%) of the
+  // entire scroll range.
+  EXPECT_SCROLL_TIMELINE_TIME_NEAR(
+      time_range * (0.5 + 0.5 * 0.75),
+      vertical_timeline->CurrentTime(scroll_tree(), false));
+}
+
 TEST_F(ScrollTimelineTest, CurrentTimeIsAdjustedForTimeRange) {
   double time_range = content_size().height() - container_size().height();
 
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index dd50507..29bcf4e5 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -17,6 +17,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
 #include "base/numerics/ranges.h"
+#include "base/system/sys_info.h"
 #include "base/time/time.h"
 #include "base/trace_event/traced_value.h"
 #include "build/build_config.h"
@@ -1112,6 +1113,15 @@
   if (raster_scale < 0.1f)
     return true;
 
+#if defined(OS_FUCHSIA)
+  // Always downscale images on low-end devices to save memory. This is a
+  // temporary fix to work around crbug.com/1161327 .
+  // TODO(crbug.com/1161327): Implement proper solution that works on all
+  // devices.
+  if (base::SysInfo::IsLowEndDevice() && raster_scale > 1.0)
+    return false;
+#endif  // defined(OS_FUCHSIA)
+
   // If the results of scaling the bounds by the expected raster scale
   // would end up with a content rect whose width/height are more than one
   // pixel different from the layer bounds, don't directly composite the image
diff --git a/cc/paint/paint_image.cc b/cc/paint/paint_image.cc
index f57728dd..70853e09 100644
--- a/cc/paint/paint_image.cc
+++ b/cc/paint/paint_image.cc
@@ -298,6 +298,11 @@
     texture_backing_->FlushPendingSkiaOps();
 }
 
+bool PaintImage::HasExclusiveTextureAccess() const {
+  DCHECK(IsTextureBacked());
+  return texture_backing_->unique();
+}
+
 int PaintImage::width() const {
   return paint_worklet_input_
              ? static_cast<int>(paint_worklet_input_->GetSize().width())
diff --git a/cc/paint/paint_image.h b/cc/paint/paint_image.h
index ab1a18c..30b369b 100644
--- a/cc/paint/paint_image.h
+++ b/cc/paint/paint_image.h
@@ -239,11 +239,10 @@
                   int src_x,
                   int src_y) const;
 
-  // Returned mailbox must not outlive this PaintImage.
-  gpu::Mailbox GetMailbox() const;
+  SkImageInfo GetSkImageInfo() const;
 
   Id stable_id() const { return id_; }
-  SkImageInfo GetSkImageInfo() const;
+  gpu::Mailbox GetMailbox() const;
   AnimationType animation_type() const { return animation_type_; }
   CompletionState completion_state() const { return completion_state_; }
   bool is_multipart() const { return is_multipart_; }
@@ -266,6 +265,7 @@
   // Skia internally buffers commands and flushes them as necessary but there
   // are some cases where we need to force a flush.
   void FlushPendingSkiaOps();
+  bool HasExclusiveTextureAccess() const;
   int width() const;
   int height() const;
   SkColorSpace* color_space() const {
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index d312f21e4..e01779d 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -1515,14 +1515,9 @@
       canvas->scale(1.f / op->scale_adjustment.width(),
                     1.f / op->scale_adjustment.height());
     }
-    sk_sp<SkImage> sk_image;
-    if (op->image.IsTextureBacked()) {
-      sk_image = op->image.GetAcceleratedSkImage();
-      DCHECK(sk_image || !canvas->recordingContext());
-    }
-    if (!sk_image)
-      sk_image = op->image.GetSwSkImage();
-
+    auto sk_image = op->image.IsTextureBacked()
+                        ? op->image.GetAcceleratedSkImage()
+                        : op->image.GetSwSkImage();
     canvas->drawImage(sk_image.get(), op->left, op->top, &paint);
     return;
   }
@@ -1594,13 +1589,9 @@
   if (!params.image_provider) {
     SkRect adjusted_src = AdjustSrcRectForScale(op->src, op->scale_adjustment);
     flags->DrawToSk(canvas, [op, adjusted_src](SkCanvas* c, const SkPaint& p) {
-      sk_sp<SkImage> sk_image;
-      if (op->image.IsTextureBacked()) {
-        sk_image = op->image.GetAcceleratedSkImage();
-        DCHECK(sk_image || !c->recordingContext());
-      }
-      if (!sk_image)
-        sk_image = op->image.GetSwSkImage();
+      auto sk_image = op->image.IsTextureBacked()
+                          ? op->image.GetAcceleratedSkImage()
+                          : op->image.GetSwSkImage();
       c->drawImageRect(sk_image.get(), adjusted_src, op->dst, &p,
                        op->constraint);
     });
diff --git a/cc/scheduler/begin_frame_tracker.cc b/cc/scheduler/begin_frame_tracker.cc
index 0480a2c..2b8b164 100644
--- a/cc/scheduler/begin_frame_tracker.cc
+++ b/cc/scheduler/begin_frame_tracker.cc
@@ -18,9 +18,11 @@
 
 void BeginFrameTracker::Start(const viz::BeginFrameArgs& new_args) {
   // Trace the frame time being passed between BeginFrameTrackers.
-  TRACE_EVENT_FLOW_STEP0(
-      TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), "BeginFrameArgs",
-      new_args.frame_time.since_origin().InMicroseconds(), location_string_);
+  TRACE_EVENT_WITH_FLOW1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"),
+                         "BeginFrameArgs",
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
+                         new_args.frame_time.since_origin().InMicroseconds(),
+                         "location", location_string_);
 
   // Trace this specific begin frame tracker Start/Finish times.
   TRACE_EVENT_COPY_ASYNC_BEGIN2(
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 8f51ef9..7a73f48e 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -356,9 +356,9 @@
   }
 
   // Trace this begin frame time through the Chrome stack
-  TRACE_EVENT_FLOW_BEGIN0(
-      TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"),
-      "viz::BeginFrameArgs", args.frame_time.since_origin().InMicroseconds());
+  TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"),
+                         "viz::BeginFrameArgs", TRACE_EVENT_FLAG_FLOW_OUT,
+                         args.frame_time.since_origin().InMicroseconds());
 
   if (settings_.using_synchronous_renderer_compositor) {
     BeginImplFrameSynchronous(args);
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 8621f41..abd434e 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -311,7 +311,6 @@
     "//chrome/browser/download/android:file_provider_java",
     "//chrome/browser/download/android:java",
     "//chrome/browser/download/android:java_resources",
-    "//chrome/browser/engagement/android:java",
     "//chrome/browser/enterprise/util:java",
     "//chrome/browser/feedback/android:java",
     "//chrome/browser/flags:java",
@@ -455,6 +454,7 @@
     "//components/signin/core/browser:signin_enums_java",
     "//components/signin/core/browser/android:java",
     "//components/signin/public/android:java",
+    "//components/site_engagement/content/android:java",
     "//components/spellcheck/browser/android:java",
     "//components/strictmode/android:java",
     "//components/subresource_filter/android:java",
@@ -1066,7 +1066,6 @@
     "//chrome/browser/download/android:java",
     "//chrome/browser/download/internal/android:javatests",
     "//chrome/browser/endpoint_fetcher:java",
-    "//chrome/browser/engagement/android:java",
     "//chrome/browser/enterprise/util:java",
     "//chrome/browser/feedback/android:java",
     "//chrome/browser/flags:java",
@@ -1218,6 +1217,7 @@
     "//components/signin/core/browser/android:signin_java_test_support",
     "//components/signin/core/browser/android:signin_javatests",
     "//components/signin/public/android:java",
+    "//components/site_engagement/content/android:java",
     "//components/strictmode/android:javatests",
     "//components/sync/android:sync_java",
     "//components/sync/protocol:protocol_java",
@@ -3223,6 +3223,7 @@
     # Files under a feature's public/ dir are included in chrome_java's source
     # files, so include these files in chrome_jni_headers.
     "feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedImageFetchClient.java",
+    "feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedPersistentKeyValueCache.java",
     "feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedServiceBridge.java",
     "feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java",
     "java/src/org/chromium/chrome/browser/AfterStartupTaskUtils.java",
diff --git a/chrome/android/DEPS b/chrome/android/DEPS
index ace4f6d..086730b 100644
--- a/chrome/android/DEPS
+++ b/chrome/android/DEPS
@@ -66,6 +66,7 @@
   "+components/security_interstitials/content/android",
   "+components/signin/core/browser/android",
   "+components/signin/public/android",
+  "+components/site_engagement/content/android",
   "+components/spellcheck/browser",
   "+components/strictmode/android",
   "+components/subresource_filter/android",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 8265217..b910bb3 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -785,6 +785,7 @@
   "java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java",
   "java/src/org/chromium/chrome/browser/metrics/ActivityTabStartupMetricsTracker.java",
   "java/src/org/chromium/chrome/browser/metrics/BackgroundTaskMemoryMetricsEmitter.java",
+  "java/src/org/chromium/chrome/browser/metrics/LaunchCauseMetrics.java",
   "java/src/org/chromium/chrome/browser/metrics/LaunchMetrics.java",
   "java/src/org/chromium/chrome/browser/metrics/MainIntentBehaviorMetrics.java",
   "java/src/org/chromium/chrome/browser/metrics/PackageMetrics.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 6d313d23..42a97e3 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -269,6 +269,7 @@
   "javatests/src/org/chromium/chrome/browser/media/ui/MediaSessionTest.java",
   "javatests/src/org/chromium/chrome/browser/media/ui/PictureInPictureControllerTest.java",
   "javatests/src/org/chromium/chrome/browser/metrics/BackgroundMetricsTest.java",
+  "javatests/src/org/chromium/chrome/browser/metrics/LaunchCauseMetricsTest.java",
   "javatests/src/org/chromium/chrome/browser/metrics/MainIntentBehaviorMetricsIntegrationTest.java",
   "javatests/src/org/chromium/chrome/browser/metrics/PageLoadMetricsTest.java",
   "javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java",
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryIPHUtils.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryIPHUtils.java
index 60348b9..3c00cf44 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryIPHUtils.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryIPHUtils.java
@@ -6,6 +6,7 @@
 
 import android.view.View;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
@@ -41,6 +42,7 @@
                 tracker.notifyEvent(EventConstants.KEYBOARD_ACCESSORY_PASSWORD_AUTOFILLED);
                 return;
             case FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_FILLING_FEATURE:
+            case FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_OFFER_FEATURE:
                 tracker.notifyEvent(EventConstants.KEYBOARD_ACCESSORY_PAYMENT_AUTOFILLED);
                 return;
         }
@@ -74,25 +76,6 @@
     }
 
     /**
-     * Shows a help bubble pointing to the given view. It contains an appropriate text for the given
-     * feature. The help bubble will not be shown if the {@link Tracker} doesn't allow it anymore.
-     * This may happen for example: if it was shown too often, too many IPH were triggered this
-     * session or other config restrictions apply.
-     * @param feature A String identifying the IPH feature and its appropriate help text.
-     * @param view The {@link View} providing context and the Rect to which the bubble will point.
-     * @param rootView The {@link View} used to determine the maximal dimensions for the bubble.
-     */
-    static void showHelpBubble(String feature, View view, View rootView) {
-        TextBubble helpBubble = createBubble(feature, new ViewRectProvider(view), rootView);
-        if (helpBubble == null) return;
-        // To emphasize which chip is pointed to, set selected to true for the built-in highlight.
-        // Prefer ViewHighlighter for views without a LayerDrawable background.
-        view.setSelected(true);
-        helpBubble.addOnDismissListener(() -> { view.setSelected(false); });
-        helpBubble.show();
-    }
-
-    /**
      * Shows a help bubble pointing to the given rect. It contains an appropriate text for the given
      * feature. The help bubble will not be shown if the {@link Tracker} doesn't allow it anymore.
      * This may happen for example: if it was shown too often, too many IPH were triggered this
@@ -102,19 +85,66 @@
      * @param rootView The {@link View} used to determine the maximal dimensions for the bubble.
      */
     static void showHelpBubble(String feature, RectProvider rectProvider, View rootView) {
-        TextBubble helpBubble = createBubble(feature, rectProvider, rootView);
+        TextBubble helpBubble = createBubble(feature, rectProvider, rootView, null);
         if (helpBubble != null) helpBubble.show();
     }
 
+    /**
+     * Shows a help bubble pointing to the given view. It contains an appropriate text for the given
+     * feature. The help bubble will not be shown if the {@link Tracker} doesn't allow it anymore.
+     * This may happen for example: if it was shown too often, too many IPH were triggered this
+     * session or other config restrictions apply.
+     * @param feature A String identifying the IPH feature and its appropriate help text.
+     * @param helpText String that should be displayed within the IPH bubble.
+     * @param rectProvider The {@link RectProvider} providing bounds to which the bubble will point.
+     * @param rootView The {@link View} used to determine the maximal dimensions for the bubble.
+     */
+    static void showHelpBubble(
+            String feature, RectProvider rectProvider, View rootView, @Nullable String helpText) {
+        TextBubble helpBubble = createBubble(feature, rectProvider, rootView, helpText);
+        if (helpBubble != null) helpBubble.show();
+    }
+
+    /**
+     * Shows a help bubble pointing to the given view. It contains an appropriate text for the given
+     * feature. The help bubble will not be shown if the {@link Tracker} doesn't allow it anymore.
+     * This may happen for example: if it was shown too often, too many IPH were triggered this
+     * session or other config restrictions apply.
+     * @param feature A String identifying the IPH feature and its appropriate help text.
+     * @param helpText String that should be displayed within the IPH bubble.
+     * @param view The {@link View} providing context and the Rect to which the bubble will point.
+     * @param rootView The {@link View} used to determine the maximal dimensions for the bubble.
+     */
+    static void showHelpBubble(
+            String feature, View view, View rootView, @Nullable String helpText) {
+        TextBubble helpBubble =
+                createBubble(feature, new ViewRectProvider(view), rootView, helpText);
+        if (helpBubble == null) return;
+        // To emphasize which chip is pointed to, set selected to true for the built-in highlight.
+        // Prefer ViewHighlighter for views without a LayerDrawable background.
+        view.setSelected(true);
+        helpBubble.addOnDismissListener(() -> { view.setSelected(false); });
+        helpBubble.show();
+    }
+
     private static TextBubble createBubble(
-            String feature, RectProvider rectProvider, View rootView) {
+            String feature, RectProvider rectProvider, View rootView, @Nullable String helpText) {
         final Tracker tracker = getTrackerFromProfile();
         if (tracker == null) return null;
         if (!tracker.shouldTriggerHelpUI(feature)) return null; // This call records the IPH intent.
-        @StringRes
-        int helpText = getHelpTextForFeature(feature);
-        TextBubble helpBubble = new TextBubble(rootView.getContext(), rootView, helpText, helpText,
-                rectProvider, ChromeAccessibilityUtil.get().isAccessibilityEnabled());
+        TextBubble helpBubble;
+        // If the help text is provided, then use it directly to generate the text bubble.
+        if (helpText != null && !helpText.isEmpty()) {
+            helpBubble = new TextBubble(rootView.getContext(), rootView, helpText, helpText,
+                    /* showArrow= */ true, rectProvider,
+                    ChromeAccessibilityUtil.get().isAccessibilityEnabled());
+        } else {
+            @StringRes
+            int helpTextResourceId = getHelpTextForFeature(feature);
+            helpBubble = new TextBubble(rootView.getContext(), rootView, helpTextResourceId,
+                    helpTextResourceId, rectProvider,
+                    ChromeAccessibilityUtil.get().isAccessibilityEnabled());
+        }
         helpBubble.setDismissOnTouchInteraction(true);
         helpBubble.addOnDismissListener(() -> { tracker.dismissed(feature); });
         return helpBubble;
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryMediator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryMediator.java
index 027021c2..87fa588 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryMediator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryMediator.java
@@ -329,6 +329,10 @@
             return FeatureConstants.KEYBOARD_ACCESSORY_PASSWORD_FILLING_FEATURE;
         }
         if (containsCreditCardInfo(suggestion)) {
+            if (!suggestion.getItemTag().isEmpty()) {
+                // Prefer showing a linked cashback over the general IPH.
+                return FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_OFFER_FEATURE;
+            }
             return FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_FILLING_FEATURE;
         }
         if (containsAddressInfo(suggestion)) {
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryModernViewBinder.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryModernViewBinder.java
index 40fd157..62f9c5b 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryModernViewBinder.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryModernViewBinder.java
@@ -57,14 +57,26 @@
 
         @Override
         protected void bind(AutofillBarItem item, ChipView chipView) {
+            int iconId = item.getSuggestion().getIconId();
             if (item.getFeatureForIPH() != null) {
-                showHelpBubble(item.getFeatureForIPH(), chipView, mRootViewForIPH);
+                if (item.getFeatureForIPH().equals(
+                            FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_OFFER_FEATURE)
+                        && iconId != 0) {
+                    if (iconId != 0) {
+                        showHelpBubble(item.getFeatureForIPH(), chipView.getStartIconViewRect(),
+                                mRootViewForIPH, item.getSuggestion().getItemTag());
+                    } else {
+                        showHelpBubble(item.getFeatureForIPH(), chipView, mRootViewForIPH,
+                                item.getSuggestion().getItemTag());
+                    }
+                } else {
+                    showHelpBubble(item.getFeatureForIPH(), chipView, mRootViewForIPH, null);
+                }
             }
             chipView.getPrimaryTextView().setText(item.getSuggestion().getLabel());
             chipView.getSecondaryTextView().setText(item.getSuggestion().getSublabel());
             chipView.getSecondaryTextView().setVisibility(
                     item.getSuggestion().getSublabel().isEmpty() ? View.GONE : View.VISIBLE);
-            int iconId = item.getSuggestion().getIconId();
             chipView.setIcon(iconId != 0 ? iconId : ChipView.INVALID_ICON_ID, false);
             KeyboardAccessoryData.Action action = item.getAction();
             assert action != null : "Tried to bind item without action. Chose a wrong ViewHolder?";
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryModernViewTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryModernViewTest.java
index db5cfe1..d1328ea7 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryModernViewTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryModernViewTest.java
@@ -362,6 +362,33 @@
 
     @Test
     @MediumTest
+    public void testDismissesPaymentOfferEducationBubbleOnFilling() {
+        String itemTag = "Cashback linked";
+        AutofillBarItem itemWithIPH = new AutofillBarItem(
+                new AutofillSuggestion("Johnathan", "Smith", itemTag, R.drawable.ic_offer_tag_green,
+                        false, 70000, false, false, false),
+                new KeyboardAccessoryData.Action("", AUTOFILL_SUGGESTION, unused -> {}));
+        itemWithIPH.setFeatureForIPH(FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_OFFER_FEATURE);
+
+        TestTracker tracker = new TestTracker();
+        TrackerFactory.setTrackerForTests(tracker);
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mModel.set(VISIBLE, true);
+            mModel.get(BAR_ITEMS).set(new BarItem[] {itemWithIPH, createTabs()});
+        });
+
+        onViewWaiting(withText("Johnathan"));
+        waitForHelpBubble(withText(itemTag));
+        onView(withText("Johnathan")).perform(click());
+
+        assertThat(tracker.wasDismissed(), is(true));
+        assertThat(tracker.getLastEmittedEvent(),
+                is(EventConstants.KEYBOARD_ACCESSORY_PAYMENT_AUTOFILLED));
+    }
+
+    @Test
+    @MediumTest
     public void testNotifiesAboutPartiallyVisibleSuggestions() throws InterruptedException {
         // Ensure that the callback isn't triggered while all items are visible:
         AtomicInteger obfuscatedChildAt = new AtomicInteger(-1);
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index c282fa9..afb8a6e 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -221,6 +221,7 @@
     "//components/policy/android:policy_java",
     "//components/search_engines/android:java",
     "//components/signin/public/android:java",
+    "//components/site_engagement/content/android:java",
     "//content/public/android:content_java",
     "//content/public/android:content_java_resources",
     "//third_party/android_deps:android_support_v7_appcompat_java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabContext.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabContext.java
index e376e2d..18dbfb2 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabContext.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabContext.java
@@ -10,12 +10,12 @@
 import org.json.JSONException;
 import org.json.JSONObject;
 
-import org.chromium.chrome.browser.engagement.SiteEngagementService;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.state.CriticalPersistedTabData;
 import org.chromium.chrome.browser.tabmodel.TabModelFilter;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.components.site_engagement.SiteEngagementService;
 import org.chromium.content_public.browser.NavigationEntry;
 
 import java.util.ArrayList;
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedPersistentKeyValueCache.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedPersistentKeyValueCache.java
new file mode 100644
index 0000000..c93ba50
--- /dev/null
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedPersistentKeyValueCache.java
@@ -0,0 +1,49 @@
+// 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.
+
+package org.chromium.chrome.browser.feed.v2;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.base.Callback;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.chrome.browser.xsurface.PersistentKeyValueCache;
+
+/**
+ * Implementation of xsurface's PersistentKeyValueCache.
+ */
+@JNINamespace("feed")
+public class FeedPersistentKeyValueCache implements PersistentKeyValueCache {
+    @Override
+    public void lookup(byte[] key, ValueConsumer consumer) {
+        assert ThreadUtils.runningOnUiThread();
+        FeedPersistentKeyValueCacheJni.get().lookup(key, new Callback<byte[]>() {
+            @Override
+            public void onResult(byte[] result) {
+                consumer.run(result);
+            }
+        });
+    }
+
+    @Override
+    public void put(byte[] key, byte[] value, @Nullable Runnable onComplete) {
+        assert ThreadUtils.runningOnUiThread();
+        FeedPersistentKeyValueCacheJni.get().put(key, value, onComplete);
+    }
+
+    @Override
+    public void evict(byte[] key, @Nullable Runnable onComplete) {
+        assert ThreadUtils.runningOnUiThread();
+        FeedPersistentKeyValueCacheJni.get().evict(key, onComplete);
+    }
+
+    @NativeMethods
+    interface Natives {
+        void lookup(byte[] key, Object consumer);
+        void put(byte[] key, byte[] value, Runnable onComplete);
+        void evict(byte[] key, Runnable onComplete);
+    }
+}
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedProcessScopeDependencyProvider.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedProcessScopeDependencyProvider.java
index c767a3c2..8def531 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedProcessScopeDependencyProvider.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedProcessScopeDependencyProvider.java
@@ -21,6 +21,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.xsurface.ImageFetchClient;
+import org.chromium.chrome.browser.xsurface.PersistentKeyValueCache;
 import org.chromium.chrome.browser.xsurface.ProcessScopeDependencyProvider;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.signin.identitymanager.ConsentLevel;
@@ -34,6 +35,7 @@
 
     private Context mContext;
     private ImageFetchClient mImageFetchClient;
+    private FeedPersistentKeyValueCache mPersistentKeyValueCache;
     private LibraryResolver mLibraryResolver;
 
     @VisibleForTesting
@@ -42,6 +44,7 @@
     FeedProcessScopeDependencyProvider() {
         mContext = createFeedContext(ContextUtils.getApplicationContext());
         mImageFetchClient = new FeedImageFetchClient();
+        mPersistentKeyValueCache = new FeedPersistentKeyValueCache();
         if (BundleUtils.isIsolatedSplitInstalled(mContext, FEED_SPLIT_NAME)) {
             mLibraryResolver = (libName) -> {
                 return BundleUtils.getNativeLibraryPath(libName, FEED_SPLIT_NAME);
@@ -85,6 +88,11 @@
     }
 
     @Override
+    public PersistentKeyValueCache getPersistentKeyValueCache() {
+        return mPersistentKeyValueCache;
+    }
+
+    @Override
     public void logError(String tag, String format, Object... args) {
         Log.e(tag, format, args);
     }
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/FeedProcessScopeDependencyProviderNativeTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/FeedProcessScopeDependencyProviderNativeTest.java
new file mode 100644
index 0000000..d06e80d
--- /dev/null
+++ b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/FeedProcessScopeDependencyProviderNativeTest.java
@@ -0,0 +1,115 @@
+// 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.
+
+package org.chromium.chrome.browser.feed.v2;
+
+import androidx.test.filters.MediumTest;
+
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Criteria;
+import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.xsurface.PersistentKeyValueCache;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for FeedProcessScopeDependencyProvider. Uses ChromeTabbedActivityTestRule to test native
+ * code. Note, this class has a 'NativeTest' suffix to avoid collision with
+ * a junit test with the name 'FeedProcessScopeDependencyProviderTest'.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@Features.EnableFeatures(ChromeFeatureList.INTEREST_FEED_V2)
+public class FeedProcessScopeDependencyProviderNativeTest {
+    static final byte[] VALUE_1 = "one".getBytes();
+    static final byte[] VALUE_2 = "two".getBytes();
+
+    String toString(byte[] array) {
+        if (array == null) return "null";
+        return new String(array);
+    }
+
+    @Rule
+    public final ChromeTabbedActivityTestRule mActivityTestRule =
+            new ChromeTabbedActivityTestRule();
+
+    @Before
+    public void setUp() throws Exception {
+        mActivityTestRule.startMainActivityWithURL("about:blank");
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Feed"})
+    public void testPersistentKeyValueCachePutAndLookup() {
+        FeedProcessScopeDependencyProvider dependencyProvider =
+                new FeedProcessScopeDependencyProvider();
+        ArrayList<String> calls = new ArrayList<String>();
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            PersistentKeyValueCache cache = dependencyProvider.getPersistentKeyValueCache();
+            cache.put(VALUE_1, VALUE_2, () -> calls.add("put"));
+            cache.lookup(VALUE_1, (byte[] v) -> calls.add("lookup1 " + toString(v)));
+            cache.lookup(VALUE_2, (byte[] v) -> calls.add("lookup2 " + toString(v)));
+        });
+        CriteriaHelper.pollUiThread(() -> {
+            Criteria.checkThat(
+                    "Calls match", calls, Matchers.contains("put", "lookup1 two", "lookup2 null"));
+        });
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Feed"})
+    public void testPersistentKeyValueCacheEvict() {
+        FeedProcessScopeDependencyProvider dependencyProvider =
+                new FeedProcessScopeDependencyProvider();
+        ArrayList<String> calls = new ArrayList<String>();
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            PersistentKeyValueCache cache = dependencyProvider.getPersistentKeyValueCache();
+            cache.put(VALUE_1, VALUE_2, () -> calls.add("put"));
+            cache.evict(VALUE_1, () -> calls.add("evict"));
+            cache.lookup(VALUE_1, (byte[] v) -> calls.add("lookup " + toString(v)));
+        });
+
+        CriteriaHelper.pollUiThread(() -> {
+            Criteria.checkThat(
+                    "Calls match", calls, Matchers.contains("put", "evict", "lookup null"));
+        });
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Feed"})
+    public void testPersistentKeyValueCacheNullRunnables() {
+        // Verify put() and evict() accept null runnables.
+        FeedProcessScopeDependencyProvider dependencyProvider =
+                new FeedProcessScopeDependencyProvider();
+        ArrayList<String> calls = new ArrayList<String>();
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            PersistentKeyValueCache cache = dependencyProvider.getPersistentKeyValueCache();
+            cache.put(VALUE_1, VALUE_2, null);
+            cache.evict(VALUE_1, null);
+            cache.put(VALUE_1, VALUE_2, () -> calls.add("put"));
+        });
+
+        CriteriaHelper.pollUiThread(
+                () -> { Criteria.checkThat("Calls match", calls, Matchers.contains("put")); });
+    }
+}
diff --git a/chrome/android/feed/feed_java_sources.gni b/chrome/android/feed/feed_java_sources.gni
index fd330a0..3a9248e 100644
--- a/chrome/android/feed/feed_java_sources.gni
+++ b/chrome/android/feed/feed_java_sources.gni
@@ -47,6 +47,7 @@
   "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/CardMenuBottomSheetContent.java",
   "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedImageFetchClient.java",
   "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedListContentManager.java",
+  "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedPersistentKeyValueCache.java",
   "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedProcessScopeDependencyProvider.java",
   "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedServiceBridge.java",
   "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedSliceViewTracker.java",
@@ -734,6 +735,7 @@
 
 if (enable_feed_v2) {
   feed_test_java_sources += [
+    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/FeedProcessScopeDependencyProviderNativeTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/FeedV2NewTabPageTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/FeedV2TestHelper.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/TestFeedServer.java",
@@ -743,9 +745,10 @@
 feed_test_deps = []
 if (enable_feed_v1 || enable_feed_v2) {
   feed_test_deps += feed_deps + [
-                      "//chrome/browser/user_education:java",
-                      "//third_party/google-truth:google_truth_java",
-                      "//third_party/android_deps:guava_android_java",
                       "//chrome/browser/privacy:java",
+                      "//chrome/browser/user_education:java",
+                      "//chrome/browser/xsurface:java",
+                      "//third_party/android_deps:guava_android_java",
+                      "//third_party/google-truth:google_truth_java",
                     ]
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 9ccdb8e7..0b14a0c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -114,6 +114,7 @@
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.media.PictureInPictureController;
 import org.chromium.chrome.browser.metrics.ActivityTabStartupMetricsTracker;
+import org.chromium.chrome.browser.metrics.LaunchCauseMetrics;
 import org.chromium.chrome.browser.metrics.LaunchMetrics;
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
@@ -330,6 +331,8 @@
     @Nullable
     private StartupTabPreloader mStartupTabPreloader;
 
+    private LaunchCauseMetrics mLaunchCauseMetrics;
+
     // TODO(972867): Pull MenuOrKeyboardActionController out of ChromeActivity.
     private List<MenuOrKeyboardActionController.MenuOrKeyboardActionHandler> mMenuActionHandlers =
             new ArrayList<>();
@@ -727,6 +730,22 @@
         return mManualFillingComponent;
     }
 
+    /**
+     * TODO(mthiesse, https://crbug.com/1163961): Make this function abstract and have derived
+     * classes make their own.
+     * @return The {@link LaunchCauseMetrics} owned by this {@link ChromeActivity}.
+     */
+    protected LaunchCauseMetrics createLaunchCauseMetrics() {
+        return new LaunchCauseMetrics();
+    }
+
+    private LaunchCauseMetrics getLaunchCauseMetrics() {
+        if (mLaunchCauseMetrics == null) {
+            mLaunchCauseMetrics = createLaunchCauseMetrics();
+        }
+        return mLaunchCauseMetrics;
+    }
+
     @Override
     public AppMenuPropertiesDelegate createAppMenuPropertiesDelegate() {
         return new AppMenuPropertiesDelegateImpl(this, getActivityTabProvider(),
@@ -922,6 +941,7 @@
         super.onResumeWithNative();
         markSessionResume();
         RecordUserAction.record("MobileComeToForeground");
+        getLaunchCauseMetrics().recordLaunchCause();
 
         Tab tab = getActivityTab();
         if (tab != null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
index 7910154..645c5b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
@@ -160,6 +160,7 @@
      * @param index Index in the array where to place a new suggestion.
      * @param label First line of the suggestion.
      * @param sublabel Second line of the suggestion.
+     * @param itemTag The offer label of the suggestion.
      * @param iconId The resource ID for the icon associated with the suggestion, or 0 for no icon.
      * @param isIconAtStart {@code true} if {@param iconId} is displayed before {@param label}.
      * @param suggestionId Identifier for the suggestion type.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitChromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitChromeApplication.java
index 20f7360..02c41c9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitChromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitChromeApplication.java
@@ -9,19 +9,17 @@
 import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.SystemClock;
 
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
+import org.chromium.base.BundleUtils;
 import org.chromium.base.JNIUtils;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.metrics.RecordHistogram;
 
-import java.lang.reflect.Field;
-
 /**
  * Application class to use for Chrome when //chrome code is in an isolated split. This class will
  * perform any necessary initialization for non-browser processes without loading code from the
@@ -101,7 +99,10 @@
             // chromeContext will have the same ClassLoader as the base context, so no need to
             // replace the ClassLoaders here.
             if (!context.getClassLoader().equals(chromeContext.getClassLoader())) {
-                replaceClassLoader(this, chromeContext.getClassLoader());
+                // Replace the application Context's ClassLoader with the chrome ClassLoader,
+                // because the application ClassLoader is expected to be able to access all chrome
+                // classes.
+                BundleUtils.replaceClassLoader(this, chromeContext.getClassLoader());
                 JNIUtils.setClassLoader(chromeContext.getClassLoader());
             }
         });
@@ -143,24 +144,9 @@
                             return;
                         }
 
-                        replaceClassLoader(
+                        BundleUtils.replaceClassLoader(
                                 activity.getBaseContext(), activity.getClass().getClassLoader());
                     }
                 });
     }
-
-    private static void replaceClassLoader(Context baseContext, ClassLoader classLoader) {
-        while (baseContext instanceof ContextWrapper) {
-            baseContext = ((ContextWrapper) baseContext).getBaseContext();
-        }
-
-        try {
-            // baseContext should now be an instance of ContextImpl.
-            Field classLoaderField = baseContext.getClass().getDeclaredField("mClassLoader");
-            classLoaderField.setAccessible(true);
-            classLoaderField.set(baseContext, classLoader);
-        } catch (ReflectiveOperationException e) {
-            throw new RuntimeException("Error setting ClassLoader.", e);
-        }
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
index c1fd6ad..04c22be 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
@@ -840,9 +840,10 @@
      * bookmark ID will be returned.
      * @param title The title to be used for the reading list item.
      * @param url The URL of the reading list item.
-     * @return The bookmark ID created after saving the article to the reading list.
+     * @return The bookmark ID created after saving the article to the reading list, or null on
+     *         error.
      */
-    public BookmarkId addToReadingList(String title, String url) {
+    public @Nullable BookmarkId addToReadingList(String title, String url) {
         ThreadUtils.assertOnUiThread();
         assert title != null;
         assert url != null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
index f3ecd364..996db5c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
@@ -9,16 +9,12 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.base.ApplicationStatus;
 import org.chromium.base.Callback;
-import org.chromium.base.Log;
 import org.chromium.base.TimeUtilsJni;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.PostTask;
-import org.chromium.base.task.TaskTraits;
-import org.chromium.chrome.browser.crash.PureJavaExceptionReporter;
 import org.chromium.chrome.browser.performance_hints.PerformanceHintsObserver;
 import org.chromium.chrome.browser.share.LensUtils;
 import org.chromium.components.embedder_support.contextmenu.ContextMenuParams;
@@ -37,10 +33,6 @@
 public class ContextMenuHelper {
     private static Callback<RevampedContextMenuCoordinator> sMenuShownCallbackForTests;
 
-    private static final String TAG = "ContextMenuHelper";
-
-    private boolean mDismissedFromHere;
-
     private final WebContents mWebContents;
     private long mNativeContextMenuHelper;
 
@@ -72,8 +64,6 @@
     @CalledByNative
     private void destroy() {
         if (mCurrentContextMenu != null) {
-            Log.i(TAG, "Dismissing context menu " + mCurrentContextMenu);
-            mDismissedFromHere = true;
             mCurrentContextMenu.dismiss();
             mCurrentContextMenu = null;
         }
@@ -85,11 +75,6 @@
     @CalledByNative
     private void setPopulatorFactory(ContextMenuPopulatorFactory populatorFactory) {
         if (mCurrentContextMenu != null) {
-            // TODO(crbug.com/1154731): Clean the debugging statements once we figure out the cause
-            // of the crash.
-            Log.i(TAG, "Dismissing context menu " + mCurrentContextMenu);
-            mDismissedFromHere = true;
-
             mCurrentContextMenu.dismiss();
             mCurrentContextMenu = null;
         }
@@ -127,41 +112,7 @@
         mCurrentContextMenuParams = params;
         mWindow = windowAndroid;
         mCallback = (result) -> {
-            if (mCurrentPopulator == null) {
-                Log.i(TAG, "mCurrentPopulator was null when mCallback was called.");
-                Log.i(TAG, "mCurrentContextMenu is " + mCurrentContextMenu);
-                Log.i(TAG,
-                        "ContextMenuHelper is " + (mNativeContextMenuHelper == 0 ? "" : "NOT")
-                                + " destroyed.");
-                Log.i(TAG,
-                        "Context menu was "
-                                + (mCurrentContextMenu != null && mCurrentContextMenu.isDismissed()
-                                                ? ""
-                                                : "NOT")
-                                + " dismissed.");
-                Log.i(TAG, "Activity: " + mWindow.getActivity().get());
-                Log.i(TAG,
-                        "Activity state: "
-                                + ApplicationStatus.getStateForActivity(
-                                        mWindow.getActivity().get()));
-
-                Throwable throwable = new Throwable(
-                        "This is not a crash. See https://crbug.com/1153706 for details."
-                        + "\nmCurrentContextMenu is " + mCurrentContextMenu
-                        + "\nmCurrentPopulator was null when mCallback was called."
-                        + "\nContextMenuHelper is " + (mNativeContextMenuHelper == 0 ? "" : "NOT")
-                        + " destroyed."
-                        + "\nContext menu was "
-                        + (mCurrentContextMenu != null && mCurrentContextMenu.isDismissed() ? ""
-                                                                                            : "NOT")
-                        + " dismissed."
-                        + "\nContext menu was " + (mDismissedFromHere ? "" : "NOT")
-                        + " dismissed by ContextMenuHelper.");
-                PostTask.postTask(TaskTraits.BEST_EFFORT_MAY_BLOCK,
-                        () -> PureJavaExceptionReporter.reportJavaException(throwable));
-
-                return;
-            }
+            if (mCurrentPopulator == null) return;
 
             mSelectedItemBeforeDismiss = true;
             mCurrentPopulator.onItemSelected(result);
@@ -177,12 +128,6 @@
             }
         };
         mOnMenuClosed = () -> {
-            Log.i(TAG, "mCurrentPopulator was " + mCurrentPopulator + " when the menu closed.");
-            Log.i(TAG, "mCurrentContextMenu was " + mCurrentContextMenu + " when the menu closed.");
-            Log.i(TAG,
-                    "Activity: " + mWindow.getActivity().get() + ", activity state: "
-                            + ApplicationStatus.getStateForActivity(mWindow.getActivity().get()));
-
             recordTimeToTakeActionHistogram(mSelectedItemBeforeDismiss);
             mCurrentContextMenu = null;
             if (mCurrentNativeDelegate != null) {
@@ -221,9 +166,6 @@
         mCurrentContextMenu = menuCoordinator;
         mChipDelegate = mCurrentPopulator.getChipDelegate();
 
-        Log.i(TAG, "Created mCurrentContextMenu: " + mCurrentContextMenu);
-        Log.i(TAG, "Activity was " + mWindow.getActivity().get() + " when the menu was created.");
-
         // TODO(crbug/1158604): Remove leftover Lens dependencies.
         LensUtils.startLensConnectionIfNecessary(mIsIncognito);
         if (mChipDelegate != null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUi.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUi.java
index 70b5d72..7a896481 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUi.java
@@ -43,10 +43,4 @@
      * Dismiss the context menu.
      */
     void dismiss();
-
-    /**
-     * @return Whether the menu is dismissed. It may be dismissed but still be visible, e.g. hiding
-     *         with an animation.
-     */
-    boolean isDismissed();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java
index 2e2d706..970b922d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java
@@ -19,9 +19,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.AlertDialog;
 
-import org.chromium.base.ApplicationStatus;
 import org.chromium.base.Callback;
-import org.chromium.base.Log;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.performance_hints.PerformanceHintsObserver;
 import org.chromium.chrome.browser.performance_hints.PerformanceHintsObserver.PerformanceClass;
@@ -58,7 +56,6 @@
         int CONTEXT_MENU_ITEM_WITH_ICON_BUTTON = 3;
     }
 
-    private static final String TAG = "CMenuCoordinator";
     private static final int INVALID_ITEM_ID = -1;
 
     private WebContents mWebContents;
@@ -71,7 +68,6 @@
     private ContextMenuDialog mDialog;
     private Runnable mOnMenuClosed;
     private ContextMenuNativeDelegate mNativeDelegate;
-    private boolean mIsDismissed;
 
     /**
      * Constructor that also sets the content offset.
@@ -100,11 +96,6 @@
         dismissDialog();
     }
 
-    @Override
-    public boolean isDismissed() {
-        return mIsDismissed;
-    }
-
     // Shows the menu with chip.
     void displayMenuWithChip(final WindowAndroid window, WebContents webContents,
             ContextMenuParams params, List<Pair<Integer, ModelList>> items,
@@ -229,10 +220,6 @@
         // See https://crbug.com/990987
         if (activity.isFinishing() || activity.isDestroyed()) return;
 
-        Log.i(TAG,
-                "#clickItem called for menu " + this + ", activity: " + activity
-                        + ", activity state: " + ApplicationStatus.getStateForActivity(activity));
-
         onItemClicked.onResult((int) id);
         dismissDialog();
     }
@@ -291,7 +278,6 @@
     }
 
     private void dismissDialog() {
-        mIsDismissed = true;
         if (mWebContentsObserver != null) {
             mWebContentsObserver.destroy();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/LaunchCauseMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/LaunchCauseMetrics.java
new file mode 100644
index 0000000..219be093
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/LaunchCauseMetrics.java
@@ -0,0 +1,84 @@
+// 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.
+
+package org.chromium.chrome.browser.metrics;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.IntDef;
+
+import org.chromium.base.ApplicationState;
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.annotations.RemovableInRelease;
+import org.chromium.base.metrics.RecordHistogram;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Computes and records metrics for what caused Chrome to be launched.
+ */
+public class LaunchCauseMetrics {
+    // Static to avoid recording launch metrics when transitioning between Activities without
+    // Chrome leaving the foreground.
+    private static boolean sRecordedLaunchCause;
+
+    /* package */ static final String LAUNCH_CAUSE_HISTOGRAM =
+            "MobileStartup.Experimental.LaunchCause";
+
+    // These values are persisted in histograms. Please do not renumber. Append only.
+    @IntDef({LaunchCause.OTHER})
+    @Retention(RetentionPolicy.SOURCE)
+    /* package */ @interface LaunchCause {
+        int OTHER = 0;
+
+        // A histogram enum cannot have only 1 entry, so while developing this we need to lie.
+        int NUM_ENTRIES = 2;
+    }
+
+    public LaunchCauseMetrics() {
+        ApplicationStatus.registerApplicationStateListener(newState -> {
+            if (newState == ApplicationState.HAS_STOPPED_ACTIVITIES) {
+                reset();
+            }
+        });
+    }
+
+    /**
+     * Resets state used to compute launch cause when Chrome is backgrounded.
+     */
+    @CallSuper
+    protected void reset() {
+        sRecordedLaunchCause = false;
+    }
+
+    /**
+     * TODO(mthiesse, https://crbug.com/1163961): Make this function abstract.
+     *
+     * Computes and returns what the cause of the Chrome launch was.
+     */
+    protected @LaunchCause int computeLaunchCause() {
+        return LaunchCause.OTHER;
+    }
+
+    /**
+     * Called after Chrome has launched and all information necessary to compute why Chrome was
+     * launched is available.
+     *
+     * Records UMA metrics for what caused Chrome to launch.
+     */
+    public void recordLaunchCause() {
+        if (sRecordedLaunchCause) return;
+        sRecordedLaunchCause = true;
+
+        @LaunchCause
+        int cause = computeLaunchCause();
+        RecordHistogram.recordEnumeratedHistogram(
+                LAUNCH_CAUSE_HISTOGRAM, cause, LaunchCause.NUM_ENTRIES);
+    }
+
+    @RemovableInRelease
+    /* package */ static void resetForTests() {
+        sRecordedLaunchCause = false;
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/background_sync/PeriodicBackgroundSyncTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/background_sync/PeriodicBackgroundSyncTest.java
index a9347c6..34e29b1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/background_sync/PeriodicBackgroundSyncTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/background_sync/PeriodicBackgroundSyncTest.java
@@ -20,7 +20,6 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.background_sync.BackgroundSyncBackgroundTaskScheduler.BackgroundSyncTask;
-import org.chromium.chrome.browser.engagement.SiteEngagementService;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
@@ -29,6 +28,7 @@
 import org.chromium.chrome.test.util.browser.TabTitleObserver;
 import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
 import org.chromium.components.externalauth.ExternalAuthUtils;
+import org.chromium.components.site_engagement.SiteEngagementService;
 import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
 import org.chromium.content_public.browser.test.util.BackgroundSyncNetworkUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java
index 9442632..d39bd02 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java
@@ -17,11 +17,15 @@
 import org.chromium.base.test.UiThreadTest;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.RequiresRestart;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.util.BookmarkTestUtil;
+import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.bookmarks.BookmarkId;
+import org.chromium.components.bookmarks.BookmarkType;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 import java.util.ArrayList;
@@ -314,4 +318,22 @@
                         + "over partner bookmarks",
                 expectedSearchResults, searchResults);
     }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    @RequiresRestart("Reading list glue code uses the flag to load a keyed service.")
+    @Features.EnableFeatures({ChromeFeatureList.READ_LATER})
+    public void testAddToReadingList() {
+        Assert.assertNull("Should return null for non http/https URLs.",
+                mBookmarkBridge.addToReadingList("a", "chrome://flags"));
+        BookmarkId readingListId = mBookmarkBridge.addToReadingList("a", "https://www.google.com/");
+        Assert.assertNotNull(readingListId);
+        Assert.assertEquals(BookmarkType.READING_LIST, readingListId.getType());
+        BookmarkItem readingListItem =
+                mBookmarkBridge.getReadingListItem("https://www.google.com/");
+        Assert.assertNotNull(readingListItem);
+        Assert.assertEquals("https://www.google.com/", readingListItem.getUrl());
+        Assert.assertEquals("a", readingListItem.getTitle());
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/LaunchCauseMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/LaunchCauseMetricsTest.java
new file mode 100644
index 0000000..5c45241
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/LaunchCauseMetricsTest.java
@@ -0,0 +1,96 @@
+// 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.
+
+package org.chromium.chrome.browser.metrics;
+
+import android.app.Activity;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
+
+import org.chromium.base.ActivityState;
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.UiThreadTest;
+import org.chromium.base.test.util.Batch;
+import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
+
+/**
+ * Tests basic functionality of LaunchCauseMetrics.
+ */
+@RunWith(BaseJUnit4ClassRunner.class)
+@Batch(Batch.UNIT_TESTS)
+public final class LaunchCauseMetricsTest {
+    @Mock
+    private Activity mActivity;
+
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+
+    @Before
+    public void setUp() {
+        NativeLibraryTestUtils.loadNativeLibraryNoBrowserProcess();
+    }
+
+    @After
+    public void tearDown() {
+        ApplicationStatus.destroyForJUnitTests();
+        LaunchCauseMetrics.resetForTests();
+    }
+
+    private static int histogramCountForValue(int value) {
+        return RecordHistogram.getHistogramValueCountForTesting(
+                LaunchCauseMetrics.LAUNCH_CAUSE_HISTOGRAM, value);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testRecordsOncePerLaunch() throws Throwable {
+        int count = histogramCountForValue(LaunchCauseMetrics.LaunchCause.OTHER);
+        LaunchCauseMetrics metrics = new LaunchCauseMetrics();
+        metrics.recordLaunchCause();
+        count++;
+        Assert.assertEquals(count, histogramCountForValue(LaunchCauseMetrics.LaunchCause.OTHER));
+
+        ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.CREATED);
+        ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
+        ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
+        metrics.recordLaunchCause();
+        Assert.assertEquals(count, histogramCountForValue(LaunchCauseMetrics.LaunchCause.OTHER));
+
+        ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.PAUSED);
+        metrics.recordLaunchCause();
+        Assert.assertEquals(count, histogramCountForValue(LaunchCauseMetrics.LaunchCause.OTHER));
+
+        ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STOPPED);
+        metrics.recordLaunchCause();
+        count++;
+        Assert.assertEquals(count, histogramCountForValue(LaunchCauseMetrics.LaunchCause.OTHER));
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testRecordsOnceWithMultipleInstances() throws Throwable {
+        int count = histogramCountForValue(LaunchCauseMetrics.LaunchCause.OTHER);
+        LaunchCauseMetrics metrics = new LaunchCauseMetrics();
+        metrics.recordLaunchCause();
+        count++;
+        Assert.assertEquals(count, histogramCountForValue(LaunchCauseMetrics.LaunchCause.OTHER));
+        new LaunchCauseMetrics().recordLaunchCause();
+        Assert.assertEquals(count, histogramCountForValue(LaunchCauseMetrics.LaunchCause.OTHER));
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
index 11edd12..a3750552 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
@@ -37,7 +37,6 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.UserActionTester;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.engagement.SiteEngagementService;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.permissions.PermissionTestRule;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -49,6 +48,7 @@
 import org.chromium.components.browser_ui.widget.RoundedIconGenerator;
 import org.chromium.components.content_settings.ContentSettingValues;
 import org.chromium.components.permissions.PermissionDialogController;
+import org.chromium.components.site_engagement.SiteEngagementService;
 import org.chromium.components.url_formatter.SchemeDisplay;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.components.user_prefs.UserPrefs;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/portals/PortalsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/portals/PortalsTest.java
index 39ae4ec2..6a1e6dfa 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/portals/PortalsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/portals/PortalsTest.java
@@ -40,7 +40,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.banners.AppBannerManager;
-import org.chromium.chrome.browser.engagement.SiteEngagementService;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.history.BrowsingHistoryBridge;
 import org.chromium.chrome.browser.history.HistoryItem;
@@ -54,6 +53,7 @@
 import org.chromium.chrome.test.util.browser.webapps.WebappTestPage;
 import org.chromium.components.location.LocationUtils;
 import org.chromium.components.permissions.PermissionDialogController;
+import org.chromium.components.site_engagement.SiteEngagementService;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.test.util.Coordinates;
 import org.chromium.content_public.browser.test.util.DOMUtils;
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 424233c1..5ea8be8 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-89.0.4384.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-89.0.4385.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/os_settings_search_tag_strings.grdp b/chrome/app/os_settings_search_tag_strings.grdp
index 1e5511e..1c28610 100644
--- a/chrome/app/os_settings_search_tag_strings.grdp
+++ b/chrome/app/os_settings_search_tag_strings.grdp
@@ -1174,6 +1174,12 @@
   <message name="IDS_OS_SETTINGS_TAG_ABOUT_TERMS_OF_SERVICE" desc="Text for search result item which, when clicked, navigates the user to 'About Chrome OS' settings, with a link to the Terms of Service.">
     Terms of Service
   </message>
+  <message name="IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS" desc="Text for search result item which, when clicked, navigates the user to 'Diagnostics' settings, with a link which opens the Diagnostics App. Alternate phrase for 'Troubleshooting'">
+    Diagnostics
+  </message>
+  <message name="IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT1" desc="Text for search result item which, when clicked, navigates the user to 'Diagnostics' settings, with a link which opens the Diagnostics App. Alternate phrase for 'Diagnostics'">
+    Troubleshooting
+  </message>
 
   <!-- On Startup section. -->
   <message name="IDS_OS_SETTINGS_TAG_ON_STARTUP" desc="Text for search result item which, when clicked, navigates the user to On Startup settings, with a radio group to configure the restore apps and pages options">
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS.png.sha1
new file mode 100644
index 0000000..4e27acd1
--- /dev/null
+++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS.png.sha1
@@ -0,0 +1 @@
+03b9e41a8763c919cf065f6fcd5c2e19d8557712
\ No newline at end of file
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT1.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT1.png.sha1
new file mode 100644
index 0000000..1eed646
--- /dev/null
+++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT1.png.sha1
@@ -0,0 +1 @@
+b2d89c3e093f3bf2538e9acba7463616a55ded65
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 06b76986..11b36fa 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -180,6 +180,9 @@
   <message name="IDS_SETTINGS_ABOUT_PAGE_LAST_UPDATE_MESSAGE" desc="Message shown on the top level about page to inform the user that this device will no longer receive latest software updates.">
     This is the last automatic software and security update for this <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>. To get future updates, upgrade to a newer model. <ph name="LINK_BEGIN">&lt;a target="_blank" href="$2<ex>https://google.com/</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
   </message>
+  <message name="IDS_SETTINGS_ABOUT_PAGE_DIAGNOSTICS" desc="Text of the button which allows the user to diagnose their device.">
+    Diagnostics
+  </message>
 
   <!-- People (OS settings) -->
   <message name="IDS_OS_SETTINGS_PEOPLE" desc="Name of a section in the OS settings page." meaning="People and their accounts.">
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ABOUT_PAGE_DIAGNOSTICS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ABOUT_PAGE_DIAGNOSTICS.png.sha1
new file mode 100644
index 0000000..3d7337b7
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ABOUT_PAGE_DIAGNOSTICS.png.sha1
@@ -0,0 +1 @@
+20e6ba8d611ab6b26e291ac9482522da01aa9f1f
\ No newline at end of file
diff --git a/chrome/app/profiles_strings.grdp b/chrome/app/profiles_strings.grdp
index cc6c4d7..c9e1d224 100644
--- a/chrome/app/profiles_strings.grdp
+++ b/chrome/app/profiles_strings.grdp
@@ -716,6 +716,9 @@
     <message name="IDS_PROFILE_PICKER_PROFILE_CARD_NEEDS_SIGNIN_PROMPT" desc="Text on the profile card signaling that the user must sign-in in order to open a profile">
       Sign in
     </message>
+    <message name="IDS_PROFILE_PICKER_PROFILE_CARD_LABEL" desc="Label for a profile card button that opens a new profile">
+      Open <ph name="PROFILE_NAME">$1<ex>User</ex></ph> profile
+    </message>
     <message name="IDS_PROFILE_PICKER_PROFILE_MENU_BUTTON_NAME" desc="Text to be spoken when the focus is set to the menu button of the profile card on the picker main screen or shown on hover.">
       Customize your profile, including its name
     </message>
diff --git a/chrome/app/profiles_strings_grdp/IDS_PROFILE_PICKER_PROFILE_CARD_LABEL.png.sha1 b/chrome/app/profiles_strings_grdp/IDS_PROFILE_PICKER_PROFILE_CARD_LABEL.png.sha1
new file mode 100644
index 0000000..d12fb547
--- /dev/null
+++ b/chrome/app/profiles_strings_grdp/IDS_PROFILE_PICKER_PROFILE_CARD_LABEL.png.sha1
@@ -0,0 +1 @@
+eea50f2911664882a4492bf72f588a4821c18315
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index fa360b36..583b8b1 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -321,8 +321,6 @@
     "component_updater/games_component_installer.h",
     "component_updater/mei_preload_component_installer.cc",
     "component_updater/mei_preload_component_installer.h",
-    "component_updater/optimization_hints_component_installer.cc",
-    "component_updater/optimization_hints_component_installer.h",
     "component_updater/origin_trials_component_installer.cc",
     "component_updater/origin_trials_component_installer.h",
     "component_updater/pepper_flash_component_installer.cc",
@@ -479,10 +477,6 @@
     "engagement/important_sites_util.h",
     "engagement/site_engagement_helper.cc",
     "engagement/site_engagement_helper.h",
-    "engagement/site_engagement_observer.cc",
-    "engagement/site_engagement_observer.h",
-    "engagement/site_engagement_service.cc",
-    "engagement/site_engagement_service.h",
     "engagement/site_engagement_service_factory.cc",
     "engagement/site_engagement_service_factory.h",
     "enterprise/browser_management/browser_management_service.cc",
@@ -512,6 +506,8 @@
     "favicon/large_icon_service_factory.h",
     "feature_engagement/tracker_factory.cc",
     "feature_engagement/tracker_factory.h",
+    "federated_learning/floc_eligibility_observer.cc",
+    "federated_learning/floc_eligibility_observer.h",
     "federated_learning/floc_event_logger.cc",
     "federated_learning/floc_event_logger.h",
     "federated_learning/floc_id_provider.h",
@@ -997,6 +993,8 @@
     "page_load_metrics/observers/aborts_page_load_metrics_observer.h",
     "page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc",
     "page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h",
+    "page_load_metrics/observers/ad_metrics/floc_page_load_metrics_observer.cc",
+    "page_load_metrics/observers/ad_metrics/floc_page_load_metrics_observer.h",
     "page_load_metrics/observers/ad_metrics/frame_data.cc",
     "page_load_metrics/observers/ad_metrics/frame_data.h",
     "page_load_metrics/observers/ad_metrics/page_ad_density_tracker.cc",
@@ -2717,6 +2715,7 @@
       "android/feed/v2/background_refresh_task.cc",
       "android/feed/v2/background_refresh_task.h",
       "android/feed/v2/feed_image_fetch_client.cc",
+      "android/feed/v2/feed_persistent_key_value_cache.cc",
       "android/feed/v2/feed_service_bridge.cc",
       "android/feed/v2/feed_service_bridge.h",
       "android/feed/v2/feed_service_factory.cc",
@@ -3028,8 +3027,6 @@
       "download/android/service/download_task_scheduler.h",
       "download/android/string_utils.cc",
       "download/download_crx_util_android.cc",
-      "engagement/site_engagement_service_android.cc",
-      "engagement/site_engagement_service_android.h",
       "enterprise/util/android_enterprise_info.cc",
       "enterprise/util/android_enterprise_info.h",
       "file_select_helper_contacts_android.cc",
@@ -3372,7 +3369,6 @@
       "//chrome/browser/share",
     ]
 
-    deps += [ "//chrome/browser/engagement/android:jni_headers" ]
     deps -= [ "//components/storage_monitor" ]
 
     if (enable_supervised_users) {
@@ -5985,9 +5981,7 @@
     deps += [
       "//chrome/common:offline_page_auto_fetcher_mojom",
       "//components/offline_pages/content/background_loader",
-      "//components/offline_pages/content/renovations",
       "//components/offline_pages/core/downloads:offline_pages_ui_adapter",
-      "//components/offline_pages/core/renovations",
       "//components/offline_pages/core/request_header",
     ]
 
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 6c0185c6..e2012e9 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3488,12 +3488,6 @@
      flag_descriptions::kSystemKeyboardLockDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kSystemKeyboardLock)},
 #if defined(OS_ANDROID)
-    {"offline-pages-load-signal-collecting",
-     flag_descriptions::kOfflinePagesLoadSignalCollectingName,
-     flag_descriptions::kOfflinePagesLoadSignalCollectingDescription,
-     kOsAndroid,
-     FEATURE_VALUE_TYPE(
-         offline_pages::kOfflinePagesLoadSignalCollectingFeature)},
     {"offline-pages-live-page-sharing",
      flag_descriptions::kOfflinePagesLivePageSharingName,
      flag_descriptions::kOfflinePagesLivePageSharingDescription, kOsAndroid,
@@ -3514,16 +3508,6 @@
      kOsAndroid,
      FEATURE_VALUE_TYPE(
          offline_pages::kOfflinePagesDescriptivePendingStatusFeature)},
-    {"offline-pages-resource-based-snapshot",
-     flag_descriptions::kOfflinePagesResourceBasedSnapshotName,
-     flag_descriptions::kOfflinePagesResourceBasedSnapshotDescription,
-     kOsAndroid,
-     FEATURE_VALUE_TYPE(
-         offline_pages::kOfflinePagesResourceBasedSnapshotFeature)},
-    {"offline-pages-renovations",
-     flag_descriptions::kOfflinePagesRenovationsName,
-     flag_descriptions::kOfflinePagesRenovationsDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(offline_pages::kOfflinePagesRenovationsFeature)},
     {"offline-pages-in-downloads-home-open-in-cct",
      flag_descriptions::kOfflinePagesInDownloadHomeOpenInCctName,
      flag_descriptions::kOfflinePagesInDownloadHomeOpenInCctDescription,
diff --git a/chrome/browser/accessibility/accessibility_extension_api_browsertest.cc b/chrome/browser/accessibility/accessibility_extension_api_browsertest.cc
index 34bdbc1c1..3e8e1f26 100644
--- a/chrome/browser/accessibility/accessibility_extension_api_browsertest.cc
+++ b/chrome/browser/accessibility/accessibility_extension_api_browsertest.cc
@@ -17,7 +17,16 @@
 
 namespace extensions {
 
-using AccessibilityPrivateApiTest = ExtensionApiTest;
+class AccessibilityPrivateApiTest : public ExtensionApiTest {
+ public:
+  void SetUp() override {
+    scoped_feature_list_.InitAndDisableFeature(
+        ::features::kSelectToSpeakNavigationControl);
+    ExtensionApiTest::SetUp();
+  }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
 
 IN_PROC_BROWSER_TEST_F(AccessibilityPrivateApiTest, SendSyntheticKeyEvent) {
   ASSERT_TRUE(RunExtensionSubtest("accessibility_private/",
diff --git a/chrome/browser/android/bookmarks/bookmark_bridge.cc b/chrome/browser/android/bookmarks/bookmark_bridge.cc
index 52ef5096..048b4ce7 100644
--- a/chrome/browser/android/bookmarks/bookmark_bridge.cc
+++ b/chrome/browser/android/bookmarks/bookmark_bridge.cc
@@ -828,8 +828,9 @@
   const BookmarkNode* node = reading_list_manager_->Add(
       GURL(base::android::ConvertJavaStringToUTF16(env, j_url)),
       base::android::ConvertJavaStringToUTF8(env, j_title));
-  DCHECK(node);
-  return JavaBookmarkIdCreateBookmarkId(env, node->id(), GetBookmarkType(node));
+  return node ? JavaBookmarkIdCreateBookmarkId(env, node->id(),
+                                               GetBookmarkType(node))
+              : ScopedJavaLocalRef<jobject>();
 }
 
 ScopedJavaLocalRef<jobject> BookmarkBridge::GetReadingListItem(
diff --git a/chrome/browser/android/feed/v2/feed_persistent_key_value_cache.cc b/chrome/browser/android/feed/v2/feed_persistent_key_value_cache.cc
new file mode 100644
index 0000000..ca8b363a9
--- /dev/null
+++ b/chrome/browser/android/feed/v2/feed_persistent_key_value_cache.cc
@@ -0,0 +1,111 @@
+// 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 <memory>
+
+#include "base/android/callback_android.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "chrome/android/chrome_jni_headers/FeedPersistentKeyValueCache_jni.h"
+#include "chrome/browser/android/feed/v2/feed_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "components/feed/core/v2/public/feed_service.h"
+#include "components/feed/core/v2/public/feed_stream_api.h"
+#include "components/feed/core/v2/public/persistent_key_value_store.h"
+
+namespace feed {
+namespace {
+using base::android::JavaParamRef;
+
+std::string JavaByteArrayToString(
+    JNIEnv* env,
+    const base::android::JavaRef<jbyteArray>& byte_array) {
+  std::string result;
+  base::android::JavaByteArrayToString(env, byte_array, &result);
+  return result;
+}
+
+void OnLookupFinished(JNIEnv* env,
+                      base::android::ScopedJavaGlobalRef<jobject> callback,
+                      PersistentKeyValueStore::Result result) {
+  base::android::ScopedJavaLocalRef<jbyteArray> j_result;
+  if (result.get_result) {
+    j_result = base::android::ToJavaByteArray(env, *result.get_result);
+  }
+  base::android::RunObjectCallbackAndroid(callback, j_result);
+}
+
+void CallRunnable(base::android::ScopedJavaGlobalRef<jobject> runnable,
+                  PersistentKeyValueStore::Result result) {
+  if (runnable)
+    base::android::RunRunnableAndroid(runnable);
+}
+
+PersistentKeyValueStore* GetStore() {
+  Profile* profile = ProfileManager::GetLastUsedProfile();
+  if (!profile)
+    return nullptr;
+
+  FeedService* feed_service = FeedServiceFactory::GetForBrowserContext(profile);
+  if (!feed_service)
+    return nullptr;
+
+  return feed_service->GetStream()->GetPersistentKeyValueStore();
+}
+
+}  // namespace
+
+void JNI_FeedPersistentKeyValueCache_Lookup(
+    JNIEnv* env,
+    const JavaParamRef<jbyteArray>& j_key,
+    const JavaParamRef<jobject>& j_response_callback) {
+  base::android::ScopedJavaGlobalRef<jobject> callback(j_response_callback);
+
+  PersistentKeyValueStore* store = GetStore();
+  if (!store) {
+    OnLookupFinished(env, std::move(callback), {});
+    return;
+  }
+  return store->Get(
+      JavaByteArrayToString(env, j_key),
+      base::BindOnce(&OnLookupFinished, env, std::move(callback)));
+}
+
+void JNI_FeedPersistentKeyValueCache_Put(
+    JNIEnv* env,
+    const JavaParamRef<jbyteArray>& j_key,
+    const JavaParamRef<jbyteArray>& j_value,
+    const JavaParamRef<jobject>& j_runnable) {
+  base::android::ScopedJavaGlobalRef<jobject> callback(j_runnable);
+
+  PersistentKeyValueStore* store = GetStore();
+  if (!store) {
+    base::android::RunRunnableAndroid(j_runnable);
+    return;
+  }
+  return store->Put(
+      JavaByteArrayToString(env, j_key), JavaByteArrayToString(env, j_value),
+      base::BindOnce(&CallRunnable,
+                     base::android::ScopedJavaGlobalRef<jobject>(j_runnable)));
+}
+
+void JNI_FeedPersistentKeyValueCache_Evict(
+    JNIEnv* env,
+    const JavaParamRef<jbyteArray>& j_key,
+    const JavaParamRef<jobject>& j_runnable) {
+  base::android::ScopedJavaGlobalRef<jobject> callback(j_runnable);
+
+  PersistentKeyValueStore* store = GetStore();
+  if (!store) {
+    base::android::RunRunnableAndroid(j_runnable);
+    return;
+  }
+  return store->Delete(
+      JavaByteArrayToString(env, j_key),
+      base::BindOnce(&CallRunnable,
+                     base::android::ScopedJavaGlobalRef<jobject>(j_runnable)));
+}
+
+}  // namespace feed
diff --git a/chrome/browser/android/feed/v2/feed_service_factory.cc b/chrome/browser/android/feed/v2/feed_service_factory.cc
index 472d37b..a19b022 100644
--- a/chrome/browser/android/feed/v2/feed_service_factory.cc
+++ b/chrome/browser/android/feed/v2/feed_service_factory.cc
@@ -23,6 +23,7 @@
 #include "chrome/common/chrome_version.h"
 #include "components/background_task_scheduler/background_task_scheduler_factory.h"
 #include "components/feed/buildflags.h"
+#include "components/feed/core/proto/v2/keyvalue_store.pb.h"
 #include "components/feed/core/proto/v2/store.pb.h"
 #include "components/feed/core/v2/public/feed_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -129,6 +130,9 @@
       storage_partition->GetProtoDatabaseProvider()->GetDB<feedstore::Record>(
           leveldb_proto::ProtoDbType::FEED_STREAM_DATABASE,
           feed_dir.AppendASCII("streamdb"), background_task_runner),
+      storage_partition->GetProtoDatabaseProvider()->GetDB<feedkvstore::Entry>(
+          leveldb_proto::ProtoDbType::FEED_KEY_VALUE_DATABASE,
+          feed_dir.AppendASCII("keyvaldb"), background_task_runner),
       identity_manager,
       HistoryServiceFactory::GetForProfile(profile,
                                            ServiceAccessType::IMPLICIT_ACCESS),
diff --git a/chrome/browser/android/metrics/launch_metrics.cc b/chrome/browser/android/metrics/launch_metrics.cc
index c5f69b0..759b28843 100644
--- a/chrome/browser/android/metrics/launch_metrics.cc
+++ b/chrome/browser/android/metrics/launch_metrics.cc
@@ -7,9 +7,9 @@
 #include "base/time/time.h"
 #include "chrome/android/chrome_jni_headers/LaunchMetrics_jni.h"
 #include "chrome/browser/banners/app_banner_settings_helper.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/prefs/pref_metrics_service.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/webapps/android/shortcut_info.h"
 #include "content/public/browser/web_contents.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
diff --git a/chrome/browser/android/usage_stats/usage_stats_database.cc b/chrome/browser/android/usage_stats/usage_stats_database.cc
index bddd3e3..bb9bdb10 100644
--- a/chrome/browser/android/usage_stats/usage_stats_database.cc
+++ b/chrome/browser/android/usage_stats/usage_stats_database.cc
@@ -219,9 +219,8 @@
     return;
   }
 
-  // TODO(crbug/927655): If/when leveldb_proto adds an UpdateEntriesInRange
-  // function, we should consolidate these two proto_db_ calls into a single
-  // call.
+  // If leveldb_proto adds a DeleteEntriesInRange function, these two proto_db_
+  // calls could be consolidated into a single call (crbug.com/939136).
 
   // Load all WebsiteEvents where the timestamp is in the specified range.
   // Function accepts a half-open range [startTime, endTime) as input, but the
diff --git a/chrome/browser/autofill/automated_tests/cache_replayer.cc b/chrome/browser/autofill/automated_tests/cache_replayer.cc
index 6470a98..3b63617 100644
--- a/chrome/browser/autofill/automated_tests/cache_replayer.cc
+++ b/chrome/browser/autofill/automated_tests/cache_replayer.cc
@@ -15,6 +15,7 @@
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/json/json_reader.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -215,17 +216,15 @@
 // that all the response body data is utilized.
 std::string GetStringFromDataElements(
     const std::vector<network::DataElement>* data_elements) {
-  network::DataElement unified_data_element;
-  unified_data_element.SetToEmptyBytes();
-  for (auto it = data_elements->begin(); it != data_elements->end(); ++it) {
-    unified_data_element.AppendBytes(it->bytes(), it->length());
+  std::string result;
+  for (const network::DataElement& element : *data_elements) {
+    DCHECK_EQ(element.type(), network::mojom::DataElementType::kBytes);
+    // Provide the length of the bytes explicitly, not to rely on the null
+    // termination.
+    result.append(element.bytes(),
+                  base::checked_cast<size_t>(element.length()));
   }
-
-  // Using the std::string constructor with length ensures that we don't rely
-  // on having a termination character to delimit the string. This is the
-  // safest approach.
-  return std::string(unified_data_element.bytes(),
-                     unified_data_element.length());
+  return result;
 }
 
 // Gets Query request proto content from HTTP POST body.
diff --git a/chrome/browser/background_sync/background_sync_controller_impl_unittest.cc b/chrome/browser/background_sync/background_sync_controller_impl_unittest.cc
index 2c3d2d19..8d2459a 100644
--- a/chrome/browser/background_sync/background_sync_controller_impl_unittest.cc
+++ b/chrome/browser/background_sync/background_sync_controller_impl_unittest.cc
@@ -10,13 +10,13 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
 #include "chrome/browser/background_sync/background_sync_delegate_impl.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_database_params.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/test/test_history_database.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/browser/background_sync_parameters.h"
 #include "content/public/browser/background_sync_registration.h"
diff --git a/chrome/browser/background_sync/background_sync_delegate_impl.cc b/chrome/browser/background_sync/background_sync_delegate_impl.cc
index 7b16a18f..dc669f6d 100644
--- a/chrome/browser/background_sync/background_sync_delegate_impl.cc
+++ b/chrome/browser/background_sync/background_sync_delegate_impl.cc
@@ -5,10 +5,10 @@
 #include "chrome/browser/background_sync/background_sync_delegate_impl.h"
 
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/metrics/ukm_background_recorder_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/background_sync_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
diff --git a/chrome/browser/background_sync/background_sync_delegate_impl.h b/chrome/browser/background_sync/background_sync_delegate_impl.h
index 50dc9e7..74b3c8ae 100644
--- a/chrome/browser/background_sync/background_sync_delegate_impl.h
+++ b/chrome/browser/background_sync/background_sync_delegate_impl.h
@@ -8,8 +8,8 @@
 #include <set>
 
 #include "build/build_config.h"
-#include "chrome/browser/engagement/site_engagement_observer.h"
 #include "components/background_sync/background_sync_delegate.h"
+#include "components/site_engagement/content/site_engagement_observer.h"
 #include "url/origin.h"
 
 class Profile;
diff --git a/chrome/browser/banners/android/BUILD.gn b/chrome/browser/banners/android/BUILD.gn
index f2c8bfd..8fac3ef 100644
--- a/chrome/browser/banners/android/BUILD.gn
+++ b/chrome/browser/banners/android/BUILD.gn
@@ -60,7 +60,6 @@
     "//chrome/android:chrome_java",
     "//chrome/android:chrome_test_java",
     "//chrome/android:chrome_test_util_java",
-    "//chrome/browser/engagement/android:java",
     "//chrome/browser/flags:java",
     "//chrome/browser/profiles/android:java",
     "//chrome/browser/tab:java",
@@ -72,6 +71,7 @@
     "//components/infobars/android:java",
     "//components/signin/core/browser/android:java",
     "//components/signin/core/browser/android:signin_java_test_support",
+    "//components/site_engagement/content/android:java",
     "//components/webapps/android:java",
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
diff --git a/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java b/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
index 8deaf41..4c74c64 100644
--- a/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
+++ b/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
@@ -64,7 +64,6 @@
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
-import org.chromium.chrome.browser.engagement.SiteEngagementService;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -92,6 +91,7 @@
 import org.chromium.components.infobars.InfoBarUiItem;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
 import org.chromium.components.signin.test.util.FakeAccountManagerFacade;
+import org.chromium.components.site_engagement.SiteEngagementService;
 import org.chromium.components.webapps.installable.InstallableAmbientBadgeInfoBar;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.WebContents;
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc
index dadee4d..4038272c 100644
--- a/chrome/browser/banners/app_banner_manager.cc
+++ b/chrome/browser/banners/app_banner_manager.cc
@@ -18,9 +18,9 @@
 #include "base/time/time.h"
 #include "chrome/browser/banners/app_banner_metrics.h"
 #include "chrome/browser/banners/app_banner_settings_helper.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/webapps/installable/installable_data.h"
 #include "components/webapps/installable/installable_manager.h"
 #include "components/webapps/installable/installable_metrics.h"
diff --git a/chrome/browser/banners/app_banner_manager.h b/chrome/browser/banners/app_banner_manager.h
index 0a4eb00..e00b47d 100644
--- a/chrome/browser/banners/app_banner_manager.h
+++ b/chrome/browser/banners/app_banner_manager.h
@@ -13,7 +13,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/strings/string16.h"
-#include "chrome/browser/engagement/site_engagement_observer.h"
+#include "components/site_engagement/content/site_engagement_observer.h"
 #include "components/webapps/installable/installable_logging.h"
 #include "components/webapps/installable/installable_params.h"
 #include "content/public/browser/media_player_id.h"
diff --git a/chrome/browser/banners/app_banner_manager_android.cc b/chrome/browser/banners/app_banner_manager_android.cc
index 81fcab5c..e39498444 100644
--- a/chrome/browser/banners/app_banner_manager_android.cc
+++ b/chrome/browser/banners/app_banner_manager_android.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/banners/android/jni_headers/AppBannerManager_jni.h"
 #include "chrome/browser/banners/app_banner_metrics.h"
 #include "chrome/browser/banners/app_banner_settings_helper.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/flags/android/chrome_feature_list.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/webapps/android/features.h"
@@ -31,6 +30,7 @@
 #include "components/feature_engagement/public/tracker.h"
 #include "components/infobars/core/infobar.h"
 #include "components/infobars/core/infobar_delegate.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/version_info/channel.h"
 #include "components/webapps/android/add_to_homescreen_params.h"
 #include "components/webapps/android/shortcut_info.h"
diff --git a/chrome/browser/banners/app_banner_manager_browsertest.cc b/chrome/browser/banners/app_banner_manager_browsertest.cc
index 2a90e31f..9c18678c 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest.cc
@@ -19,12 +19,12 @@
 #include "chrome/browser/banners/app_banner_manager_desktop.h"
 #include "chrome/browser/banners/app_banner_metrics.h"
 #include "chrome/browser/banners/app_banner_settings_helper.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/webapps/installable/installable_logging.h"
 #include "components/webapps/installable/installable_manager.h"
 #include "components/webapps/installable/installable_metrics.h"
diff --git a/chrome/browser/banners/app_banner_settings_helper_unittest.cc b/chrome/browser/banners/app_banner_settings_helper_unittest.cc
index fa274730..d0de883a 100644
--- a/chrome/browser/banners/app_banner_settings_helper_unittest.cc
+++ b/chrome/browser/banners/app_banner_settings_helper_unittest.cc
@@ -4,9 +4,9 @@
 
 #include "chrome/browser/banners/app_banner_settings_helper.h"
 #include "chrome/browser/banners/app_banner_metrics.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 
 namespace webapps {
 
diff --git a/chrome/browser/browser_process.h b/chrome/browser/browser_process.h
index f04600e..1ea08a7a 100644
--- a/chrome/browser/browser_process.h
+++ b/chrome/browser/browser_process.h
@@ -90,10 +90,6 @@
 class NetworkTimeTracker;
 }
 
-namespace optimization_guide {
-class OptimizationGuideService;
-}
-
 namespace policy {
 class ChromeBrowserPolicyConnector;
 class PolicyService;
@@ -229,11 +225,6 @@
   virtual federated_learning::FlocSortingLshClustersService*
   floc_sorting_lsh_clusters_service() = 0;
 
-  // Returns the service used to provide hints for what optimizations can be
-  // performed on slow page loads.
-  virtual optimization_guide::OptimizationGuideService*
-  optimization_guide_service() = 0;
-
   // Returns the StartupData which owns any pre-created objects in //chrome
   // before the full browser starts.
   virtual StartupData* startup_data() = 0;
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 55619d2..35510d17 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -100,8 +100,6 @@
 #include "components/metrics_services_manager/metrics_services_manager.h"
 #include "components/metrics_services_manager/metrics_services_manager_client.h"
 #include "components/network_time/network_time_tracker.h"
-#include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/optimization_guide_service.h"
 #include "components/permissions/permissions_client.h"
 #include "components/policy/core/common/policy_service.h"
 #include "components/prefs/json_pref_store.h"
@@ -1009,14 +1007,6 @@
   return floc_sorting_lsh_clusters_service_.get();
 }
 
-optimization_guide::OptimizationGuideService*
-BrowserProcessImpl::optimization_guide_service() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!created_optimization_guide_service_)
-    CreateOptimizationGuideService();
-  return optimization_guide_service_.get();
-}
-
 StartupData* BrowserProcessImpl::startup_data() {
   return startup_data_;
 }
@@ -1275,19 +1265,6 @@
       std::make_unique<federated_learning::FlocSortingLshClustersService>();
 }
 
-void BrowserProcessImpl::CreateOptimizationGuideService() {
-  DCHECK(!created_optimization_guide_service_);
-  DCHECK(!optimization_guide_service_);
-  created_optimization_guide_service_ = true;
-
-  if (!optimization_guide::features::IsOptimizationHintsEnabled())
-    return;
-
-  optimization_guide_service_ =
-      std::make_unique<optimization_guide::OptimizationGuideService>(
-          content::GetUIThreadTaskRunner({}));
-}
-
 #if !defined(OS_ANDROID)
 // Android's GCMDriver currently makes the assumption that it's a singleton.
 // Until this gets fixed, instantiating multiple Java GCMDrivers will throw an
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index a967b1e..1d3c566 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -175,8 +175,6 @@
       override;
   federated_learning::FlocSortingLshClustersService*
   floc_sorting_lsh_clusters_service() override;
-  optimization_guide::OptimizationGuideService* optimization_guide_service()
-      override;
 
   StartupData* startup_data() override;
 
@@ -330,10 +328,6 @@
   std::unique_ptr<federated_learning::FlocSortingLshClustersService>
       floc_sorting_lsh_clusters_service_;
 
-  bool created_optimization_guide_service_ = false;
-  std::unique_ptr<optimization_guide::OptimizationGuideService>
-      optimization_guide_service_;
-
   bool shutting_down_ = false;
 
   bool tearing_down_ = false;
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index c4647ab..117da8a 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -60,9 +60,9 @@
 #include "chrome/browser/device_api/device_service_impl.h"
 #include "chrome/browser/download/chrome_download_manager_delegate.h"
 #include "chrome/browser/download/download_prefs.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/extensions/chrome_extension_cookies.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
+#include "chrome/browser/federated_learning/floc_eligibility_observer.h"
 #include "chrome/browser/federated_learning/floc_id_provider.h"
 #include "chrome/browser/federated_learning/floc_id_provider_factory.h"
 #include "chrome/browser/font_access/chrome_font_access_delegate.h"
@@ -89,6 +89,7 @@
 #include "chrome/browser/notifications/platform_notification_service_impl.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/payments/payment_request_display_manager_factory.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #if !defined(OS_ANDROID)
 #include "chrome/browser/payments/payment_handler_navigation_throttle.h"
 #endif
@@ -5747,12 +5748,16 @@
 }
 
 std::string ChromeContentBrowserClient::GetInterestCohortForJsApi(
-    content::BrowserContext* browser_context,
+    content::WebContents* web_contents,
     const GURL& url,
     const base::Optional<url::Origin>& top_frame_origin) {
+  federated_learning::FlocEligibilityObserver::GetOrCreateForCurrentDocument(
+      web_contents->GetMainFrame())
+      ->OnInterestCohortApiUsed();
+
   federated_learning::FlocIdProvider* floc_id_provider =
       federated_learning::FlocIdProviderFactory::GetForProfile(
-          Profile::FromBrowserContext(browser_context));
+          Profile::FromBrowserContext(web_contents->GetBrowserContext()));
 
   if (!floc_id_provider)
     return std::string();
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 0d7dc1a..8b59398 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -639,7 +639,7 @@
       content::NavigationDownloadPolicy* download_policy) override;
 
   std::string GetInterestCohortForJsApi(
-      content::BrowserContext* browser_context,
+      content::WebContents* web_contents,
       const GURL& url,
       const base::Optional<url::Origin>& top_frame_origin) override;
 
diff --git a/chrome/browser/chromeos/assistant/assistant_util.cc b/chrome/browser/chromeos/assistant/assistant_util.cc
index 607cabe..2b9d8210 100644
--- a/chrome/browser/chromeos/assistant/assistant_util.cc
+++ b/chrome/browser/chromeos/assistant/assistant_util.cc
@@ -72,7 +72,7 @@
       NOTREACHED();
       return AssistantAllowedState::DISALLOWED_BY_ACCOUNT_TYPE;
 
-    case user_manager::USER_TYPE_SUPERVISED:
+    case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
     case user_manager::NUM_USER_TYPES:
       NOTREACHED();
       return AssistantAllowedState::DISALLOWED_BY_ACCOUNT_TYPE;
diff --git a/chrome/browser/chromeos/assistant/assistant_util_unittest.cc b/chrome/browser/chromeos/assistant/assistant_util_unittest.cc
index 59bcc92..e5d9aa9 100644
--- a/chrome/browser/chromeos/assistant/assistant_util_unittest.cc
+++ b/chrome/browser/chromeos/assistant/assistant_util_unittest.cc
@@ -130,7 +130,7 @@
         EXPECT_EQ(account_id_, fake_user_manager_->GetGuestAccountId());
         return;
       case user_manager::NUM_USER_TYPES:
-      case user_manager::USER_TYPE_SUPERVISED:
+      case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
         NOTREACHED();
     }
   }
@@ -161,7 +161,7 @@
       case user_manager::USER_TYPE_GUEST:
         fake_user_manager_->AddGuestUser();
         return;
-      case user_manager::USER_TYPE_SUPERVISED:
+      case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
       case user_manager::NUM_USER_TYPES:
         NOTREACHED();
     }
diff --git a/chrome/browser/chromeos/crosapi/browser_util.cc b/chrome/browser/chromeos/crosapi/browser_util.cc
index afaf136..f3c68b5a 100644
--- a/chrome/browser/chromeos/crosapi/browser_util.cc
+++ b/chrome/browser/chromeos/crosapi/browser_util.cc
@@ -59,7 +59,7 @@
       return true;
     case user_manager::USER_TYPE_GUEST:
     case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
-    case user_manager::USER_TYPE_SUPERVISED:
+    case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
     case user_manager::USER_TYPE_KIOSK_APP:
     case user_manager::USER_TYPE_CHILD:
     case user_manager::USER_TYPE_ARC_KIOSK_APP:
diff --git a/chrome/browser/chromeos/drive/drivefs_native_message_host.cc b/chrome/browser/chromeos/drive/drivefs_native_message_host.cc
index b617839..bb6d7e7 100644
--- a/chrome/browser/chromeos/drive/drivefs_native_message_host.cc
+++ b/chrome/browser/chromeos/drive/drivefs_native_message_host.cc
@@ -64,7 +64,9 @@
     DCHECK(client_);
 
     if (UseBidirectionalNativeMessaging()) {
-      drivefs_remote_->HandleMessageFromExtension(message);
+      if (drivefs_remote_) {
+        drivefs_remote_->HandleMessageFromExtension(message);
+      }
     } else {
       if (!drive_service_ || !drive_service_->GetDriveFsInterface()) {
         OnDriveFsResponse(FILE_ERROR_SERVICE_UNAVAILABLE, "");
diff --git a/chrome/browser/chromeos/drive/drivefs_native_message_host_unittest.cc b/chrome/browser/chromeos/drive/drivefs_native_message_host_unittest.cc
index c06d9ab..3b45624 100644
--- a/chrome/browser/chromeos/drive/drivefs_native_message_host_unittest.cc
+++ b/chrome/browser/chromeos/drive/drivefs_native_message_host_unittest.cc
@@ -150,6 +150,7 @@
           extension_port_.BindNewPipeAndPassReceiver(),
           receiver_.BindNewPipeAndPassRemote());
   MockClient client;
+  EXPECT_CALL(*this, HandleMessageFromExtension).Times(0);
   EXPECT_CALL(client, PostMessageFromNativeHost).Times(0);
   EXPECT_CALL(client, CloseChannel("FILE_ERROR_FAILED: foo"));
   receiver_.set_disconnect_handler(run_loop.QuitClosure());
@@ -158,6 +159,9 @@
   extension_port_.ResetWithReason(1u, "foo");
 
   run_loop.Run();
+
+  host->OnMessage("bar");
+  base::RunLoop().RunUntilIdle();
 }
 
 class DriveFsNativeMessageHostTestWithoutFlag
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
index e9ec6a9..3d5fe45 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -223,8 +223,8 @@
     // Network access.
     emk::kSockets,
 
-    // Just provides dictionaries, no access to content.
-    emk::kSpellcheck,
+    // Deprecated manifest key.
+    // "spellcheck",
 
     // (Note: Using string literal since extensions::manifest_keys only has
     // constants for sub-keys.)
diff --git a/chrome/browser/chromeos/extensions/users_private/users_private_api.cc b/chrome/browser/chromeos/extensions/users_private/users_private_api.cc
index 6cfc71f..89bb922e 100644
--- a/chrome/browser/chromeos/extensions/users_private/users_private_api.cc
+++ b/chrome/browser/chromeos/extensions/users_private/users_private_api.cc
@@ -75,7 +75,7 @@
   api_user.name = base::UTF16ToUTF8(user.GetDisplayName());
   api_user.is_owner = user.GetAccountId() ==
                       user_manager::UserManager::Get()->GetOwnerAccountId();
-  api_user.is_supervised = user.IsSupervised();
+  api_user.is_supervised = user.IsChildOrDeprecatedSupervised();
   api_user.is_child = user.IsChild();
   return api_user;
 }
@@ -128,7 +128,8 @@
   for (size_t i = 0; i < email_list->GetSize(); ++i) {
     std::string email;
     email_list->GetString(i, &email);
-    if (user_manager->IsSupervisedAccountId(AccountId::FromUserEmail(email))) {
+    if (user_manager->IsDeprecatedSupervisedAccountId(
+            AccountId::FromUserEmail(email))) {
       email_list->Remove(i, nullptr);
       --i;
     }
diff --git a/chrome/browser/chromeos/first_run/first_run.cc b/chrome/browser/chromeos/first_run/first_run.cc
index 69d29d1e..a652d8e 100644
--- a/chrome/browser/chromeos/first_run/first_run.cc
+++ b/chrome/browser/chromeos/first_run/first_run.cc
@@ -67,7 +67,7 @@
 bool IsRegularUserOrSupervisedChild(user_manager::UserManager* user_manager) {
   switch (user_manager->GetActiveUser()->GetType()) {
     case user_manager::USER_TYPE_REGULAR:
-    case user_manager::USER_TYPE_SUPERVISED:
+    case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
     case user_manager::USER_TYPE_CHILD:
       return true;
     default:
@@ -85,7 +85,7 @@
     return true;
   switch (user_manager->GetActiveUser()->GetType()) {
     case user_manager::USER_TYPE_REGULAR:
-    case user_manager::USER_TYPE_SUPERVISED:
+    case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
       return !profile->GetProfilePolicyConnector()->IsManaged();
     default:
       return false;
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index 698d996..06cbc5f 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -152,13 +152,14 @@
     ::switches::kDisableWebRtcHWEncoding,
     ::switches::kOzonePlatform,
     ash::switches::kAshClearFastInkBuffer,
+    ash::switches::kAshEnablePaletteOnAllDisplays,
     ash::switches::kAshEnableTabletMode,
     ash::switches::kAshEnableWaylandServer,
     ash::switches::kAshForceEnableStylusTools,
-    ash::switches::kAshEnablePaletteOnAllDisplays,
     ash::switches::kAshTouchHud,
     ash::switches::kAuraLegacyPowerButton,
     ash::switches::kEnableDimShelf,
+    ash::switches::kForceInTabletPhysicalState,
     ash::switches::kShowTaps,
     blink::switches::kBlinkSettings,
     blink::switches::kDarkModeSettings,
diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc
index e197778..9d660792 100644
--- a/chrome/browser/chromeos/login/existing_user_controller.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller.cc
@@ -481,7 +481,7 @@
     if (user->IsKioskType())
       continue;
     // TODO(xiyuan): Clean user profile whose email is not in allowlist.
-    if (user->GetType() == user_manager::USER_TYPE_SUPERVISED)
+    if (user->GetType() == user_manager::USER_TYPE_SUPERVISED_DEPRECATED)
       continue;
     const bool meets_allowlist_requirements =
         !user->HasGaiaAccount() || user_manager->IsGaiaUserAllowed(*user);
diff --git a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
index c90d503..24feb50 100644
--- a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
@@ -122,9 +122,7 @@
 const char kManagedDomain[] = "example.com";
 const char kUserAllowlist[] = "*@ad-domain.com";
 const char kUserNotMatchingAllowlist[] = "user@another_mail.com";
-const char kSupervisedUserID[] = "supervised_user@locally-managed.localhost";
 const char kPassword[] = "test_password";
-const char kHash[] = "test_hash";
 
 const char kPublicSessionUserEmail[] = "public_session_user@localhost";
 const char kPublicSessionSecondUserEmail[] =
@@ -347,15 +345,6 @@
       SigninSpecifics());
 }
 
-IN_PROC_BROWSER_TEST_F(ExistingUserControllerUntrustedTest,
-                       SupervisedUserLoginForbidden) {
-  UserContext user_context(user_manager::UserType::USER_TYPE_SUPERVISED,
-                           AccountId::FromUserEmail(kSupervisedUserID));
-  user_context.SetKey(Key(kPassword));
-  user_context.SetUserIDHash(kHash);
-  existing_user_controller()->Login(user_context, SigninSpecifics());
-}
-
 MATCHER_P(HasDetails, expected, "") {
   return expected == *content::Details<const std::string>(arg).ptr();
 }
diff --git a/chrome/browser/chromeos/login/login_ui_hide_supervised_users_browsertest.cc b/chrome/browser/chromeos/login/login_ui_hide_supervised_users_browsertest.cc
index 14b19bc..1075ab5 100644
--- a/chrome/browser/chromeos/login/login_ui_hide_supervised_users_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_ui_hide_supervised_users_browsertest.cc
@@ -19,10 +19,10 @@
 constexpr char kHistogramName[] =
     "ChromeOS.LegacySupervisedUsers.HiddenFromLoginScreen";
 
-const LoginManagerMixin::TestUserInfo kLegacySupervisedUser{
+const LoginManagerMixin::TestUserInfo kDeprecatedSupervisedUser{
     AccountId::FromUserEmailGaiaId("test@locally-managed.localhost",
                                    "123456780"),
-    user_manager::USER_TYPE_SUPERVISED};
+    user_manager::USER_TYPE_SUPERVISED_DEPRECATED};
 
 const LoginManagerMixin::TestUserInfo kFamilyLinkUser{
     AccountId::FromUserEmailGaiaId(test::kTestEmail, test::kTestGaiaId),
@@ -38,11 +38,11 @@
 
  protected:
   LoginManagerMixin login_mixin_{&mixin_host_,
-                                 {kLegacySupervisedUser, kFamilyLinkUser}};
+                                 {kDeprecatedSupervisedUser, kFamilyLinkUser}};
   base::HistogramTester histogram_tester_;
 };
 
-// Verifies that the login screen hides legacy supervised users and records
+// Verifies that the login screen hides deprecated supervised users and records
 // metrics.
 IN_PROC_BROWSER_TEST_F(LoginUIHideSupervisedUsersTest, SupervisedUserHidden) {
   // Only the Gaia users should be displayed on the login screen.
@@ -50,10 +50,10 @@
   EXPECT_EQ(3u, user_manager::UserManager::Get()->GetUsers().size());
   for (user_manager::User* user :
        user_manager::UserManager::Get()->GetUsers()) {
-    EXPECT_TRUE(!user->IsSupervised() || user->IsChild());
+    EXPECT_TRUE(!user->IsChildOrDeprecatedSupervised() || user->IsChild());
   }
 
-  // The login screen hid one legacy supervised user.
+  // The login screen hid one deprecated supervised user.
   histogram_tester_.ExpectBucketCount(kHistogramName, /*sample=*/true,
                                       /*expected_count=*/1);
   // The login screen displayed two regular users and one Family Link user.
diff --git a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc
index 5688756..3a015d2 100644
--- a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc
+++ b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc
@@ -110,10 +110,12 @@
   if (enable_for_testing_)
     return true;
 
-  // PIN is disabled for legacy supervised user, but allowed to child user.
+  // PIN is disabled for deprecated supervised user, but allowed to child user.
   user_manager::User* user = user_manager::UserManager::Get()->GetActiveUser();
-  if (user && user->GetType() == user_manager::UserType::USER_TYPE_SUPERVISED)
+  if (user && user->GetType() ==
+                  user_manager::UserType::USER_TYPE_SUPERVISED_DEPRECATED) {
     return false;
+  }
 
   return true;
 }
diff --git a/chrome/browser/chromeos/login/screens/user_selection_screen.cc b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
index 15017c8..c10730ec 100644
--- a/chrome/browser/chromeos/login/screens/user_selection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
@@ -531,15 +531,16 @@
     base::DictionaryValue* user_dict) {
   const bool is_public_session =
       user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
-  const bool is_legacy_supervised_user =
-      user->GetType() == user_manager::USER_TYPE_SUPERVISED;
+  const bool is_deprecated_supervised_user =
+      user->GetType() == user_manager::USER_TYPE_SUPERVISED_DEPRECATED;
   const bool is_child_user = user->GetType() == user_manager::USER_TYPE_CHILD;
 
   user_dict->SetString(kKeyUsername, user->GetAccountId().Serialize());
   user_dict->SetString(kKeyEmailAddress, user->display_email());
   user_dict->SetString(kKeyDisplayName, user->GetDisplayName());
   user_dict->SetBoolean(kKeyPublicAccount, is_public_session);
-  user_dict->SetBoolean(kKeyLegacySupervisedUser, is_legacy_supervised_user);
+  user_dict->SetBoolean(kKeyLegacySupervisedUser,
+                        is_deprecated_supervised_user);
   user_dict->SetBoolean(kKeyChildUser, is_child_user);
   user_dict->SetBoolean(kKeyDesktopUser, false);
   user_dict->SetInteger(kKeyInitialAuthType, static_cast<int>(auth_type));
@@ -587,7 +588,7 @@
     return false;
 
   // Public sessions are always allowed to log in offline.
-  // Supervised users are always allowed to log in offline.
+  // Deprecated supervised users are always allowed to log in offline.
   // For all other users, force online sign in if:
   // * The flag to force online sign-in is set for the user.
   // * The user's OAuth token is invalid or unknown.
@@ -596,13 +597,14 @@
 
   const user_manager::User::OAuthTokenStatus token_status =
       user->oauth_token_status();
-  const bool is_supervised_user =
-      user->GetType() == user_manager::USER_TYPE_SUPERVISED;
+  // TODO(crbug/1155729): Check if this bool is ever true. If not, remove it.
+  const bool is_deprecated_supervised_user =
+      user->GetType() == user_manager::USER_TYPE_SUPERVISED_DEPRECATED;
   const bool is_public_session =
       user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
   const bool has_gaia_account = user->HasGaiaAccount();
 
-  if (is_supervised_user)
+  if (is_deprecated_supervised_user)
     return false;
 
   if (is_public_session)
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 83d711d1..b4f1bcc 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -1025,7 +1025,7 @@
   for (auto* user : logged_in_users) {
     if (user->GetType() != user_manager::USER_TYPE_REGULAR &&
         user->GetType() != user_manager::USER_TYPE_GUEST &&
-        user->GetType() != user_manager::USER_TYPE_SUPERVISED &&
+        user->GetType() != user_manager::USER_TYPE_SUPERVISED_DEPRECATED &&
         user->GetType() != user_manager::USER_TYPE_CHILD) {
       continue;
     }
diff --git a/chrome/browser/chromeos/login/signin/auth_error_observer.cc b/chrome/browser/chromeos/login/signin/auth_error_observer.cc
index 03310ce..d6177c9 100644
--- a/chrome/browser/chromeos/login/signin/auth_error_observer.cc
+++ b/chrome/browser/chromeos/login/signin/auth_error_observer.cc
@@ -25,8 +25,9 @@
 bool AuthErrorObserver::ShouldObserve(Profile* profile) {
   const user_manager::User* const user =
       ProfileHelper::Get()->GetUserByProfile(profile);
-  return user && (user->HasGaiaAccount() ||
-                  user->GetType() == user_manager::USER_TYPE_SUPERVISED);
+  return user &&
+         (user->HasGaiaAccount() ||
+          user->GetType() == user_manager::USER_TYPE_SUPERVISED_DEPRECATED);
 }
 
 AuthErrorObserver::AuthErrorObserver(Profile* profile) : profile_(profile) {
@@ -80,7 +81,7 @@
   const user_manager::User* const user =
       ProfileHelper::Get()->GetUserByProfile(profile_);
   DCHECK(user->HasGaiaAccount() ||
-         user->GetType() == user_manager::USER_TYPE_SUPERVISED);
+         user->GetType() == user_manager::USER_TYPE_SUPERVISED_DEPRECATED);
 
   if (auth_error.IsPersistentError()) {
     // Invalidate OAuth2 refresh token to force Gaia sign-in flow. This is
@@ -100,10 +101,6 @@
                     "token status.";
       user_manager::UserManager::Get()->SaveUserOAuthStatus(
           user->GetAccountId(), user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
-      if (user->GetType() == user_manager::USER_TYPE_SUPERVISED) {
-        base::RecordAction(
-            base::UserMetricsAction("ManagedUsers_Chromeos_Sync_Recovered"));
-      }
     }
   }
 }
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager.cc b/chrome/browser/chromeos/login/users/chrome_user_manager.cc
index d144d2c..32ef3b6 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager.cc
@@ -97,8 +97,8 @@
       return IsManagedSessionEnabledForUser(active_user)
                  ? LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED
                  : LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT;
-    case user_manager::USER_TYPE_SUPERVISED:
-      return LoginState::LOGGED_IN_USER_SUPERVISED;
+    case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
+      return LoginState::LOGGED_IN_USER_SUPERVISED_DEPRECATED;
     case user_manager::USER_TYPE_KIOSK_APP:
       return LoginState::LOGGED_IN_USER_KIOSK_APP;
     case user_manager::USER_TYPE_CHILD:
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
index f3021ef3..6358671 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -1184,7 +1184,7 @@
     const user_manager::User& user) const {
   DCHECK(user.GetType() == user_manager::USER_TYPE_REGULAR ||
          user.GetType() == user_manager::USER_TYPE_GUEST ||
-         user.GetType() == user_manager::USER_TYPE_SUPERVISED ||
+         user.GetType() == user_manager::USER_TYPE_SUPERVISED_DEPRECATED ||
          user.GetType() == user_manager::USER_TYPE_CHILD);
 
   return chrome_user_manager_util::IsUserAllowed(
@@ -1367,7 +1367,7 @@
          account_id == user_manager::StubAdAccountId();
 }
 
-bool ChromeUserManagerImpl::IsSupervisedAccountId(
+bool ChromeUserManagerImpl::IsDeprecatedSupervisedAccountId(
     const AccountId& account_id) const {
   const policy::BrowserPolicyConnectorChromeOS* connector =
       g_browser_process->platform_part()->browser_policy_connector_chromeos();
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.h b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.h
index aff78c79..e7a2bbe 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.h
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.h
@@ -104,7 +104,8 @@
   void AsyncRemoveCryptohome(const AccountId& account_id) const override;
   bool IsGuestAccountId(const AccountId& account_id) const override;
   bool IsStubAccountId(const AccountId& account_id) const override;
-  bool IsSupervisedAccountId(const AccountId& account_id) const override;
+  bool IsDeprecatedSupervisedAccountId(
+      const AccountId& account_id) const override;
   bool HasBrowserRestarted() const override;
   const gfx::ImageSkia& GetResourceImagekiaNamed(int id) const override;
   base::string16 GetResourceStringUTF16(int string_id) const override;
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_util.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_util.cc
index a36e055..87ca8aa7 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_util.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_util.cc
@@ -62,13 +62,13 @@
                    bool is_user_allowlisted) {
   DCHECK(user.GetType() == user_manager::USER_TYPE_REGULAR ||
          user.GetType() == user_manager::USER_TYPE_GUEST ||
-         user.GetType() == user_manager::USER_TYPE_SUPERVISED ||
+         user.GetType() == user_manager::USER_TYPE_SUPERVISED_DEPRECATED ||
          user.GetType() == user_manager::USER_TYPE_CHILD);
 
   if (user.GetType() == user_manager::USER_TYPE_GUEST && !is_guest_allowed) {
     return false;
   }
-  if (user.GetType() == user_manager::USER_TYPE_SUPERVISED) {
+  if (user.GetType() == user_manager::USER_TYPE_SUPERVISED_DEPRECATED) {
     return false;
   }
   if (user.HasGaiaAccount() && !is_user_allowlisted) {
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_util.h b/chrome/browser/chromeos/login/users/chrome_user_manager_util.h
index 6bd93b9..00ae5f5 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_util.h
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_util.h
@@ -13,7 +13,7 @@
 
 // Returns true if all `users` are allowed depending on the provided device
 // policies. Accepted user types: USER_TYPE_REGULAR, USER_TYPE_GUEST,
-// USER_TYPE_SUPERVISED, USER_TYPE_CHILD.
+// USER_TYPE_SUPERVISED_DEPRECATED, USER_TYPE_CHILD.
 // This function only checks against the device policies provided, so it does
 // not depend on CrosSettings or any other policy store.
 bool AreAllUsersAllowed(const user_manager::UserList& users,
@@ -22,7 +22,7 @@
 
 // Returns true if `user` is allowed, according to the given constraints.
 // Accepted user types: USER_TYPE_REGULAR, USER_TYPE_GUEST,
-// USER_TYPE_SUPERVISED, USER_TYPE_CHILD.
+// USER_TYPE_SUPERVISED_DEPRECATED, USER_TYPE_CHILD.
 bool IsUserAllowed(const user_manager::User& user,
                    bool is_guest_allowed,
                    bool is_user_allowlisted);
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
index a1e4f0c..f3c18cb6 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
@@ -341,7 +341,7 @@
   return account_id == user_manager::StubAccountId();
 }
 
-bool FakeChromeUserManager::IsSupervisedAccountId(
+bool FakeChromeUserManager::IsDeprecatedSupervisedAccountId(
     const AccountId& account_id) const {
   const policy::BrowserPolicyConnectorChromeOS* connector =
       g_browser_process->platform_part()->browser_policy_connector_chromeos();
@@ -612,13 +612,13 @@
     const user_manager::User& user) const {
   DCHECK(user.GetType() == user_manager::USER_TYPE_REGULAR ||
          user.GetType() == user_manager::USER_TYPE_GUEST ||
-         user.GetType() == user_manager::USER_TYPE_SUPERVISED ||
+         user.GetType() == user_manager::USER_TYPE_SUPERVISED_DEPRECATED ||
          user.GetType() == user_manager::USER_TYPE_CHILD);
 
   if (user.GetType() == user_manager::USER_TYPE_GUEST &&
       !IsGuestSessionAllowed())
     return false;
-  if (user.GetType() == user_manager::USER_TYPE_SUPERVISED)
+  if (user.GetType() == user_manager::USER_TYPE_SUPERVISED_DEPRECATED)
     return false;
   if (user.HasGaiaAccount() && !IsGaiaUserAllowed(user))
     return false;
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
index cef8d2c..cccb79a1 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
@@ -130,7 +130,8 @@
   void AsyncRemoveCryptohome(const AccountId& account_id) const override;
   bool IsGuestAccountId(const AccountId& account_id) const override;
   bool IsStubAccountId(const AccountId& account_id) const override;
-  bool IsSupervisedAccountId(const AccountId& account_id) const override;
+  bool IsDeprecatedSupervisedAccountId(
+      const AccountId& account_id) const override;
   bool HasBrowserRestarted() const override;
   const gfx::ImageSkia& GetResourceImagekiaNamed(int id) const override;
   base::string16 GetResourceStringUTF16(int string_id) const override;
diff --git a/chrome/browser/chromeos/login/users/mock_user_manager.h b/chrome/browser/chromeos/login/users/mock_user_manager.h
index 7e27199..7b61e99 100644
--- a/chrome/browser/chromeos/login/users/mock_user_manager.h
+++ b/chrome/browser/chromeos/login/users/mock_user_manager.h
@@ -88,7 +88,7 @@
   MOCK_CONST_METHOD0(IsFirstExecAfterBoot, bool(void));
   MOCK_CONST_METHOD1(IsGuestAccountId, bool(const AccountId&));
   MOCK_CONST_METHOD1(IsStubAccountId, bool(const AccountId&));
-  MOCK_CONST_METHOD1(IsSupervisedAccountId, bool(const AccountId&));
+  MOCK_CONST_METHOD1(IsDeprecatedSupervisedAccountId, bool(const AccountId&));
   MOCK_CONST_METHOD0(HasBrowserRestarted, bool(void));
 
   // UserManagerBase overrides:
diff --git a/chrome/browser/chromeos/policy/extension_install_event_log_collector.cc b/chrome/browser/chromeos/policy/extension_install_event_log_collector.cc
index 1c137442..0aa6fe9 100644
--- a/chrome/browser/chromeos/policy/extension_install_event_log_collector.cc
+++ b/chrome/browser/chromeos/policy/extension_install_event_log_collector.cc
@@ -149,8 +149,9 @@
       return em::ExtensionInstallReportLogEvent::USER_TYPE_GUEST;
     case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
       return em::ExtensionInstallReportLogEvent::USER_TYPE_PUBLIC_ACCOUNT;
-    case user_manager::USER_TYPE_SUPERVISED:
-      return em::ExtensionInstallReportLogEvent::USER_TYPE_SUPERVISED;
+    case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
+      return em::ExtensionInstallReportLogEvent::
+          USER_TYPE_SUPERVISED_DEPRECATED;
     case user_manager::USER_TYPE_KIOSK_APP:
       return em::ExtensionInstallReportLogEvent::USER_TYPE_KIOSK_APP;
     case user_manager::USER_TYPE_CHILD:
diff --git a/chrome/browser/chromeos/policy/off_hours/device_off_hours_controller.cc b/chrome/browser/chromeos/policy/off_hours/device_off_hours_controller.cc
index 05ee0a0b..7fd916e 100644
--- a/chrome/browser/chromeos/policy/off_hours/device_off_hours_controller.cc
+++ b/chrome/browser/chromeos/policy/off_hours/device_off_hours_controller.cc
@@ -73,7 +73,7 @@
   for (auto* user : logged_in_users) {
     if (user->GetType() == user_manager::USER_TYPE_REGULAR ||
         user->GetType() == user_manager::USER_TYPE_GUEST ||
-        user->GetType() == user_manager::USER_TYPE_SUPERVISED ||
+        user->GetType() == user_manager::USER_TYPE_SUPERVISED_DEPRECATED ||
         user->GetType() == user_manager::USER_TYPE_CHILD) {
       users_to_check.push_back(user);
     }
diff --git a/chrome/browser/chromeos/policy/user_policy_manager_builder_chromeos.cc b/chrome/browser/chromeos/policy/user_policy_manager_builder_chromeos.cc
index c5815dd8..54c3464 100644
--- a/chrome/browser/chromeos/policy/user_policy_manager_builder_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_policy_manager_builder_chromeos.cc
@@ -116,7 +116,7 @@
   //   |UserCloudPolicyManagerChromeOS| is created here.
   // All other user types do not have user policy.
   const AccountId& account_id = user->GetAccountId();
-  if (user->GetType() == user_manager::USER_TYPE_SUPERVISED ||
+  if (user->GetType() == user_manager::USER_TYPE_SUPERVISED_DEPRECATED ||
       (user->GetType() != user_manager::USER_TYPE_CHILD &&
        BrowserPolicyConnector::IsNonEnterpriseUser(
            account_id.GetUserEmail()))) {
diff --git a/chrome/browser/chromeos/power/ml/user_activity_manager_unittest.cc b/chrome/browser/chromeos/power/ml/user_activity_manager_unittest.cc
index 0efcbd4..9cba5bc 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_manager_unittest.cc
+++ b/chrome/browser/chromeos/power/ml/user_activity_manager_unittest.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/chromeos/power/ml/smart_dim/ml_agent.h"
 #include "chrome/browser/chromeos/power/ml/user_activity_event.pb.h"
 #include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/tab_activity_simulator.h"
@@ -36,6 +35,7 @@
 #include "chromeos/services/machine_learning/public/cpp/fake_service_connection.h"
 #include "chromeos/services/machine_learning/public/cpp/service_connection.h"
 #include "components/session_manager/session_manager_types.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/ukm/content/source_url_recorder.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/chromeos/system/timezone_util.cc b/chrome/browser/chromeos/system/timezone_util.cc
index f29b0f41..18d804f1 100644
--- a/chrome/browser/chromeos/system/timezone_util.cc
+++ b/chrome/browser/chromeos/system/timezone_util.cc
@@ -170,7 +170,7 @@
 
   switch (user->GetType()) {
     case user_manager::USER_TYPE_REGULAR:
-    case user_manager::USER_TYPE_SUPERVISED:
+    case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
     case user_manager::USER_TYPE_KIOSK_APP:
     case user_manager::USER_TYPE_ARC_KIOSK_APP:
     case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
diff --git a/chrome/browser/component_updater/registration.cc b/chrome/browser/component_updater/registration.cc
index 4ef2087..2b17bd7 100644
--- a/chrome/browser/component_updater/registration.cc
+++ b/chrome/browser/component_updater/registration.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/component_updater/games_component_installer.h"
 #include "chrome/browser/component_updater/hyphenation_component_installer.h"
 #include "chrome/browser/component_updater/mei_preload_component_installer.h"
-#include "chrome/browser/component_updater/optimization_hints_component_installer.h"
 #include "chrome/browser/component_updater/origin_trials_component_installer.h"
 #include "chrome/browser/component_updater/pepper_flash_component_installer.h"
 #include "chrome/browser/component_updater/ssl_error_assistant_component_installer.h"
@@ -36,6 +35,7 @@
 #include "components/component_updater/component_updater_service.h"
 #include "components/component_updater/crl_set_remover.h"
 #include "components/component_updater/installer_policies/on_device_head_suggest_component_installer.h"
+#include "components/component_updater/installer_policies/optimization_hints_component_installer.h"
 #include "components/component_updater/installer_policies/safety_tips_component_installer.h"
 #include "components/nacl/common/buildflags.h"
 #include "device/vr/buildflags/buildflags.h"
@@ -118,7 +118,7 @@
                         g_browser_process->floc_sorting_lsh_clusters_service());
   RegisterOnDeviceHeadSuggestComponent(
       cus, g_browser_process->GetApplicationLocale());
-  RegisterOptimizationHintsComponent(cus, is_off_the_record_profile);
+  RegisterOptimizationHintsComponent(cus);
   RegisterTrustTokenKeyCommitmentsComponentIfTrustTokensEnabled(cus);
   RegisterFirstPartySetsComponent(cus);
 
diff --git a/chrome/browser/content_index/content_index_provider_impl.cc b/chrome/browser/content_index/content_index_provider_impl.cc
index 52014b7f..29ec5c9 100644
--- a/chrome/browser/content_index/content_index_provider_impl.cc
+++ b/chrome/browser/content_index/content_index_provider_impl.cc
@@ -11,7 +11,6 @@
 #include "base/strings/string_split.h"
 #include "base/task/post_task.h"
 #include "build/build_config.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/engagement/site_engagement_service_factory.h"
 #include "chrome/browser/metrics/ukm_background_recorder_service.h"
 #include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h"
@@ -21,6 +20,7 @@
 #include "components/offline_items_collection/core/offline_item.h"
 #include "components/offline_items_collection/core/update_delta.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_index_context.h"
diff --git a/chrome/browser/content_index/content_index_provider_unittest.cc b/chrome/browser/content_index/content_index_provider_unittest.cc
index 8208c26..36226811 100644
--- a/chrome/browser/content_index/content_index_provider_unittest.cc
+++ b/chrome/browser/content_index/content_index_provider_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/time/time.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/engagement/site_engagement_service_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/test/base/testing_profile.h"
@@ -20,6 +19,7 @@
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/test/test_history_database.h"
 #include "components/offline_items_collection/core/offline_content_provider.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/content_index_provider.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/engagement/android/BUILD.gn b/chrome/browser/engagement/android/BUILD.gn
index 719956e..ed6f5b6 100644
--- a/chrome/browser/engagement/android/BUILD.gn
+++ b/chrome/browser/engagement/android/BUILD.gn
@@ -4,33 +4,18 @@
 
 import("//build/config/android/rules.gni")
 
-android_library("java") {
-  sources = [ "java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java" ]
-  deps = [
-    ":jni_headers",
-    "//base:base_java",
-    "//base:jni_java",
-    "//chrome/browser/profiles/android:java",
-  ]
-  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
-}
-
 android_library("javatests") {
   testonly = true
   sources = [ "java/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java" ]
   deps = [
-    ":java",
     "//base:base_java_test_support",
     "//chrome/android:chrome_java",
     "//chrome/browser/flags:java",
     "//chrome/browser/profiles/android:java",
     "//chrome/browser/tab:java",
     "//chrome/test/android:chrome_java_test_support",
+    "//components/site_engagement/content/android:java",
     "//third_party/android_deps:androidx_test_runner_java",
     "//third_party/junit:junit",
   ]
 }
-
-generate_jni("jni_headers") {
-  sources = [ "java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java" ]
-}
diff --git a/chrome/browser/engagement/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java b/chrome/browser/engagement/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java
index 229f89e7..a7cbfa0 100644
--- a/chrome/browser/engagement/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java
+++ b/chrome/browser/engagement/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java
@@ -18,6 +18,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.components.site_engagement.SiteEngagementService;
 
 /**
  * Test for the Site Engagement Service Java binding.
diff --git a/chrome/browser/engagement/history_aware_site_engagement_service.h b/chrome/browser/engagement/history_aware_site_engagement_service.h
index 28ef6f5..0f1b433 100644
--- a/chrome/browser/engagement/history_aware_site_engagement_service.h
+++ b/chrome/browser/engagement/history_aware_site_engagement_service.h
@@ -8,9 +8,9 @@
 #include <set>
 
 #include "base/scoped_observation.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_service_observer.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 
 namespace content {
 class BrowserContext;
diff --git a/chrome/browser/engagement/important_sites_util.cc b/chrome/browser/engagement/important_sites_util.cc
index d59b459f..ff8a55e 100644
--- a/chrome/browser/engagement/important_sites_util.cc
+++ b/chrome/browser/engagement/important_sites_util.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/banners/app_banner_settings_helper.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/installable/installable_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
@@ -30,6 +29,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/site_engagement/core/mojom/site_engagement_details.mojom.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "third_party/blink/public/mojom/site_engagement/site_engagement.mojom.h"
diff --git a/chrome/browser/engagement/important_sites_util_unittest.cc b/chrome/browser/engagement/important_sites_util_unittest.cc
index 3490d94..0b1ef7d 100644
--- a/chrome/browser/engagement/important_sites_util_unittest.cc
+++ b/chrome/browser/engagement/important_sites_util_unittest.cc
@@ -16,7 +16,6 @@
 #include "build/build_config.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
@@ -27,6 +26,7 @@
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/web_contents.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/engagement/site_engagement_helper.h b/chrome/browser/engagement/site_engagement_helper.h
index 16f8d64..1bc870f 100644
--- a/chrome/browser/engagement/site_engagement_helper.h
+++ b/chrome/browser/engagement/site_engagement_helper.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/media_player_id.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
diff --git a/chrome/browser/engagement/site_engagement_helper_unittest.cc b/chrome/browser/engagement/site_engagement_helper_unittest.cc
index 8134641..56652e98 100644
--- a/chrome/browser/engagement/site_engagement_helper_unittest.cc
+++ b/chrome/browser/engagement/site_engagement_helper_unittest.cc
@@ -8,13 +8,13 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/timer/mock_timer.h"
 #include "base/values.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/site_engagement/content/engagement_type.h"
 #include "components/site_engagement/content/site_engagement_metrics.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/page_navigator.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/engagement/site_engagement_service_factory.h b/chrome/browser/engagement/site_engagement_service_factory.h
index 53d4177..a699263 100644
--- a/chrome/browser/engagement/site_engagement_service_factory.h
+++ b/chrome/browser/engagement/site_engagement_service_factory.h
@@ -7,8 +7,8 @@
 
 #include "base/macros.h"
 #include "base/memory/singleton.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 
 class Profile;
 
diff --git a/chrome/browser/engagement/site_engagement_service_unittest.cc b/chrome/browser/engagement/site_engagement_service_unittest.cc
index 1775aae..d5970b96 100644
--- a/chrome/browser/engagement/site_engagement_service_unittest.cc
+++ b/chrome/browser/engagement/site_engagement_service_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/engagement/site_engagement_service.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 
 #include <algorithm>
 #include <map>
@@ -23,7 +23,6 @@
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/engagement/history_aware_site_engagement_service.h"
 #include "chrome/browser/engagement/site_engagement_helper.h"
-#include "chrome/browser/engagement/site_engagement_observer.h"
 #include "chrome/browser/engagement/site_engagement_service_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/common/chrome_switches.h"
@@ -40,6 +39,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/site_engagement/content/engagement_type.h"
 #include "components/site_engagement/content/site_engagement_metrics.h"
+#include "components/site_engagement/content/site_engagement_observer.h"
 #include "components/site_engagement/content/site_engagement_score.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 8c943ea..7ff55fa8 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -342,8 +342,6 @@
     "api/signed_in_devices/signed_in_devices_api.h",
     "api/signed_in_devices/signed_in_devices_manager.cc",
     "api/signed_in_devices/signed_in_devices_manager.h",
-    "api/spellcheck/spellcheck_api.cc",
-    "api/spellcheck/spellcheck_api.h",
     "api/storage/managed_value_store_cache.cc",
     "api/storage/managed_value_store_cache.h",
     "api/storage/policy_value_store.cc",
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
index 2be6cd6a..d844ae82 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
@@ -1043,6 +1043,7 @@
   auto* client = settings.per_profile ? profile_client_ : browser_client_;
   client->UploadSecurityEventReport(
       context_,
+      /* include_device_info */ !settings.per_profile,
       policy::RealtimeReportingJobConfiguration::BuildReport(
           std::move(event_list),
           reporting::GetContext(Profile::FromBrowserContext(context_))),
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
index c73893cd..55226593 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
@@ -62,7 +62,7 @@
 namespace {
 
 ACTION_P(CaptureArg, wrapper) {
-  *wrapper = arg1.Clone();
+  *wrapper = arg2.Clone();
 }
 
 constexpr char kConnectorsPrefValue[] = R"([
@@ -279,7 +279,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
       .WillOnce(CaptureArg(&report));
 
   TriggerOnPolicySpecifiedPasswordReuseDetectedEvent();
@@ -315,7 +315,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
       .WillOnce(CaptureArg(&report));
 
   TriggerOnPolicySpecifiedPasswordChangedEvent();
@@ -348,7 +348,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
       .WillOnce(CaptureArg(&report));
 
   TriggerOnDangerousDownloadOpenedEvent();
@@ -398,7 +398,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
       .WillOnce(CaptureArg(&report));
 
   TriggerOnSecurityInterstitialProceededEvent();
@@ -438,7 +438,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
       .WillOnce(CaptureArg(&report));
 
   TriggerOnSecurityInterstitialShownEvent();
@@ -478,7 +478,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
       .WillOnce(CaptureArg(&report));
 
   TriggerOnDangerousDownloadEvent();
@@ -520,7 +520,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
       .WillOnce(CaptureArg(&report));
 
   TriggerOnDangerousDownloadEventBypass();
@@ -560,7 +560,7 @@
       api::safe_browsing_private::OnSecurityInterstitialShown::kEventName);
   event_router_->AddEventObserver(&event_observer);
 
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _)).Times(1);
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _)).Times(1);
   TriggerOnSecurityInterstitialShownEvent();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1u, event_observer.PassEventArgs().GetList().size());
@@ -568,7 +568,7 @@
 
   // Now turn off policy.  This time no report should be generated.
   SetReportingPolicy(false);
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _)).Times(0);
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _)).Times(0);
   TriggerOnSecurityInterstitialShownEvent();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1u, event_observer.PassEventArgs().GetList().size());
@@ -587,7 +587,7 @@
 
   // Now turn on policy.
   SetReportingPolicy(true);
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _)).Times(1);
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _)).Times(1);
   TriggerOnSecurityInterstitialShownEvent();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1u, event_observer.PassEventArgs().GetList().size());
@@ -602,7 +602,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _)).Times(0);
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _)).Times(0);
 
   TriggerOnPolicySpecifiedPasswordReuseDetectedEvent();
   base::RunLoop().RunUntilIdle();
@@ -618,7 +618,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _)).Times(0);
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _)).Times(0);
 
   TriggerOnPolicySpecifiedPasswordChangedEvent();
   base::RunLoop().RunUntilIdle();
@@ -635,7 +635,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _)).Times(0);
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _)).Times(0);
 
   TriggerOnDangerousDownloadOpenedEvent();
   base::RunLoop().RunUntilIdle();
@@ -652,7 +652,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _)).Times(0);
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _)).Times(0);
 
   TriggerOnSecurityInterstitialProceededEvent();
   base::RunLoop().RunUntilIdle();
@@ -669,7 +669,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _)).Times(0);
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _)).Times(0);
 
   TriggerOnSecurityInterstitialShownEvent();
   base::RunLoop().RunUntilIdle();
@@ -686,7 +686,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _)).Times(0);
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _)).Times(0);
 
   TriggerOnDangerousDownloadEvent();
   base::RunLoop().RunUntilIdle();
@@ -703,7 +703,7 @@
   event_router_->AddEventObserver(&event_observer);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _)).Times(0);
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _)).Times(0);
 
   TriggerOnDangerousDownloadEventBypass();
   base::RunLoop().RunUntilIdle();
@@ -716,7 +716,7 @@
   SetUpRouters(/*realtime_reporting_enable=*/true, /*authorized=*/true);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
       .WillOnce(CaptureArg(&report));
 
   TriggerOnSensitiveDataEvent(safe_browsing::EventResult::ALLOWED);
@@ -766,7 +766,7 @@
   SetUpRouters(/*realtime_reporting_enable=*/true, /*authorized=*/true);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
       .WillOnce(CaptureArg(&report));
 
   TriggerOnSensitiveDataEvent(safe_browsing::EventResult::BLOCKED);
@@ -816,7 +816,7 @@
   SetUpRouters(/*realtime_reporting_enable=*/true, /*authorized=*/true);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
       .WillOnce(CaptureArg(&report));
 
   TriggerOnUnscannedFileEvent(safe_browsing::EventResult::ALLOWED);
@@ -860,7 +860,7 @@
   SetUpRouters(/*realtime_reporting_enable=*/true, /*authorized=*/true);
 
   base::Value report;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
       .WillOnce(CaptureArg(&report));
 
   TriggerOnUnscannedFileEvent(safe_browsing::EventResult::BLOCKED);
@@ -911,7 +911,7 @@
       ->SetIdentityManagerForTesting(
           identity_test_environment.identity_manager());
 
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
       .WillRepeatedly(Return());
 
   // With no primary account, we should not set the username.
@@ -1043,13 +1043,13 @@
 #endif
 
   if (should_report) {
-    EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _)).Times(1);
+    EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _)).Times(1);
   } else if (client_) {
     // Because the test will crate a |client_| object when the policy is
     // set, even if the feature flag or other conditions indicate that
     // reports should not be sent, it is possible that the pointer is not
     // null. In this case, make sure UploadSecurityEventReport() is not called.
-    EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _)).Times(0);
+    EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _)).Times(0);
   }
 
   TriggerOnPolicySpecifiedPasswordChangedEvent();
diff --git a/chrome/browser/extensions/api/spellcheck/spellcheck_api.cc b/chrome/browser/extensions/api/spellcheck/spellcheck_api.cc
deleted file mode 100644
index 0ba0c2d..0000000
--- a/chrome/browser/extensions/api/spellcheck/spellcheck_api.cc
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (c) 2012 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/extensions/api/spellcheck/spellcheck_api.h"
-
-#include "base/lazy_instance.h"
-#include "chrome/browser/spellchecker/spellcheck_factory.h"
-#include "chrome/browser/spellchecker/spellcheck_service.h"
-#include "chrome/common/extensions/api/spellcheck/spellcheck_handler.h"
-#include "extensions/common/manifest_constants.h"
-
-namespace extensions {
-
-namespace {
-
-SpellcheckDictionaryInfo* GetSpellcheckDictionaryInfo(
-    const Extension* extension) {
-  SpellcheckDictionaryInfo *spellcheck_info =
-      static_cast<SpellcheckDictionaryInfo*>(
-          extension->GetManifestData(manifest_keys::kSpellcheck));
-  return spellcheck_info;
-}
-
-SpellcheckService::DictionaryFormat GetDictionaryFormat(
-    const std::string& format) {
-  if (format == "hunspell") {
-    return SpellcheckService::DICT_HUNSPELL;
-  } else if (format == "text") {
-    return SpellcheckService::DICT_TEXT;
-  } else {
-    return SpellcheckService::DICT_UNKNOWN;
-  }
-}
-
-}  // namespace
-
-SpellcheckAPI::SpellcheckAPI(content::BrowserContext* context) {
-  extension_registry_observer_.Add(ExtensionRegistry::Get(context));
-}
-
-SpellcheckAPI::~SpellcheckAPI() = default;
-
-static base::LazyInstance<BrowserContextKeyedAPIFactory<SpellcheckAPI>>::
-    DestructorAtExit g_spellcheck_api_factory = LAZY_INSTANCE_INITIALIZER;
-
-// static
-BrowserContextKeyedAPIFactory<SpellcheckAPI>*
-SpellcheckAPI::GetFactoryInstance() {
-  return g_spellcheck_api_factory.Pointer();
-}
-
-void SpellcheckAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
-                                      const Extension* extension) {
-  SpellcheckDictionaryInfo* spellcheck_info =
-      GetSpellcheckDictionaryInfo(extension);
-  if (spellcheck_info) {
-    // TODO(rlp): Handle load failure. =
-    SpellcheckService* spellcheck =
-        SpellcheckServiceFactory::GetForContext(browser_context);
-    spellcheck->LoadExternalDictionary(
-        spellcheck_info->language,
-        spellcheck_info->locale,
-        spellcheck_info->path,
-        GetDictionaryFormat(spellcheck_info->format));
-  }
-}
-void SpellcheckAPI::OnExtensionUnloaded(
-    content::BrowserContext* browser_context,
-    const Extension* extension,
-    UnloadedExtensionReason reason) {
-  SpellcheckDictionaryInfo* spellcheck_info =
-      GetSpellcheckDictionaryInfo(extension);
-  if (spellcheck_info) {
-    // TODO(rlp): Handle unload failure.
-    SpellcheckService* spellcheck =
-        SpellcheckServiceFactory::GetForContext(browser_context);
-    spellcheck->UnloadExternalDictionary(spellcheck_info->path);
-  }
-}
-
-template <>
-void
-BrowserContextKeyedAPIFactory<SpellcheckAPI>::DeclareFactoryDependencies() {
-  DependsOn(SpellcheckServiceFactory::GetInstance());
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/extensions/api/spellcheck/spellcheck_api.h b/chrome/browser/extensions/api/spellcheck/spellcheck_api.h
deleted file mode 100644
index b75884c6..0000000
--- a/chrome/browser/extensions/api/spellcheck/spellcheck_api.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2013 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_EXTENSIONS_API_SPELLCHECK_SPELLCHECK_API_H_
-#define CHROME_BROWSER_EXTENSIONS_API_SPELLCHECK_SPELLCHECK_API_H_
-
-#include "base/macros.h"
-#include "base/scoped_observer.h"
-#include "extensions/browser/browser_context_keyed_api_factory.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/browser/extension_registry_observer.h"
-
-namespace extensions {
-
-class SpellcheckAPI : public BrowserContextKeyedAPI,
-                      public ExtensionRegistryObserver {
- public:
-  explicit SpellcheckAPI(content::BrowserContext* context);
-  ~SpellcheckAPI() override;
-
-  // BrowserContextKeyedAPI implementation.
-  static BrowserContextKeyedAPIFactory<SpellcheckAPI>* GetFactoryInstance();
-
- private:
-  friend class BrowserContextKeyedAPIFactory<SpellcheckAPI>;
-
-  // ExtensionRegistryObserver implementation.
-  void OnExtensionLoaded(content::BrowserContext* browser_context,
-                         const Extension* extension) override;
-  void OnExtensionUnloaded(content::BrowserContext* browser_context,
-                           const Extension* extension,
-                           UnloadedExtensionReason reason) override;
-
-  // BrowserContextKeyedAPI implementation.
-  static const char* service_name() {
-    return "SpellcheckAPI";
-  }
-
-  // Listen to extension load, unloaded notifications.
-  ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
-      extension_registry_observer_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(SpellcheckAPI);
-};
-
-template <>
-void BrowserContextKeyedAPIFactory<SpellcheckAPI>::DeclareFactoryDependencies();
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_EXTENSIONS_API_SPELLCHECK_SPELLCHECK_API_H_
diff --git a/chrome/browser/extensions/browser_context_keyed_service_factories.cc b/chrome/browser/extensions/browser_context_keyed_service_factories.cc
index 7591f15..925218d 100644
--- a/chrome/browser/extensions/browser_context_keyed_service_factories.cc
+++ b/chrome/browser/extensions/browser_context_keyed_service_factories.cc
@@ -49,7 +49,6 @@
 #include "chrome/browser/extensions/warning_badge_service_factory.h"
 #include "chrome/browser/speech/extension_api/tts_extension_api.h"
 #include "chrome/common/buildflags.h"
-#include "components/spellcheck/spellcheck_buildflags.h"
 #include "extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.h"
 #include "extensions/browser/api/networking_private/networking_private_delegate_factory.h"
 #include "ppapi/buildflags/buildflags.h"
@@ -67,10 +66,6 @@
 #include "chrome/browser/extensions/api/mdns/mdns_api.h"
 #endif
 
-#if BUILDFLAG(ENABLE_SPELLCHECK)
-#include "chrome/browser/extensions/api/spellcheck/spellcheck_api.h"
-#endif
-
 #if BUILDFLAG(ENABLE_AUTOFILL_ASSISTANT_API)
 #include "chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.h"
 #endif
@@ -130,9 +125,6 @@
   extensions::SettingsPrivateEventRouterFactory::GetInstance();
   extensions::SettingsOverridesAPI::GetFactoryInstance();
   extensions::SignedInDevicesManager::GetFactoryInstance();
-#if BUILDFLAG(ENABLE_SPELLCHECK)
-  extensions::SpellcheckAPI::GetFactoryInstance();
-#endif
   extensions::SystemIndicatorManagerFactory::GetInstance();
   extensions::TabCaptureRegistry::GetFactoryInstance();
   extensions::TabsWindowsAPI::GetFactoryInstance();
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc b/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc
index ec61213..415f06b1 100644
--- a/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc
+++ b/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc
@@ -61,8 +61,8 @@
       return ForceInstalledMetrics::UserType::USER_TYPE_GUEST;
     case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
       return ForceInstalledMetrics::UserType::USER_TYPE_PUBLIC_ACCOUNT;
-    case user_manager::USER_TYPE_SUPERVISED:
-      return ForceInstalledMetrics::UserType::USER_TYPE_SUPERVISED;
+    case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
+      return ForceInstalledMetrics::UserType::USER_TYPE_SUPERVISED_DEPRECATED;
     case user_manager::USER_TYPE_KIOSK_APP:
       return ForceInstalledMetrics::UserType::USER_TYPE_KIOSK_APP;
     case user_manager::USER_TYPE_CHILD:
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_metrics.h b/chrome/browser/extensions/forced_extensions/force_installed_metrics.h
index 06fe74f..10d46ea 100644
--- a/chrome/browser/extensions/forced_extensions/force_installed_metrics.h
+++ b/chrome/browser/extensions/forced_extensions/force_installed_metrics.h
@@ -46,7 +46,9 @@
     // Session with Regular new user, which has a user name and password.
     USER_TYPE_REGULAR_NEW = 2,
     USER_TYPE_PUBLIC_ACCOUNT = 3,
-    USER_TYPE_SUPERVISED = 4,
+    // TODO(crbug/1155729): Legacy supervised users are deprecated. Use
+    // USER_TYPE_CHILD instead. Remove this enum.
+    USER_TYPE_SUPERVISED_DEPRECATED = 4,
     USER_TYPE_KIOSK_APP = 5,
     USER_TYPE_CHILD = 6,
     USER_TYPE_ARC_KIOSK_APP = 7,
diff --git a/chrome/browser/federated_learning/floc_eligibility_browsertest.cc b/chrome/browser/federated_learning/floc_eligibility_browsertest.cc
new file mode 100644
index 0000000..6ed725d9
--- /dev/null
+++ b/chrome/browser/federated_learning/floc_eligibility_browsertest.cc
@@ -0,0 +1,246 @@
+// 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/strings/strcat.h"
+#include "base/test/bind.h"
+#include "chrome/browser/federated_learning/floc_id_provider.h"
+#include "chrome/browser/federated_learning/floc_id_provider_factory.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/embedder_support/switches.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
+#include "components/subresource_filter/core/common/test_ruleset_utils.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_frame_navigation_observer.h"
+#include "content/public/test/test_host_resolver.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "net/dns/mock_host_resolver.h"
+
+namespace {
+
+class FixedFlocIdProvider : public federated_learning::FlocIdProvider {
+ public:
+  FixedFlocIdProvider() = default;
+  ~FixedFlocIdProvider() override = default;
+
+  std::string GetInterestCohortForJsApi(
+      const GURL& url,
+      const base::Optional<url::Origin>& top_frame_origin) const override {
+    return "12345.6.7.8.9";
+  }
+};
+
+}  // namespace
+
+class FlocEligibilityBrowserTest
+    : public subresource_filter::SubresourceFilterBrowserTest {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
+                                    "InterestCohortAPI");
+  }
+
+  // BrowserTestBase::SetUpInProcessBrowserTestFixture
+  void SetUpInProcessBrowserTestFixture() override {
+    subscription_ =
+        BrowserContextDependencyManager::GetInstance()
+            ->RegisterCreateServicesCallbackForTesting(base::BindRepeating(
+                &FlocEligibilityBrowserTest::OnWillCreateBrowserContextServices,
+                base::Unretained(this)));
+  }
+
+  std::string InvokeInterestCohortJsApi(
+      const content::ToRenderFrameHost& adapter) {
+    return EvalJs(adapter, R"(
+      document.interestCohort()
+      .then(floc => floc)
+      .catch(error => 'rejected');
+    )")
+        .ExtractString();
+  }
+
+  bool HistoryContainsUrlVisit(const GURL& url) {
+    return QueryUrl(url).success;
+  }
+
+  bool IsUrlVisitEligibleToComputeFloc(const GURL& url) {
+    history::QueryURLResult result = QueryUrl(url);
+    EXPECT_EQ(1u, result.visits.size());
+    return result.visits[0].floc_allowed;
+  }
+
+  history::QueryURLResult QueryUrl(const GURL& url) {
+    history::QueryURLResult query_url_result;
+
+    base::RunLoop run_loop;
+    base::CancelableTaskTracker tracker;
+    history_service()->QueryURL(
+        url, /*want_visits=*/true,
+        base::BindLambdaForTesting([&](history::QueryURLResult result) {
+          query_url_result = std::move(result);
+          run_loop.Quit();
+        }),
+        &tracker);
+    run_loop.Run();
+
+    return query_url_result;
+  }
+
+  void DeleteAllHistory() {
+    base::RunLoop run_loop;
+    base::CancelableTaskTracker tracker;
+    HistoryServiceFactory::GetForProfile(browser()->profile(),
+                                         ServiceAccessType::EXPLICIT_ACCESS)
+        ->ExpireHistoryBetween(
+            /*restrict_urls=*/{}, /*begin_time=*/base::Time(),
+            base::Time::Max(),
+            /*user_initiated=*/true,
+            base::BindLambdaForTesting([&]() { run_loop.Quit(); }), &tracker);
+    run_loop.Run();
+  }
+
+  content::WebContents* web_contents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  history::HistoryService* history_service() {
+    return HistoryServiceFactory::GetForProfile(
+        browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS);
+  }
+
+  void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
+    federated_learning::FlocIdProviderFactory::GetInstance()->SetTestingFactory(
+        context, base::BindRepeating(
+                     &FlocEligibilityBrowserTest::CreateFixedFlocIdProvider,
+                     base::Unretained(this)));
+  }
+
+  std::unique_ptr<KeyedService> CreateFixedFlocIdProvider(
+      content::BrowserContext* context) {
+    return std::make_unique<FixedFlocIdProvider>();
+  }
+
+  GURL NavigateToTestPage() {
+    auto waiter =
+        std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>(
+            web_contents());
+
+    GURL main_page_url(embedded_test_server()->GetURL(
+        "a.test", "/ad_tagging/frame_factory.html"));
+
+    ui_test_utils::NavigateToURL(browser(), main_page_url);
+
+    // Four resources in the main frame and one favicon.
+    waiter->AddMinimumCompleteResourcesExpectation(5);
+    waiter->Wait();
+
+    return main_page_url;
+  }
+
+ protected:
+  base::CallbackListSubscription subscription_;
+};
+
+IN_PROC_BROWSER_TEST_F(FlocEligibilityBrowserTest, NotEligibleByDefault) {
+  net::IPAddress::ConsiderLoopbackIPToBePubliclyRoutableForTesting();
+
+  GURL main_page_url = NavigateToTestPage();
+
+  ASSERT_TRUE(HistoryContainsUrlVisit(main_page_url));
+
+  // Expect that the navigation history is not eligible for floc computation.
+  EXPECT_FALSE(IsUrlVisitEligibleToComputeFloc(main_page_url));
+}
+
+IN_PROC_BROWSER_TEST_F(FlocEligibilityBrowserTest, EligibleAfterAdResource) {
+  net::IPAddress::ConsiderLoopbackIPToBePubliclyRoutableForTesting();
+
+  SetRulesetWithRules(
+      {subresource_filter::testing::CreateSuffixRule("ad_script.js")});
+
+  GURL main_page_url = NavigateToTestPage();
+
+  // Expect that the navigation history is eligible for floc computation as the
+  // page contains an ad resource.
+  EXPECT_TRUE(IsUrlVisitEligibleToComputeFloc(main_page_url));
+}
+
+IN_PROC_BROWSER_TEST_F(FlocEligibilityBrowserTest, EligibleAfterApiCall) {
+  net::IPAddress::ConsiderLoopbackIPToBePubliclyRoutableForTesting();
+
+  GURL main_page_url = NavigateToTestPage();
+
+  ASSERT_TRUE(HistoryContainsUrlVisit(main_page_url));
+  EXPECT_FALSE(IsUrlVisitEligibleToComputeFloc(main_page_url));
+
+  // Expect that the navigation history is eligible for floc computation after
+  // an API call.
+  EXPECT_EQ("12345.6.7.8.9", InvokeInterestCohortJsApi(web_contents()));
+  EXPECT_TRUE(IsUrlVisitEligibleToComputeFloc(main_page_url));
+}
+
+IN_PROC_BROWSER_TEST_F(FlocEligibilityBrowserTest, NotEligibleDueToPrivateIP) {
+  SetRulesetWithRules(
+      {subresource_filter::testing::CreateSuffixRule("ad_script.js")});
+
+  GURL main_page_url = NavigateToTestPage();
+
+  // Expect that the navigation history is not eligible for floc computation as
+  // the IP was not publicly routable.
+  EXPECT_FALSE(IsUrlVisitEligibleToComputeFloc(main_page_url));
+}
+
+IN_PROC_BROWSER_TEST_F(FlocEligibilityBrowserTest, NotEligibleSubframeHistory) {
+  net::IPAddress::ConsiderLoopbackIPToBePubliclyRoutableForTesting();
+
+  GURL main_page_url(embedded_test_server()->GetURL("a.test", "/iframe.html"));
+  GURL auto_subframe_url(
+      embedded_test_server()->GetURL("a.test", "/title1.html"));
+
+  // Navigate to a page that contains an iframe ("title1.html").
+  ui_test_utils::NavigateToURL(browser(), main_page_url);
+
+  ASSERT_TRUE(HistoryContainsUrlVisit(main_page_url));
+  ASSERT_FALSE(HistoryContainsUrlVisit(auto_subframe_url));
+
+  // Trigger an user-initiated navigation on the iframe, so that it will show up
+  // in history.
+  GURL manual_subframe_url(
+      embedded_test_server()->GetURL("a.test", "/title2.html"));
+  content::NavigateIframeToURL(web_contents(),
+                               /*iframe_id=*/"test", manual_subframe_url);
+  ASSERT_TRUE(HistoryContainsUrlVisit(manual_subframe_url));
+
+  EXPECT_EQ("12345.6.7.8.9", InvokeInterestCohortJsApi(web_contents()));
+  EXPECT_EQ("12345.6.7.8.9", InvokeInterestCohortJsApi(content::ChildFrameAt(
+                                 web_contents()->GetMainFrame(), 0)));
+
+  // Expect that only the main frame navigation history is eligible for floc
+  // computation.
+  EXPECT_TRUE(IsUrlVisitEligibleToComputeFloc(main_page_url));
+  EXPECT_FALSE(IsUrlVisitEligibleToComputeFloc(manual_subframe_url));
+}
+
+IN_PROC_BROWSER_TEST_F(FlocEligibilityBrowserTest,
+                       SettingFlocAllowedNoopOnDeletedHistory) {
+  net::IPAddress::ConsiderLoopbackIPToBePubliclyRoutableForTesting();
+
+  GURL main_page_url = NavigateToTestPage();
+
+  ASSERT_TRUE(HistoryContainsUrlVisit(main_page_url));
+
+  DeleteAllHistory();
+
+  // Expect that attempting to set the "floc allowed" bit will be a no-op if the
+  // page visit doesn't exist.
+  EXPECT_EQ("12345.6.7.8.9", InvokeInterestCohortJsApi(web_contents()));
+  ASSERT_FALSE(HistoryContainsUrlVisit(main_page_url));
+}
diff --git a/chrome/browser/federated_learning/floc_eligibility_observer.cc b/chrome/browser/federated_learning/floc_eligibility_observer.cc
new file mode 100644
index 0000000..1528acf
--- /dev/null
+++ b/chrome/browser/federated_learning/floc_eligibility_observer.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/federated_learning/floc_eligibility_observer.h"
+
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/history/content/browser/history_context_helper.h"
+#include "components/history/core/browser/history_service.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/web_contents.h"
+
+namespace federated_learning {
+
+FlocEligibilityObserver::~FlocEligibilityObserver() = default;
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+FlocEligibilityObserver::OnCommit(
+    content::NavigationHandle* navigation_handle) {
+  // At this point the add-page-to-history decision should have been made,
+  // because history is added in HistoryTabHelper::DidFinishNavigation, and this
+  // OnEligibleCommit method is invoked in the same broadcasting family through
+  // MetricsWebContentsObserver::DidFinishNavigation.
+
+  // TODO(yaoxia): Perhaps we want an explicit signal for "the page was added
+  // to history or was ineligible". This way we don't need to count on the above
+  // relation, and can also stop observing if the history was not added.
+
+  // If the IP was not publicly routable, the navigation history is not eligible
+  // for floc. We can stop observing now.
+  if (!navigation_handle->GetSocketAddress().address().IsPubliclyRoutable())
+    return ObservePolicy::STOP_OBSERVING;
+
+  DCHECK(!eligible_commit_);
+  eligible_commit_ = true;
+
+  return ObservePolicy::CONTINUE_OBSERVING;
+}
+
+void FlocEligibilityObserver::OnAdResource() {
+  MaybeSetFlocAllowedInHistory();
+}
+
+void FlocEligibilityObserver::OnInterestCohortApiUsed() {
+  MaybeSetFlocAllowedInHistory();
+}
+
+FlocEligibilityObserver::FlocEligibilityObserver(content::RenderFrameHost* rfh)
+    : web_contents_(content::WebContents::FromRenderFrameHost(rfh)) {}
+
+void FlocEligibilityObserver::MaybeSetFlocAllowedInHistory() {
+  if (!eligible_commit_ || did_set_floc_allowed_)
+    return;
+
+  history::HistoryService* hs = HistoryServiceFactory::GetForProfile(
+      Profile::FromBrowserContext(web_contents_->GetBrowserContext()),
+      ServiceAccessType::IMPLICIT_ACCESS);
+
+  hs->SetFlocAllowed(
+      history::ContextIDForWebContents(web_contents_),
+      web_contents_->GetController().GetLastCommittedEntry()->GetUniqueID(),
+      web_contents_->GetLastCommittedURL());
+
+  did_set_floc_allowed_ = true;
+}
+
+RENDER_DOCUMENT_HOST_USER_DATA_KEY_IMPL(FlocEligibilityObserver)
+
+}  // namespace federated_learning
diff --git a/chrome/browser/federated_learning/floc_eligibility_observer.h b/chrome/browser/federated_learning/floc_eligibility_observer.h
new file mode 100644
index 0000000..f842fa71
--- /dev/null
+++ b/chrome/browser/federated_learning/floc_eligibility_observer.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_FEDERATED_LEARNING_FLOC_ELIGIBILITY_OBSERVER_H_
+#define CHROME_BROWSER_FEDERATED_LEARNING_FLOC_ELIGIBILITY_OBSERVER_H_
+
+#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
+#include "content/public/browser/render_document_host_user_data.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace federated_learning {
+
+// This observer monitors page-level (i.e. main document) signals to determine
+// whether the navigation's associated history entry is eligible for floc
+// computation. The history entry is eligible for floc computation if all of the
+// following conditions hold:
+// 1) the IP of the navigation was publicly routable.
+// 2) either the page has an ad resource, or the document.interestCohort API is
+// used in the page.
+//
+// When the page is considered eligible for floc computation, a corresponding
+// HistoryService API will be called to persistently set the eligibility bit.
+class FlocEligibilityObserver
+    : public content::RenderDocumentHostUserData<FlocEligibilityObserver> {
+ public:
+  using ObservePolicy =
+      page_load_metrics::PageLoadMetricsObserver::ObservePolicy;
+
+  ~FlocEligibilityObserver() override;
+
+  // Called when the navigation has committed. Returns whether it should
+  // continue observing floc related signals.
+  ObservePolicy OnCommit(content::NavigationHandle* navigation_handle);
+
+  // Called when an ad resource is seen in the page.
+  void OnAdResource();
+
+  // Called when the document.interestCohort API is used in the page.
+  void OnInterestCohortApiUsed();
+
+ private:
+  explicit FlocEligibilityObserver(content::RenderFrameHost* rfh);
+
+  friend class content::RenderDocumentHostUserData<FlocEligibilityObserver>;
+
+  void MaybeSetFlocAllowedInHistory();
+
+  content::WebContents* web_contents_;
+
+  // |eligible_commit_| means all the commit time prerequisites are met
+  // (i.e. IP was publicly routable). It can only be set to true at commit time.
+  // When it's set, it also implies that the add-page-to-history decision has
+  // been made, i.e. either the page has been added to history, or has been
+  // skipped.
+  bool eligible_commit_ = false;
+
+  bool did_set_floc_allowed_ = false;
+
+  RENDER_DOCUMENT_HOST_USER_DATA_KEY_DECL();
+};
+
+}  // namespace federated_learning
+
+#endif  // CHROME_BROWSER_FEDERATED_LEARNING_FLOC_ELIGIBILITY_OBSERVER_H_
diff --git a/chrome/browser/federated_learning/floc_eligibility_unittest.cc b/chrome/browser/federated_learning/floc_eligibility_unittest.cc
new file mode 100644
index 0000000..3ecc3b9
--- /dev/null
+++ b/chrome/browser/federated_learning/floc_eligibility_unittest.cc
@@ -0,0 +1,160 @@
+// 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/bind.h"
+#include "chrome/browser/federated_learning/floc_eligibility_observer.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/page_load_metrics/observers/ad_metrics/floc_page_load_metrics_observer.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/history/content/browser/history_context_helper.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h"
+#include "components/page_load_metrics/browser/page_load_tracker.h"
+#include "components/page_load_metrics/common/page_load_metrics.mojom.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/test/navigation_simulator.h"
+
+namespace federated_learning {
+
+class FlocEligibilityUnitTest : public ChromeRenderViewHostTestHarness {
+ public:
+  FlocEligibilityUnitTest() = default;
+  ~FlocEligibilityUnitTest() override = default;
+
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+    ASSERT_TRUE(profile()->CreateHistoryService());
+
+    tester_ =
+        std::make_unique<page_load_metrics::PageLoadMetricsObserverTester>(
+            web_contents(), this,
+            base::BindRepeating(&FlocEligibilityUnitTest::RegisterObservers,
+                                base::Unretained(this)));
+  }
+
+  history::HistoryService* history_service() {
+    return HistoryServiceFactory::GetForProfile(
+        profile(), ServiceAccessType::EXPLICIT_ACCESS);
+  }
+
+  bool IsUrlVisitEligibleToComputeFloc(const GURL& url) {
+    history::QueryURLResult result = QueryUrl(url);
+    EXPECT_EQ(1u, result.visits.size());
+    return result.visits[0].floc_allowed;
+  }
+
+  history::QueryURLResult QueryUrl(const GURL& url) {
+    history::QueryURLResult query_url_result;
+
+    base::RunLoop run_loop;
+    base::CancelableTaskTracker tracker;
+    history_service()->QueryURL(
+        url, /*want_visits=*/true,
+        base::BindLambdaForTesting([&](history::QueryURLResult result) {
+          query_url_result = std::move(result);
+          run_loop.Quit();
+        }),
+        &tracker);
+    run_loop.Run();
+
+    return query_url_result;
+  }
+
+  void SimulateResourceDataUseUpdate(bool is_ad_resource) {
+    page_load_metrics::mojom::ResourceDataUpdatePtr resource =
+        page_load_metrics::mojom::ResourceDataUpdate::New();
+    resource->reported_as_ad_resource = is_ad_resource;
+    resource->received_data_length = 1;
+
+    std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr> resources;
+    resources.push_back(std::move(resource));
+
+    tester_->SimulateResourceDataUseUpdate(resources,
+                                           web_contents()->GetMainFrame());
+  }
+
+  void NavigateToPage(const GURL& url, bool publicly_routable) {
+    auto simulator = content::NavigationSimulator::CreateBrowserInitiated(
+        url, web_contents());
+    simulator->SetTransition(ui::PageTransition::PAGE_TRANSITION_TYPED);
+
+    if (!publicly_routable) {
+      net::IPAddress address;
+      EXPECT_TRUE(address.AssignFromIPLiteral("0.0.0.0"));
+      simulator->SetSocketAddress(net::IPEndPoint(address, /*port=*/0));
+    }
+
+    simulator->Commit();
+
+    history_service()->AddPage(
+        url, base::Time::Now(),
+        history::ContextIDForWebContents(web_contents()),
+        web_contents()->GetController().GetLastCommittedEntry()->GetUniqueID(),
+        /*referrer=*/GURL(),
+        /*redirects=*/{}, ui::PageTransition::PAGE_TRANSITION_TYPED,
+        history::VisitSource::SOURCE_BROWSED,
+        /*did_replace_entry=*/false,
+        /*floc_allowed=*/false);
+  }
+
+  FlocEligibilityObserver* GetFlocEligibilityObserver() {
+    return FlocEligibilityObserver::GetOrCreateForCurrentDocument(
+        web_contents()->GetMainFrame());
+  }
+
+ private:
+  void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) {
+    auto floc_plm_observer = std::make_unique<FlocPageLoadMetricsObserver>();
+    floc_plm_observer_ = floc_plm_observer.get();
+    tracker->AddObserver(std::move(floc_plm_observer));
+  }
+
+  FlocPageLoadMetricsObserver* floc_plm_observer_;
+  std::unique_ptr<page_load_metrics::PageLoadMetricsObserverTester> tester_;
+};
+
+TEST_F(FlocEligibilityUnitTest, OnInterestCohortApiUsed) {
+  GURL url("https://foo.com");
+  NavigateToPage(url, /*publicly_routable=*/true);
+
+  EXPECT_FALSE(IsUrlVisitEligibleToComputeFloc(url));
+
+  GetFlocEligibilityObserver()->OnInterestCohortApiUsed();
+  EXPECT_TRUE(IsUrlVisitEligibleToComputeFloc(url));
+}
+
+TEST_F(FlocEligibilityUnitTest, OnAdResourceObserved) {
+  GURL url("https://foo.com");
+  NavigateToPage(url, /*publicly_routable=*/true);
+
+  EXPECT_FALSE(IsUrlVisitEligibleToComputeFloc(url));
+
+  SimulateResourceDataUseUpdate(/*is_ad_resource=*/true);
+  EXPECT_TRUE(IsUrlVisitEligibleToComputeFloc(url));
+}
+
+TEST_F(FlocEligibilityUnitTest, OnNonAdResourceObserved) {
+  GURL url("https://foo.com");
+  NavigateToPage(url, /*publicly_routable=*/true);
+
+  EXPECT_FALSE(IsUrlVisitEligibleToComputeFloc(url));
+
+  SimulateResourceDataUseUpdate(/*is_ad_resource=*/false);
+  EXPECT_FALSE(IsUrlVisitEligibleToComputeFloc(url));
+}
+
+TEST_F(FlocEligibilityUnitTest, StopObservingPrivateIP) {
+  GURL url("https://foo.com");
+  NavigateToPage(url, /*publicly_routable=*/false);
+
+  EXPECT_FALSE(IsUrlVisitEligibleToComputeFloc(url));
+
+  SimulateResourceDataUseUpdate(/*is_ad_resource=*/true);
+  EXPECT_FALSE(IsUrlVisitEligibleToComputeFloc(url));
+
+  GetFlocEligibilityObserver()->OnInterestCohortApiUsed();
+  EXPECT_FALSE(IsUrlVisitEligibleToComputeFloc(url));
+}
+
+}  // namespace federated_learning
diff --git a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
index 11459e1654a..b001aa3 100644
--- a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
+++ b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
@@ -137,7 +137,7 @@
       return "guest";
     case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
       return "public_account";
-    case user_manager::USER_TYPE_SUPERVISED:
+    case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
       return "supervised";
     case user_manager::USER_TYPE_KIOSK_APP:
       return "kiosk_app";
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 092abbea..f1df1d25 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1067,7 +1067,7 @@
   },
   {
     "name": "dynamic-color-gamut",
-    "owners": [ "cblume", "khushalsagar", "ccameron" ],
+    "owners": [ "cblume", "ccameron" ],
     "expiry_milestone": 90
   },
   {
@@ -1760,7 +1760,7 @@
   },
   {
     "name": "enable-image-reader",
-    "owners": [ "vikassoni", "khushalsagar" ],
+    "owners": [ "vikassoni", "liberato" ],
     "expiry_milestone": 90
   },
   {
@@ -1993,12 +1993,12 @@
   },
   {
     "name": "enable-oop-rasterization",
-    "owners": [ "enne", "khushalsagar" ],
+    "owners": [ "sunnyps", "vasilyt" ],
     "expiry_milestone": 86
   },
   {
     "name": "enable-oop-rasterization-ddl",
-    "owners": [ "robertphillips", "khushalsagar" ],
+    "owners": [ "robertphillips", "penghuang" ],
     "expiry_milestone": 87
   },
   {
@@ -2242,7 +2242,7 @@
   },
   {
     "name": "enable-surface-control",
-    "owners": [ "vikassoni", "khushalsagar" ],
+    "owners": [ "vikassoni", "vasilyt" ],
     "expiry_milestone": 90
   },
   {
@@ -3528,11 +3528,6 @@
     "expiry_milestone": 86
   },
   {
-    "name": "offline-pages-load-signal-collecting",
-    "owners": [ "sclittle", "srsudar", "offline-dev" ],
-    "expiry_milestone": 86
-  },
-  {
     "name": "offline-pages-pending-download",
     "owners": [ "sclittle", "srsudar", "offline-dev" ],
     "expiry_milestone": 76
@@ -3543,16 +3538,6 @@
     "expiry_milestone": 76
   },
   {
-    "name": "offline-pages-renovations",
-    "owners": [ "sclittle", "srsudar", "offline-dev" ],
-    "expiry_milestone": 86
-  },
-  {
-    "name": "offline-pages-resource-based-snapshot",
-    "owners": [ "sclittle", "srsudar", "offline-dev" ],
-    "expiry_milestone": 86
-  },
-  {
     "name": "offlining-recent-pages",
     "owners": [ "sclittle", "srsudar", "offline-dev" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 8fdf87a..34cf6b2 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2921,31 +2921,12 @@
     "When enabled offline pages launched from the Downloads Home will be "
     "opened in Chrome Custom Tabs (CCT) instead of regular tabs.";
 
-const char kOfflinePagesLoadSignalCollectingName[] =
-    "Enables collecting load timing data for offline page snapshots.";
-const char kOfflinePagesLoadSignalCollectingDescription[] =
-    "Enables loading completeness data collection while writing an offline "
-    "page.  This data is collected in the snapshotted offline page to allow "
-    "data analysis to improve deciding when to make the offline snapshot.";
-
 const char kOfflinePagesPrefetchingName[] =
     "Enables suggested offline pages to be prefetched.";
 const char kOfflinePagesPrefetchingDescription[] =
     "Enables suggested offline pages to be prefetched, so useful content is "
     "available while offline.";
 
-const char kOfflinePagesResourceBasedSnapshotName[] =
-    "Enables offline page snapshots to be based on percentage of page loaded.";
-const char kOfflinePagesResourceBasedSnapshotDescription[] =
-    "Enables offline page snapshots to use a resource percentage based "
-    "approach for determining when the page is loaded as opposed to a time "
-    "based approach";
-
-const char kOfflinePagesRenovationsName[] = "Enables offline page renovations.";
-const char kOfflinePagesRenovationsDescription[] =
-    "Enables offline page renovations which correct issues with dynamic "
-    "content that occur when offlining pages that use JavaScript.";
-
 const char kOfflinePagesLivePageSharingName[] =
     "Enables live page sharing of offline pages";
 const char kOfflinePagesLivePageSharingDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index a887fc5..ea8bf99 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1705,18 +1705,9 @@
 extern const char kOfflinePagesInDownloadHomeOpenInCctName[];
 extern const char kOfflinePagesInDownloadHomeOpenInCctDescription[];
 
-extern const char kOfflinePagesLoadSignalCollectingName[];
-extern const char kOfflinePagesLoadSignalCollectingDescription[];
-
 extern const char kOfflinePagesPrefetchingName[];
 extern const char kOfflinePagesPrefetchingDescription[];
 
-extern const char kOfflinePagesResourceBasedSnapshotName[];
-extern const char kOfflinePagesResourceBasedSnapshotDescription[];
-
-extern const char kOfflinePagesRenovationsName[];
-extern const char kOfflinePagesRenovationsDescription[];
-
 extern const char kOfflinePagesLivePageSharingName[];
 extern const char kOfflinePagesLivePageSharingDescription[];
 
diff --git a/chrome/browser/history/history_tab_helper.cc b/chrome/browser/history/history_tab_helper.cc
index 50ff785..10691cf 100644
--- a/chrome/browser/history/history_tab_helper.cc
+++ b/chrome/browser/history/history_tab_helper.cc
@@ -96,18 +96,16 @@
                                                 page_transition);
   }
 
-  // In the future, this may encapsulate more conditions, e.g. page level
-  // opt-in, opt-out, etc.
-  bool floc_allowed =
-      navigation_handle->GetSocketAddress().address().IsPubliclyRoutable();
-
+  // Note: floc_allowed is set to false initially and is later updated by the
+  // floc eligibility observer. Eventually it will be removed from the history
+  // service API.
   history::HistoryAddPageArgs add_page_args(
       navigation_handle->GetURL(), timestamp,
       history::ContextIDForWebContents(web_contents()), nav_entry_id,
       navigation_handle->GetReferrer().url,
       navigation_handle->GetRedirectChain(), page_transition, hidden,
       history::SOURCE_BROWSED, navigation_handle->DidReplaceEntry(),
-      !content_suggestions_navigation, floc_allowed,
+      !content_suggestions_navigation, /*floc_allowed=*/false,
       navigation_handle->IsSameDocument()
           ? base::Optional<base::string16>(
                 navigation_handle->GetWebContents()->GetTitle())
diff --git a/chrome/browser/history/top_sites_factory.cc b/chrome/browser/history/top_sites_factory.cc
index 3829678e..6dbb94b 100644
--- a/chrome/browser/history/top_sites_factory.cc
+++ b/chrome/browser/history/top_sites_factory.cc
@@ -15,7 +15,6 @@
 #include "base/stl_util.h"
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/engagement/site_engagement_service_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/history_utils.h"
@@ -32,6 +31,7 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "components/search/ntp_features.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
index 39d36f8..ef249b1 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
@@ -17,7 +17,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/lookalikes/lookalike_url_blocking_page.h"
 #include "chrome/browser/lookalikes/lookalike_url_controller_client.h"
 #include "chrome/browser/lookalikes/lookalike_url_service.h"
@@ -30,6 +29,7 @@
 #include "components/no_state_prefetch/browser/prerender_contents.h"
 #include "components/reputation/core/safety_tips_config.h"
 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/ukm/content/source_url_recorder.h"
 #include "components/url_formatter/spoof_checks/top_domains/top500_domains.h"
 #include "components/url_formatter/spoof_checks/top_domains/top_domain_util.h"
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
index e4194e3..012646cd 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
@@ -11,7 +11,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "build/build_config.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/history_test_utils.h"
 #include "chrome/browser/lookalikes/lookalike_url_blocking_page.h"
@@ -33,6 +32,7 @@
 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "components/security_interstitials/core/metrics_helper.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
diff --git a/chrome/browser/lookalikes/lookalike_url_service.cc b/chrome/browser/lookalikes/lookalike_url_service.cc
index 72c67a66..01343e8 100644
--- a/chrome/browser/lookalikes/lookalike_url_service.cc
+++ b/chrome/browser/lookalikes/lookalike_url_service.cc
@@ -15,7 +15,6 @@
 #include "base/task/thread_pool.h"
 #include "base/time/default_clock.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/engagement/site_engagement_service_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
@@ -24,6 +23,7 @@
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/lookalikes/core/lookalike_url_util.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/url_formatter/spoof_checks/top_domains/top_domain_util.h"
 #include "components/url_formatter/url_formatter.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
diff --git a/chrome/browser/media/media_engagement_score_unittest.cc b/chrome/browser/media/media_engagement_score_unittest.cc
index 0fc111d..ec35cf19 100644
--- a/chrome/browser/media/media_engagement_score_unittest.cc
+++ b/chrome/browser/media/media_engagement_score_unittest.cc
@@ -13,11 +13,11 @@
 #include "base/test/simple_test_clock.h"
 #include "base/values.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "media/base/media_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/notifications/persistent_notification_handler.cc b/chrome/browser/notifications/persistent_notification_handler.cc
index 443fe4d69..6d5ba89 100644
--- a/chrome/browser/notifications/persistent_notification_handler.cc
+++ b/chrome/browser/notifications/persistent_notification_handler.cc
@@ -8,7 +8,6 @@
 #include "base/callback.h"
 #include "base/check_op.h"
 #include "base/metrics/histogram_macros.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/notifications/metrics/notification_metrics_logger.h"
 #include "chrome/browser/notifications/metrics/notification_metrics_logger_factory.h"
 #include "chrome/browser/notifications/notification_common.h"
@@ -18,6 +17,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "components/permissions/permission_uma_util.h"
 #include "components/permissions/permission_util.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_event_dispatcher.h"
 #include "content/public/browser/permission_controller.h"
diff --git a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
index 1c7fb14..13cfe98 100644
--- a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
+++ b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
@@ -20,7 +20,6 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/notifications/notification_common.h"
 #include "chrome/browser/notifications/notification_display_service_impl.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
@@ -43,6 +42,7 @@
 #include "components/permissions/permission_request_manager.h"
 #include "components/permissions/permission_result.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
diff --git a/chrome/browser/offline_pages/background_loader_offliner.cc b/chrome/browser/offline_pages/background_loader_offliner.cc
index 4b7b1d1d..97fdbd5 100644
--- a/chrome/browser/offline_pages/background_loader_offliner.cc
+++ b/chrome/browser/offline_pages/background_loader_offliner.cc
@@ -24,15 +24,12 @@
 #include "chrome/browser/ssl/security_state_tab_helper.h"
 #include "chrome/common/chrome_isolated_world_ids.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
-#include "components/offline_pages/content/renovations/render_frame_script_injector.h"
 #include "components/offline_pages/core/background/offliner_policy.h"
 #include "components/offline_pages/core/background/save_page_request.h"
 #include "components/offline_pages/core/client_namespace_constants.h"
 #include "components/offline_pages/core/offline_page_client_policy.h"
 #include "components/offline_pages/core/offline_page_feature.h"
 #include "components/offline_pages/core/offline_page_model.h"
-#include "components/offline_pages/core/renovations/page_renovation_loader.h"
-#include "components/offline_pages/core/renovations/page_renovator.h"
 #include "components/previews/content/previews_user_data.h"
 #include "components/security_state/core/security_state.h"
 #include "content/public/browser/browser_context.h"
@@ -49,10 +46,6 @@
 namespace offline_pages {
 
 namespace {
-const char kContentType[] = "text/plain";
-const char kContentTransferEncodingBinary[] =
-    "Content-Transfer-Encoding: binary";
-const char kXHeaderForSignals[] = "X-Chrome-Loading-Metrics-Data: 1";
 
 std::string AddHistogramSuffix(const ClientId& client_id,
                                const char* histogram_name) {
@@ -171,26 +164,11 @@
   completion_callback_ = std::move(completion_callback);
   progress_callback_ = progress_callback;
 
-  if (IsOfflinePagesRenovationsEnabled()) {
-    // Lazily create PageRenovationLoader
-    if (!page_renovation_loader_)
-      page_renovation_loader_ = std::make_unique<PageRenovationLoader>();
-
-    // Set up PageRenovator for this offlining instance.
-    auto script_injector = std::make_unique<RenderFrameScriptInjector>(
-        loader_->web_contents()->GetMainFrame(),
-        ISOLATED_WORLD_ID_CHROME_INTERNAL);
-    page_renovator_ = std::make_unique<PageRenovator>(
-        page_renovation_loader_.get(), std::move(script_injector),
-        request.url());
-  }
-
   // Load page attempt.
   loader_.get()->LoadPage(request.url());
 
   snapshot_controller_ = std::make_unique<BackgroundSnapshotController>(
-      base::ThreadTaskRunnerHandle::Get(), this,
-      static_cast<bool>(page_renovator_));
+      base::ThreadTaskRunnerHandle::Get(), this, false);
 
   return true;
 }
@@ -437,41 +415,6 @@
 
   save_state_ = SAVING;
 
-  // Capture loading signals to UMA.
-  RequestStats& image_stats = stats_[ResourceDataType::IMAGE];
-  RequestStats& css_stats = stats_[ResourceDataType::TEXT_CSS];
-  RequestStats& xhr_stats = stats_[ResourceDataType::XHR];
-
-  // Add loading signal into the MHTML that will be generated if the command
-  // line flag is set for it.
-  if (IsOfflinePagesLoadSignalCollectingEnabled()) {
-    // Write resource percentage signal data into extra data before emitting it
-    // to the MHTML.
-    signal_data_.SetDouble("StartedImages", image_stats.requested);
-    signal_data_.SetDouble("CompletedImages", image_stats.completed);
-    signal_data_.SetDouble("StartedCSS", css_stats.requested);
-    signal_data_.SetDouble("CompletedCSS", css_stats.completed);
-    signal_data_.SetDouble("StartedXHR", xhr_stats.requested);
-    signal_data_.SetDouble("CompletedXHR", xhr_stats.completed);
-
-    // Stash loading signals for writing when we write out the MHTML.
-    std::string headers = base::StringPrintf(
-        "%s\r\n%s\r\n\r\n", kContentTransferEncodingBinary, kXHeaderForSignals);
-    std::string body;
-    base::JSONWriter::Write(signal_data_, &body);
-    std::string content_type = kContentType;
-    std::string content_location = base::StringPrintf(
-        "cid:signal-data-%" PRId64 "@mhtml.blink", request.request_id());
-
-    content::MHTMLExtraParts* extra_parts =
-        content::MHTMLExtraParts::FromWebContents(web_contents);
-    DCHECK(extra_parts);
-    if (extra_parts != nullptr) {
-      extra_parts->AddExtraMHTMLPart(content_type, content_location, headers,
-                                     body);
-    }
-  }
-
   std::unique_ptr<OfflinePageArchiver> archiver(new OfflinePageMHTMLArchiver());
 
   OfflinePageModel::SavePageParams params;
@@ -495,14 +438,6 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
-void BackgroundLoaderOffliner::RunRenovations() {
-  if (page_renovator_) {
-    page_renovator_->RunRenovations(
-        base::BindOnce(&BackgroundLoaderOffliner::RenovationsCompleted,
-                       weak_ptr_factory_.GetWeakPtr()));
-  }
-}
-
 void BackgroundLoaderOffliner::OnPageSaved(SavePageResult save_result,
                                            int64_t offline_id) {
   if (!pending_request_)
diff --git a/chrome/browser/offline_pages/background_loader_offliner.h b/chrome/browser/offline_pages/background_loader_offliner.h
index 8c96256..d353b02d 100644
--- a/chrome/browser/offline_pages/background_loader_offliner.h
+++ b/chrome/browser/offline_pages/background_loader_offliner.h
@@ -30,7 +30,6 @@
 
 class OfflinerPolicy;
 class OfflinePageModel;
-class PageRenovationLoader;
 class PageRenovator;
 
 struct RequestStats {
@@ -84,7 +83,6 @@
 
   // BackgroundSnapshotController::Client implementation.
   void StartSnapshot() override;
-  void RunRenovations() override;
 
   void SetBackgroundSnapshotControllerForTest(
       std::unique_ptr<BackgroundSnapshotController> controller);
@@ -170,11 +168,6 @@
   // Whether we are on a low-end device.
   bool is_low_end_device_;
 
-  // PageRenovationLoader must live longer than the PageRenovator.
-  std::unique_ptr<PageRenovationLoader> page_renovation_loader_;
-  // Per-offliner PageRenovator instance.
-  std::unique_ptr<PageRenovator> page_renovator_;
-
   // Save state.
   SaveState save_state_;
   // Page load state.
diff --git a/chrome/browser/offline_pages/background_loader_offliner_unittest.cc b/chrome/browser/offline_pages/background_loader_offliner_unittest.cc
index 41bea3d..d1ef8f28 100644
--- a/chrome/browser/offline_pages/background_loader_offliner_unittest.cc
+++ b/chrome/browser/offline_pages/background_loader_offliner_unittest.cc
@@ -968,9 +968,6 @@
 }
 
 TEST_F(BackgroundLoaderOfflinerTest, SignalCollectionDisabled) {
-  // Ensure feature flag for Signal collection is off,
-  EXPECT_FALSE(offline_pages::IsOfflinePagesLoadSignalCollectingEnabled());
-
   base::Time creation_time = base::Time::Now();
   SavePageRequest request(kRequestId, HttpUrl(), kClientId, creation_time,
                           kUserRequested);
@@ -980,75 +977,10 @@
   CompleteLoading();
   PumpLoop();
 
-  // No extra parts should be added if the flag is off.
+  // No extra parts should be added.
   content::MHTMLExtraParts* extra_parts =
       content::MHTMLExtraParts::FromWebContents(offliner()->web_contents());
   EXPECT_EQ(extra_parts->size(), 0);
 }
 
-TEST_F(BackgroundLoaderOfflinerTest, SignalCollectionEnabled) {
-  // Ensure feature flag for signal collection is on.
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      kOfflinePagesLoadSignalCollectingFeature);
-  EXPECT_TRUE(IsOfflinePagesLoadSignalCollectingEnabled());
-
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request(kRequestId, HttpUrl(), kClientId, creation_time,
-                          kUserRequested);
-  EXPECT_TRUE(offliner()->LoadAndSave(request, completion_callback(),
-                                      progress_callback()));
-
-  CompleteLoading();
-  PumpLoop();
-
-  // One extra part should be added if the flag is on.
-  content::MHTMLExtraParts* extra_parts =
-      content::MHTMLExtraParts::FromWebContents(offliner()->web_contents());
-  EXPECT_EQ(extra_parts->size(), 1);
-}
-
-TEST_F(BackgroundLoaderOfflinerTest, ResourceSignalCollection) {
-  // Ensure feature flag for signal collection is on.
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      kOfflinePagesLoadSignalCollectingFeature);
-  EXPECT_TRUE(IsOfflinePagesLoadSignalCollectingEnabled());
-
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request(kRequestId, HttpUrl(), kClientId, creation_time,
-                          kUserRequested);
-  EXPECT_TRUE(offliner()->LoadAndSave(request, completion_callback(),
-                                      progress_callback()));
-
-  // Simulate resource requests starting and completing
-  offliner()->ObserveResourceLoading(
-      ResourceLoadingObserver::ResourceDataType::IMAGE, true);
-  offliner()->ObserveResourceLoading(
-      ResourceLoadingObserver::ResourceDataType::IMAGE, false);
-  offliner()->ObserveResourceLoading(
-      ResourceLoadingObserver::ResourceDataType::TEXT_CSS, true);
-  offliner()->ObserveResourceLoading(
-      ResourceLoadingObserver::ResourceDataType::TEXT_CSS, true);
-  offliner()->ObserveResourceLoading(
-      ResourceLoadingObserver::ResourceDataType::XHR, true);
-
-  CompleteLoading();
-  PumpLoop();
-
-  // One extra part should be added if the flag is on.
-  content::MHTMLExtraParts* extra_parts =
-      content::MHTMLExtraParts::FromWebContents(offliner()->web_contents());
-  EXPECT_EQ(extra_parts->size(), 1);
-
-  offline_pages::RequestStats* stats = GetRequestStats();
-  EXPECT_EQ(1,
-            stats[ResourceLoadingObserver::ResourceDataType::IMAGE].requested);
-  EXPECT_EQ(1,
-            stats[ResourceLoadingObserver::ResourceDataType::IMAGE].completed);
-  EXPECT_EQ(
-      2, stats[ResourceLoadingObserver::ResourceDataType::TEXT_CSS].requested);
-  EXPECT_EQ(1, stats[ResourceLoadingObserver::ResourceDataType::XHR].requested);
-}
-
 }  // namespace offline_pages
diff --git a/chrome/browser/optimization_guide/android/optimization_guide_bridge_unittest.cc b/chrome/browser/optimization_guide/android/optimization_guide_bridge_unittest.cc
index ee11d5b..b1be12f1 100644
--- a/chrome/browser/optimization_guide/android/optimization_guide_bridge_unittest.cc
+++ b/chrome/browser/optimization_guide/android/optimization_guide_bridge_unittest.cc
@@ -16,7 +16,6 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/optimization_guide/core/optimization_guide_prefs.h"
-#include "components/optimization_guide/core/optimization_guide_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/testing_pref_service.h"
 #include "content/public/test/browser_task_environment.h"
@@ -34,12 +33,8 @@
 
 class MockOptimizationGuideHintsManager : public OptimizationGuideHintsManager {
  public:
-  MockOptimizationGuideHintsManager(
-      optimization_guide::OptimizationGuideService* optimization_guide_service,
-      Profile* profile,
-      PrefService* pref_service)
-      : OptimizationGuideHintsManager(optimization_guide_service,
-                                      profile,
+  MockOptimizationGuideHintsManager(Profile* profile, PrefService* pref_service)
+      : OptimizationGuideHintsManager(profile,
                                       pref_service,
                                       /*hint_store=*/nullptr,
                                       /*top_host_provider=*/nullptr,
@@ -91,18 +86,14 @@
                       return std::make_unique<
                           MockOptimizationGuideKeyedService>(context);
                     })));
-    optimization_guide_service_ =
-        std::make_unique<optimization_guide::OptimizationGuideService>(
-            task_environment_.GetMainThreadTaskRunner());
     optimization_guide_hints_manager_ =
         std::make_unique<MockOptimizationGuideHintsManager>(
-            optimization_guide_service_.get(), profile_, pref_service_.get());
+            profile_, pref_service_.get());
   }
 
   void TearDown() override {
     optimization_guide_hints_manager_->Shutdown();
     optimization_guide_hints_manager_.reset();
-    optimization_guide_service_.reset();
   }
 
   void RegisterOptimizationTypes() {
@@ -123,8 +114,6 @@
       base::test::TaskEnvironment::MainThreadType::UI};
   TestingProfileManager profile_manager_;
   TestingProfile* profile_;
-  std::unique_ptr<optimization_guide::OptimizationGuideService>
-      optimization_guide_service_;
   base::ScopedTempDir temp_dir_;
   std::unique_ptr<TestingPrefServiceSimple> pref_service_;
 };
diff --git a/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc b/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc
index b026f6d..17579ca5 100644
--- a/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc
+++ b/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc
@@ -18,7 +18,6 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -34,13 +33,14 @@
 #include "components/optimization_guide/core/optimization_guide_enums.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/core/optimization_guide_prefs.h"
-#include "components/optimization_guide/core/optimization_guide_service.h"
 #include "components/optimization_guide/core/optimization_guide_store.h"
 #include "components/optimization_guide/core/optimization_guide_switches.h"
+#include "components/optimization_guide/core/optimization_hints_component_update_listener.h"
 #include "components/optimization_guide/core/test_hints_component_creator.h"
 #include "components/optimization_guide/core/top_host_provider.h"
 #include "components/optimization_guide/proto/hints.pb.h"
 #include "components/prefs/pref_service.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "components/variations/hashing.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -210,8 +210,8 @@
 
     base::HistogramTester histogram_tester;
 
-    g_browser_process->optimization_guide_service()->MaybeUpdateHintsComponent(
-        component_info);
+    optimization_guide::OptimizationHintsComponentUpdateListener::GetInstance()
+        ->MaybeUpdateHintsComponent(component_info);
 
     RetryForHistogramUntilCountReached(
         &histogram_tester,
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
index 4f9c11c1..847f852b 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
@@ -40,10 +40,10 @@
 #include "components/optimization_guide/core/optimization_guide_enums.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/core/optimization_guide_prefs.h"
-#include "components/optimization_guide/core/optimization_guide_service.h"
 #include "components/optimization_guide/core/optimization_guide_store.h"
 #include "components/optimization_guide/core/optimization_guide_switches.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
+#include "components/optimization_guide/core/optimization_hints_component_update_listener.h"
 #include "components/optimization_guide/core/optimization_metadata.h"
 #include "components/optimization_guide/core/top_host_provider.h"
 #include "components/optimization_guide/proto/models.pb.h"
@@ -61,8 +61,8 @@
 namespace {
 
 // The component version used with a manual config. This ensures that any hint
-// component received from the OptimizationGuideService on a subsequent startup
-// will have a newer version than it.
+// component received from the Optimization Hints component on a subsequent
+// startup will have a newer version than it.
 constexpr char kManualConfigComponentVersion[] = "0.0.0";
 
 // Delay until successfully fetched hints should be updated by requesting from
@@ -252,14 +252,12 @@
 }  // namespace
 
 OptimizationGuideHintsManager::OptimizationGuideHintsManager(
-    optimization_guide::OptimizationGuideService* optimization_guide_service,
     Profile* profile,
     PrefService* pref_service,
     optimization_guide::OptimizationGuideStore* hint_store,
     optimization_guide::TopHostProvider* top_host_provider,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-    : optimization_guide_service_(optimization_guide_service),
-      background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
+    : background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT})),
       profile_(profile),
       pref_service_(pref_service),
@@ -300,8 +298,8 @@
 }
 
 void OptimizationGuideHintsManager::Shutdown() {
-  if (optimization_guide_service_)
-    optimization_guide_service_->RemoveObserver(this);
+  optimization_guide::OptimizationHintsComponentUpdateListener::GetInstance()
+      ->RemoveObserver(this);
 
   g_browser_process->network_quality_tracker()
       ->RemoveEffectiveConnectionTypeObserver(this);
@@ -563,8 +561,8 @@
 
   // Register as an observer regardless of hint proto override usage. This is
   // needed as a signal during testing.
-  if (optimization_guide_service_)
-    optimization_guide_service_->AddObserver(this);
+  optimization_guide::OptimizationHintsComponentUpdateListener::GetInstance()
+      ->AddObserver(this);
 }
 
 void OptimizationGuideHintsManager::UpdateComponentHints(
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
index 7ec386e..412cdba 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
@@ -25,7 +25,7 @@
 #include "components/optimization_guide/content/optimization_guide_decider.h"
 #include "components/optimization_guide/core/hints_component_info.h"
 #include "components/optimization_guide/core/hints_fetcher.h"
-#include "components/optimization_guide/core/optimization_guide_service_observer.h"
+#include "components/optimization_guide/core/optimization_hints_component_observer.h"
 #include "components/optimization_guide/proto/hints.pb.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "net/nqe/effective_connection_type.h"
@@ -44,7 +44,6 @@
 class HintsFetcherFactory;
 class OptimizationFilter;
 class OptimizationMetadata;
-class OptimizationGuideService;
 class OptimizationGuideStore;
 enum class OptimizationTargetDecision;
 enum class OptimizationTypeDecision;
@@ -57,12 +56,11 @@
 class Profile;
 
 class OptimizationGuideHintsManager
-    : public optimization_guide::OptimizationGuideServiceObserver,
+    : public optimization_guide::OptimizationHintsComponentObserver,
       public network::NetworkQualityTracker::EffectiveConnectionTypeObserver,
       public NavigationPredictorKeyedService::Observer {
  public:
   OptimizationGuideHintsManager(
-      optimization_guide::OptimizationGuideService* optimization_guide_service,
       Profile* profile,
       PrefService* pref_service,
       optimization_guide::OptimizationGuideStore* hint_store,
@@ -79,7 +77,7 @@
   GetOptimizationGuideDecisionFromOptimizationTypeDecision(
       optimization_guide::OptimizationTypeDecision optimization_type_decision);
 
-  // optimization_guide::OptimizationGuideServiceObserver implementation:
+  // optimization_guide::OptimizationHintsComponentObserver implementation:
   void OnHintsComponentAvailable(
       const optimization_guide::HintsComponentInfo& info) override;
 
@@ -377,10 +375,6 @@
       const GURL& navigation_url,
       optimization_guide::proto::OptimizationType optimization_type);
 
-  // The OptimizationGuideService that this guide is listening to. Not owned.
-  optimization_guide::OptimizationGuideService* const
-      optimization_guide_service_;
-
   // The information of the latest component delivered by
   // |optimization_guide_service_|.
   base::Optional<optimization_guide::HintsComponentInfo> hints_component_info_;
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
index 9794ceda..e900839c 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
@@ -29,7 +29,6 @@
 #include "components/optimization_guide/core/optimization_guide_enums.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/core/optimization_guide_prefs.h"
-#include "components/optimization_guide/core/optimization_guide_service.h"
 #include "components/optimization_guide/core/optimization_guide_store.h"
 #include "components/optimization_guide/core/optimization_guide_switches.h"
 #include "components/optimization_guide/core/proto_database_provider_test_base.h"
@@ -126,33 +125,6 @@
 
 }  // namespace
 
-class TestOptimizationGuideService
-    : public optimization_guide::OptimizationGuideService {
- public:
-  explicit TestOptimizationGuideService(
-      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner)
-      : OptimizationGuideService(ui_task_runner) {}
-
-  ~TestOptimizationGuideService() override = default;
-
-  void AddObserver(
-      optimization_guide::OptimizationGuideServiceObserver* observer) override {
-    add_observer_called_ = true;
-  }
-
-  void RemoveObserver(
-      optimization_guide::OptimizationGuideServiceObserver* observer) override {
-    remove_observer_called_ = true;
-  }
-
-  bool AddObserverCalled() const { return add_observer_called_; }
-  bool RemoveObserverCalled() const { return remove_observer_called_; }
-
- private:
-  bool add_observer_called_ = false;
-  bool remove_observer_called_ = false;
-};
-
 // A mock class implementation of TopHostProvider.
 class FakeTopHostProvider : public optimization_guide::TopHostProvider {
  public:
@@ -285,7 +257,7 @@
   void SetUp() override {
     optimization_guide::ProtoDatabaseProviderTestBase::SetUp();
     web_contents_factory_ = std::make_unique<content::TestWebContentsFactory>();
-    CreateServiceAndHintsManager(/*top_host_provider=*/nullptr);
+    CreateHintsManager(/*top_host_provider=*/nullptr);
   }
 
   void TearDown() override {
@@ -293,14 +265,11 @@
     ResetHintsManager();
   }
 
-  void CreateServiceAndHintsManager(
+  void CreateHintsManager(
       optimization_guide::TopHostProvider* top_host_provider) {
     if (hints_manager_) {
       ResetHintsManager();
     }
-    optimization_guide_service_ =
-        std::make_unique<TestOptimizationGuideService>(
-            task_environment_.GetMainThreadTaskRunner());
     pref_service_ = std::make_unique<TestingPrefServiceSimple>();
     optimization_guide::prefs::RegisterProfilePrefs(pref_service_->registry());
 
@@ -311,18 +280,15 @@
     hint_store_ = std::make_unique<optimization_guide::OptimizationGuideStore>(
         db_provider_.get(), temp_dir(),
         task_environment_.GetMainThreadTaskRunner());
+
     hints_manager_ = std::make_unique<OptimizationGuideHintsManager>(
-        optimization_guide_service_.get(), &testing_profile_,
-        pref_service_.get(), hint_store_.get(), top_host_provider,
-        url_loader_factory_);
+        &testing_profile_, pref_service_.get(), hint_store_.get(),
+        top_host_provider, url_loader_factory_);
     hints_manager_->SetClockForTesting(task_environment_.GetMockClock());
 
-    // Add observer is called after the HintCache is fully initialized,
-    // indicating that the OptimizationGuideHintsManager is ready to process
-    // hints.
-    while (!optimization_guide_service_->AddObserverCalled()) {
-      RunUntilIdle();
-    }
+    // Run until hint cache is initialized and the OptimizationGuideHintsManager
+    // is ready to process hints.
+    RunUntilIdle();
   }
 
   void ResetHintsManager() {
@@ -454,7 +420,6 @@
   std::unique_ptr<content::TestWebContentsFactory> web_contents_factory_;
   std::unique_ptr<optimization_guide::OptimizationGuideStore> hint_store_;
   std::unique_ptr<OptimizationGuideHintsManager> hints_manager_;
-  std::unique_ptr<TestOptimizationGuideService> optimization_guide_service_;
   std::unique_ptr<TestingPrefServiceSimple> pref_service_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   network::TestURLLoaderFactory test_url_loader_factory_;
@@ -493,7 +458,7 @@
 
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       optimization_guide::switches::kHintsProtoOverride, encoded_config);
-  CreateServiceAndHintsManager(/*top_host_provider=*/nullptr);
+  CreateHintsManager(/*top_host_provider=*/nullptr);
   hints_manager()->RegisterOptimizationTypes(
       {optimization_guide::proto::LITE_PAGE_REDIRECT});
 
@@ -530,7 +495,7 @@
 
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       optimization_guide::switches::kHintsProtoOverride, "this-is-not-a-proto");
-  CreateServiceAndHintsManager(/*top_host_provider=*/nullptr);
+  CreateHintsManager(/*top_host_provider=*/nullptr);
 
   // The below histogram should not be recorded since hints weren't coming
   // directly from the component.
@@ -560,7 +525,7 @@
     base::HistogramTester histogram_tester;
     base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
         optimization_guide::switches::kHintsProtoOverride, encoded_config);
-    CreateServiceAndHintsManager(/*top_host_provider=*/nullptr);
+    CreateHintsManager(/*top_host_provider=*/nullptr);
     // The below histogram should not be recorded since hints weren't coming
     // directly from the component.
     histogram_tester.ExpectTotalCount("OptimizationGuide.ProcessHintsResult",
@@ -1898,7 +1863,7 @@
       std::make_unique<FakeTopHostProvider>(
           std::vector<std::string>({"example1.com", "example2.com"}));
 
-  CreateServiceAndHintsManager(top_host_provider.get());
+  CreateHintsManager(top_host_provider.get());
   InitializeWithDefaultConfig("1.0.0");
 
   // Force timer to expire and schedule a hints fetch.
@@ -1987,7 +1952,7 @@
        HintsFetchNotAllowedIfFeatureIsEnabledButUserNotAllowed) {
   base::CommandLine::ForCurrentProcess()->RemoveSwitch(
       optimization_guide::switches::kDisableCheckingUserPermissionsForTesting);
-  CreateServiceAndHintsManager(/*top_host_provider=*/nullptr);
+  CreateHintsManager(/*top_host_provider=*/nullptr);
   hints_manager()->RegisterOptimizationTypes(
       {optimization_guide::proto::DEFER_ALL_SCRIPT});
   hints_manager()->SetHintsFetcherFactoryForTesting(
@@ -2005,7 +1970,7 @@
        HintsFetchNotAllowedIfFeatureIsEnabledButTopHostProviderIsNotProvided) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       optimization_guide::switches::kDisableCheckingUserPermissionsForTesting);
-  CreateServiceAndHintsManager(/*top_host_provider=*/nullptr);
+  CreateHintsManager(/*top_host_provider=*/nullptr);
   hints_manager()->RegisterOptimizationTypes(
       {optimization_guide::proto::DEFER_ALL_SCRIPT});
   hints_manager()->SetHintsFetcherFactoryForTesting(
@@ -2027,7 +1992,7 @@
 
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       optimization_guide::switches::kDisableCheckingUserPermissionsForTesting);
-  CreateServiceAndHintsManager(top_host_provider.get());
+  CreateHintsManager(top_host_provider.get());
 
   hints_manager()->SetHintsFetcherFactoryForTesting(
       BuildTestHintsFetcherFactory(
@@ -2047,7 +2012,7 @@
       optimization_guide::switches::kDisableCheckingUserPermissionsForTesting);
   std::unique_ptr<FakeTopHostProvider> top_host_provider =
       std::make_unique<FakeTopHostProvider>(std::vector<std::string>({}));
-  CreateServiceAndHintsManager(top_host_provider.get());
+  CreateHintsManager(top_host_provider.get());
 
   hints_manager()->RegisterOptimizationTypes(
       {optimization_guide::proto::DEFER_ALL_SCRIPT});
@@ -2070,7 +2035,7 @@
   std::unique_ptr<FakeTopHostProvider> top_host_provider =
       std::make_unique<FakeTopHostProvider>(
           std::vector<std::string>({"example1.com", "example2.com"}));
-  CreateServiceAndHintsManager(top_host_provider.get());
+  CreateHintsManager(top_host_provider.get());
   hints_manager()->RegisterOptimizationTypes(
       {optimization_guide::proto::DEFER_ALL_SCRIPT});
   hints_manager()->SetHintsFetcherFactoryForTesting(
@@ -2099,7 +2064,7 @@
       std::make_unique<FakeTopHostProvider>(
           std::vector<std::string>({"example1.com", "example2.com"}));
 
-  CreateServiceAndHintsManager(top_host_provider.get());
+  CreateHintsManager(top_host_provider.get());
   hints_manager()->RegisterOptimizationTypes(
       {optimization_guide::proto::DEFER_ALL_SCRIPT});
   hints_manager()->SetHintsFetcherFactoryForTesting(
@@ -2126,7 +2091,7 @@
           std::vector<std::string>({"example1.com", "example2.com"}));
 
   // Force hints fetch scheduling.
-  CreateServiceAndHintsManager(top_host_provider.get());
+  CreateHintsManager(top_host_provider.get());
   hints_manager()->RegisterOptimizationTypes(
       {optimization_guide::proto::DEFER_ALL_SCRIPT});
   hints_manager()->SetHintsFetcherFactoryForTesting(
@@ -3713,7 +3678,7 @@
           std::vector<std::string>({"example1.com", "example2.com"}));
 
   // Force hints fetch scheduling.
-  CreateServiceAndHintsManager(top_host_provider.get());
+  CreateHintsManager(top_host_provider.get());
   hints_manager()->RegisterOptimizationTypes(
       {optimization_guide::proto::DEFER_ALL_SCRIPT});
   hints_manager()->SetHintsFetcherFactoryForTesting(
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index f5df625..acdaa6cc 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -25,7 +25,6 @@
 #include "components/optimization_guide/core/hints_processing_util.h"
 #include "components/optimization_guide/core/optimization_guide_constants.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/optimization_guide_service.h"
 #include "components/optimization_guide/core/optimization_guide_store.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/optimization_guide/core/top_host_provider.h"
@@ -172,8 +171,7 @@
   }
 
   hints_manager_ = std::make_unique<OptimizationGuideHintsManager>(
-      g_browser_process->optimization_guide_service(), profile,
-      profile->GetPrefs(), hint_store, top_host_provider_.get(),
+      profile, profile->GetPrefs(), hint_store, top_host_provider_.get(),
       url_loader_factory);
   prediction_manager_ = std::make_unique<optimization_guide::PredictionManager>(
       prediction_model_and_features_store, top_host_provider_.get(),
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
index 7216e87..ce3fc62 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
@@ -10,7 +10,6 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_hints_manager.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
@@ -25,10 +24,12 @@
 #include "components/optimization_guide/core/optimization_guide_prefs.h"
 #include "components/optimization_guide/core/optimization_guide_store.h"
 #include "components/optimization_guide/core/optimization_guide_switches.h"
+#include "components/optimization_guide/core/optimization_hints_component_update_listener.h"
 #include "components/optimization_guide/core/test_hints_component_creator.h"
 #include "components/optimization_guide/proto/hints.pb.h"
 #include "components/prefs/pref_service.h"
 #include "components/previews/core/previews_switches.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "components/variations/active_field_trials.h"
 #include "components/variations/hashing.h"
@@ -222,8 +223,8 @@
             optimization_guide::proto::NOSCRIPT, {url_with_hints_.host()},
             "simple.html");
 
-    g_browser_process->optimization_guide_service()->MaybeUpdateHintsComponent(
-        component_info);
+    optimization_guide::OptimizationHintsComponentUpdateListener::GetInstance()
+        ->MaybeUpdateHintsComponent(component_info);
 
     run_loop.Run();
   }
diff --git a/chrome/browser/optimization_guide/optimization_guide_top_host_provider.cc b/chrome/browser/optimization_guide/optimization_guide_top_host_provider.cc
index a80b79e3..284b325 100644
--- a/chrome/browser/optimization_guide/optimization_guide_top_host_provider.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_top_host_provider.cc
@@ -9,7 +9,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/time/default_clock.h"
 #include "base/values.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_permissions_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/optimization_guide/core/hints_processing_util.h"
@@ -18,6 +17,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/site_engagement/core/mojom/site_engagement_details.mojom.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
diff --git a/chrome/browser/optimization_guide/optimization_guide_top_host_provider_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_top_host_provider_unittest.cc
index f2aa3b2..2185318 100644
--- a/chrome/browser/optimization_guide/optimization_guide_top_host_provider_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_top_host_provider_unittest.cc
@@ -8,7 +8,6 @@
 #include "base/test/simple_test_clock.h"
 #include "base/time/default_clock.h"
 #include "base/values.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/previews/previews_https_notification_infobar_decider.h"
 #include "chrome/browser/previews/previews_service.h"
 #include "chrome/browser/previews/previews_service_factory.h"
@@ -22,6 +21,7 @@
 #include "components/optimization_guide/core/optimization_guide_switches.h"
 #include "components/prefs/pref_service.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/test/mock_navigation_handle.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager.cc b/chrome/browser/optimization_guide/prediction/prediction_manager.cc
index 5f0d201..b31581ce 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager.cc
@@ -23,7 +23,6 @@
 #include "base/time/default_clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/download/download_service_factory.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_navigation_data.h"
 #include "chrome/browser/optimization_guide/optimization_guide_permissions_util.h"
 #include "chrome/browser/optimization_guide/prediction/prediction_model_download_manager.h"
@@ -45,6 +44,7 @@
 #include "components/optimization_guide/core/top_host_provider.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/prefs/pref_service.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc b/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc
index f1dd17a..0d336fba 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc
@@ -24,7 +24,6 @@
 #include "components/leveldb_proto/testing/fake_db.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/core/optimization_guide_prefs.h"
-#include "components/optimization_guide/core/optimization_guide_service.h"
 #include "components/optimization_guide/core/optimization_guide_store.h"
 #include "components/optimization_guide/core/optimization_guide_switches.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model_download_manager_unittest.cc b/chrome/browser/optimization_guide/prediction/prediction_model_download_manager_unittest.cc
index b89fcc4..49dfe47 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_model_download_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_model_download_manager_unittest.cc
@@ -154,6 +154,32 @@
         switches::kDisableModelDownloadVerificationForTesting);
   }
 
+  // Retries until the path has been deleted or until all handles to |path| have
+  // been closed. Returns whether |path| has been deleted.
+  //
+  // See crbug/1156112#c1 for suggested mitigation steps.
+  bool HasPathBeenDeleted(const base::FilePath& path) {
+    while (true) {
+      RunUntilIdle();
+
+      bool path_exists = base::PathExists(path);
+      if (!path_exists)
+        return true;
+
+      base::File::Error file_error = base::File::GetLastFileError();
+      // In the event this does not fix the flake, log the error so we know what
+      // it is.
+      // TODO(crbug/1156112): Remove this log once the flake has been resolved.
+      DLOG(ERROR) << "Path Exists Error: " << file_error;
+
+      if (file_error != base::File::FILE_ERROR_ACCESS_DENIED)
+        return !path_exists;
+
+      // Retry if the last file error is access denied since it's likely that
+      // the file is in the process of being deleted.
+    }
+  }
+
  private:
   void WriteFileForStatus(PredictionModelDownloadFileStatus status) {
     if (status == PredictionModelDownloadFileStatus::kVerifiedCrxWithNoFiles ||
@@ -383,7 +409,7 @@
   RunUntilIdle();
 
   EXPECT_FALSE(observer.last_ready_model().has_value());
-  EXPECT_FALSE(base::PathExists(GetFilePathForDownloadFileStatus(
+  EXPECT_TRUE(HasPathBeenDeleted(GetFilePathForDownloadFileStatus(
       PredictionModelDownloadFileStatus::kUnverifiedFile)));
   histogram_tester.ExpectUniqueSample(
       "OptimizationGuide.PredictionModelDownloadManager."
@@ -403,7 +429,7 @@
   RunUntilIdle();
 
   EXPECT_FALSE(observer.last_ready_model().has_value());
-  EXPECT_FALSE(base::PathExists(GetFilePathForDownloadFileStatus(
+  EXPECT_TRUE(HasPathBeenDeleted(GetFilePathForDownloadFileStatus(
       PredictionModelDownloadFileStatus::kVerifiedCrxWithNoFiles)));
 
   histogram_tester.ExpectUniqueSample(
@@ -426,7 +452,7 @@
   RunUntilIdle();
 
   EXPECT_FALSE(observer.last_ready_model().has_value());
-  EXPECT_FALSE(base::PathExists(GetFilePathForDownloadFileStatus(
+  EXPECT_TRUE(HasPathBeenDeleted(GetFilePathForDownloadFileStatus(
       PredictionModelDownloadFileStatus::kVerifiedCrxWithBadModelInfoFile)));
 
   histogram_tester.ExpectUniqueSample(
@@ -449,7 +475,7 @@
   RunUntilIdle();
 
   EXPECT_FALSE(observer.last_ready_model().has_value());
-  EXPECT_FALSE(base::PathExists(GetFilePathForDownloadFileStatus(
+  EXPECT_TRUE(HasPathBeenDeleted(GetFilePathForDownloadFileStatus(
       PredictionModelDownloadFileStatus::kVerifiedCrxWithInvalidModelInfo)));
 
   histogram_tester.ExpectUniqueSample(
@@ -471,7 +497,7 @@
   RunUntilIdle();
 
   EXPECT_FALSE(observer.last_ready_model().has_value());
-  EXPECT_FALSE(base::PathExists(GetFilePathForDownloadFileStatus(
+  EXPECT_TRUE(HasPathBeenDeleted(GetFilePathForDownloadFileStatus(
       PredictionModelDownloadFileStatus::
           kVerfiedCrxWithValidModelInfoNoModelFile)));
 
@@ -506,7 +532,7 @@
           .value(),
       FILE_PATH_LITERAL("OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD_123.tflite"));
   // Downloaded file should still be deleted.
-  EXPECT_FALSE(base::PathExists(GetFilePathForDownloadFileStatus(
+  EXPECT_TRUE(HasPathBeenDeleted(GetFilePathForDownloadFileStatus(
       PredictionModelDownloadFileStatus::kVerifiedCrxWithGoodModelFiles)));
 
   histogram_tester.ExpectUniqueSample(
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/floc_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/floc_page_load_metrics_observer.cc
new file mode 100644
index 0000000..eb4eef0a
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/floc_page_load_metrics_observer.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/page_load_metrics/observers/ad_metrics/floc_page_load_metrics_observer.h"
+
+#include "chrome/browser/federated_learning/floc_eligibility_observer.h"
+#include "content/public/browser/web_contents.h"
+
+FlocPageLoadMetricsObserver::FlocPageLoadMetricsObserver() = default;
+
+FlocPageLoadMetricsObserver::~FlocPageLoadMetricsObserver() = default;
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+FlocPageLoadMetricsObserver::OnCommit(
+    content::NavigationHandle* navigation_handle,
+    ukm::SourceId source_id) {
+  return federated_learning::FlocEligibilityObserver::
+      GetOrCreateForCurrentDocument(navigation_handle->GetRenderFrameHost())
+          ->OnCommit(navigation_handle);
+}
+
+void FlocPageLoadMetricsObserver::OnResourceDataUseObserved(
+    content::RenderFrameHost* rfh,
+    const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
+        resources) {
+  bool any_ads_resource = base::ranges::any_of(
+      resources,
+      [](const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
+        return resource->reported_as_ad_resource &&
+               resource->received_data_length > 0;
+      });
+
+  if (any_ads_resource) {
+    content::WebContents* web_contents = GetDelegate().GetWebContents();
+    federated_learning::FlocEligibilityObserver::GetOrCreateForCurrentDocument(
+        web_contents->GetMainFrame())
+        ->OnAdResource();
+  }
+}
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/floc_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ad_metrics/floc_page_load_metrics_observer.h
new file mode 100644
index 0000000..9f719647
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/floc_page_load_metrics_observer.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_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_AD_METRICS_FLOC_PAGE_LOAD_METRICS_OBSERVER_H_
+#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_AD_METRICS_FLOC_PAGE_LOAD_METRICS_OBSERVER_H_
+
+#include "base/macros.h"
+#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
+
+// This observer monitors navigation commit and resource usages, which may
+// affect whether the navigation's associated history entry is eligible for floc
+// computation.
+//
+// The final eligibility decision may be based on other signals. See the
+// FlocEligibilityObserver class for the full criteria.
+class FlocPageLoadMetricsObserver
+    : public page_load_metrics::PageLoadMetricsObserver {
+ public:
+  FlocPageLoadMetricsObserver();
+  ~FlocPageLoadMetricsObserver() override;
+
+  FlocPageLoadMetricsObserver(const FlocPageLoadMetricsObserver&) = delete;
+  FlocPageLoadMetricsObserver& operator=(const FlocPageLoadMetricsObserver&) =
+      delete;
+
+  // page_load_metrics::PageLoadMetricsObserver
+  ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
+                         ukm::SourceId source_id) override;
+  void OnResourceDataUseObserved(
+      content::RenderFrameHost* rfh,
+      const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
+          resources) override;
+};
+
+#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_AD_METRICS_FLOC_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
index b4e816f..7db09d1 100644
--- a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
@@ -16,7 +16,6 @@
 #include "cc/metrics/ukm_smoothness_data.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/prefetch/no_state_prefetch/prerender_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -35,6 +34,7 @@
 #include "components/page_load_metrics/browser/protocol_util.h"
 #include "components/prefs/pref_service.h"
 #include "components/search_engines/template_url_service.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
 #include "media/base/mime_util.h"
diff --git a/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.cc
index 4ccc9fc6..7000966 100644
--- a/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.cc
@@ -9,11 +9,11 @@
 #include "base/check.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/engagement/site_engagement_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
 #include "components/security_state/core/security_state.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/navigation_handle.h"
 #include "services/metrics/public/cpp/metrics_utils.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index 11fab4e..5c45f2fc 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -11,6 +11,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/observers/ad_metrics/floc_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/core/amp_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.h"
@@ -128,6 +129,7 @@
     if (ads_observer)
       tracker->AddObserver(std::move(ads_observer));
 
+    tracker->AddObserver(std::make_unique<FlocPageLoadMetricsObserver>());
     tracker->AddObserver(std::make_unique<ThirdPartyMetricsObserver>());
 
     std::unique_ptr<page_load_metrics::PageLoadMetricsObserver> ukm_observer =
diff --git a/chrome/browser/paint_preview/paint_preview_browsertest.cc b/chrome/browser/paint_preview/paint_preview_browsertest.cc
index d4bc4ff..84be6cb 100644
--- a/chrome/browser/paint_preview/paint_preview_browsertest.cc
+++ b/chrome/browser/paint_preview/paint_preview_browsertest.cc
@@ -376,6 +376,9 @@
 #endif
 IN_PROC_BROWSER_TEST_P(PaintPreviewBrowserTest,
                        MAYBE_DontReloadInRenderProcessExit) {
+  // In the FileSystem variant of this test, blocking needs to be permitted to
+  // allow cleanup to work during the crash.
+  base::ScopedAllowBlockingForTesting scope;
   LoadPage(http_server_.GetURL("a.com", "/title1.html"));
 
   content::WebContents* web_contents = GetWebContents();
@@ -414,15 +417,12 @@
           .Then(loop.QuitClosure()));
 
   // Crash the renderer.
-  {
-    base::ScopedAllowBlockingForTesting scope;
-    content::RenderProcessHost* process =
-        GetWebContents()->GetMainFrame()->GetProcess();
-    content::RenderProcessHostWatcher crash_observer(
-        process, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
-    process->Shutdown(0);
-    crash_observer.Wait();
-  }
+  content::RenderProcessHost* process =
+      GetWebContents()->GetMainFrame()->GetProcess();
+  content::RenderProcessHostWatcher crash_observer(
+      process, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+  process->Shutdown(0);
+  crash_observer.Wait();
 
   // The browser would have crashed before the loop exited if the callback was
   // not posted.
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 1337a64a..0c91780 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -24,7 +24,6 @@
 #include "build/buildflag.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/password_manager/account_password_store_factory.h"
@@ -76,6 +75,7 @@
 #include "components/sessions/content/content_record_password_state.h"
 #include "components/signin/public/base/signin_metrics.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/site_isolation/site_isolation_policy.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync/driver/sync_user_settings.h"
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 21d62c0..4f5eb97 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -23,6 +23,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/lock.h"
 #include "base/test/bind.h"
+#include "base/test/icu_test_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/metrics/user_action_tester.h"
 #include "base/test/test_timeouts.h"
@@ -918,6 +919,8 @@
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionJSUpdatesEnabledTest,
                        ViewerPropertiesDialogTest) {
+  // The properties dialog formats some values based on locale.
+  base::test::ScopedRestoreICUDefaultLocale scoped_locale{"en_US"};
   RunTestsInJsModule("viewer_properties_dialog_test.js", "document_info.pdf");
 }
 
diff --git a/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc b/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc
index 707025b..83050808 100644
--- a/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc
+++ b/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc
@@ -16,7 +16,6 @@
 #include "base/util/values/values_util.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/permissions/adaptive_quiet_notification_permission_ui_enabler.h"
 #include "chrome/browser/permissions/quiet_notification_permission_ui_config.h"
 #include "chrome/browser/permissions/quiet_notification_permission_ui_state.h"
@@ -36,6 +35,7 @@
 #include "components/permissions/test/mock_permission_request.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/scoped_user_pref_update.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/ukm/content/source_url_recorder.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "components/user_manager/scoped_user_manager.h"
diff --git a/chrome/browser/permissions/chrome_permissions_client.cc b/chrome/browser/permissions/chrome_permissions_client.cc
index 8ca1a6f..c8d5867 100644
--- a/chrome/browser/permissions/chrome_permissions_client.cc
+++ b/chrome/browser/permissions/chrome_permissions_client.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/engagement/important_sites_util.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/metrics/ukm_background_recorder_service.h"
 #include "chrome/browser/permissions/abusive_origin_permission_revocation_request.h"
 #include "chrome/browser/permissions/adaptive_quiet_notification_permission_ui_enabler.h"
@@ -35,6 +34,7 @@
 #include "components/google/core/common/google_util.h"
 #include "components/permissions/features.h"
 #include "components/prefs/pref_service.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/subresource_filter/content/browser/subresource_filter_content_settings_manager.h"
 #include "components/subresource_filter/content/browser/subresource_filter_profile_context.h"
 #include "components/ukm/content/source_url_recorder.h"
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index b73a41e2..171c537 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -523,6 +523,9 @@
 const char kAssistantQuickAnswersEnabled[] =
     "settings.voice_interaction.quick_answers.enabled";
 
+// Deprecated 01/2021
+const char kGoogleServicesHostedDomain[] = "google.services.hosted_domain";
+
 // Register local state used only for migration (clearing or moving to a new
 // key).
 void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
@@ -618,6 +621,8 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   registry->RegisterBooleanPref(kAssistantQuickAnswersEnabled, true);
+
+  registry->RegisterStringPref(kGoogleServicesHostedDomain, std::string());
 }
 
 }  // namespace
@@ -1261,6 +1266,9 @@
   // Added 12/2020
   profile_prefs->ClearPref(kAssistantQuickAnswersEnabled);
 
+  // Added 01/2021
+  profile_prefs->ClearPref(kGoogleServicesHostedDomain);
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_PROFILE_PREFS
 }
diff --git a/chrome/browser/previews/defer_all_script_browsertest.cc b/chrome/browser/previews/defer_all_script_browsertest.cc
index db47ce0..8897db5 100644
--- a/chrome/browser/previews/defer_all_script_browsertest.cc
+++ b/chrome/browser/previews/defer_all_script_browsertest.cc
@@ -14,7 +14,6 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/previews/previews_service.h"
@@ -31,7 +30,7 @@
 #include "components/optimization_guide/core/hints_component_util.h"
 #include "components/optimization_guide/core/optimization_guide_constants.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/optimization_guide_service.h"
+#include "components/optimization_guide/core/optimization_hints_component_update_listener.h"
 #include "components/optimization_guide/core/test_hints_component_creator.h"
 #include "components/optimization_guide/proto/hints.pb.h"
 #include "components/prefs/pref_service.h"
@@ -121,8 +120,8 @@
       const optimization_guide::HintsComponentInfo& component_info) {
     base::HistogramTester histogram_tester;
 
-    g_browser_process->optimization_guide_service()->MaybeUpdateHintsComponent(
-        component_info);
+    optimization_guide::OptimizationHintsComponentUpdateListener::GetInstance()
+        ->MaybeUpdateHintsComponent(component_info);
 
     RetryForHistogramUntilCountReached(
         &histogram_tester,
diff --git a/chrome/browser/previews/defer_all_script_priority_browsertest.cc b/chrome/browser/previews/defer_all_script_priority_browsertest.cc
index 64123b2..124e0df 100644
--- a/chrome/browser/previews/defer_all_script_priority_browsertest.cc
+++ b/chrome/browser/previews/defer_all_script_priority_browsertest.cc
@@ -12,8 +12,6 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/browser_process_impl.h"
 #include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
@@ -27,7 +25,7 @@
 #include "components/optimization_guide/core/hints_component_util.h"
 #include "components/optimization_guide/core/optimization_guide_constants.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/optimization_guide_service.h"
+#include "components/optimization_guide/core/optimization_hints_component_update_listener.h"
 #include "components/optimization_guide/core/test_hints_component_creator.h"
 #include "components/optimization_guide/proto/hints.pb.h"
 #include "components/previews/core/previews_features.h"
@@ -179,8 +177,8 @@
       const optimization_guide::HintsComponentInfo& component_info) {
     base::HistogramTester histogram_tester;
 
-    g_browser_process->optimization_guide_service()->MaybeUpdateHintsComponent(
-        component_info);
+    optimization_guide::OptimizationHintsComponentUpdateListener::GetInstance()
+        ->MaybeUpdateHintsComponent(component_info);
 
     RetryForHistogramUntilCountReached(
         &histogram_tester,
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 8ddea2f..9640573 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -30,7 +30,6 @@
 #include "chrome/browser/domain_reliability/service_factory.h"
 #include "chrome/browser/download/download_core_service_factory.h"
 #include "chrome/browser/download/download_service_factory.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/engagement/site_engagement_service_factory.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/favicon/history_ui_favicon_request_handler_factory.h"
@@ -106,6 +105,7 @@
 #include "components/reading_list/features/reading_list_switches.h"
 #include "components/safe_browsing/buildflags.h"
 #include "components/signin/public/base/signin_buildflags.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/spellcheck/spellcheck_buildflags.h"
 #include "extensions/buildflags/buildflags.h"
 #include "media/base/media_switches.h"
diff --git a/chrome/browser/profiles/gaia_info_update_service.cc b/chrome/browser/profiles/gaia_info_update_service.cc
index 8d01b1f..3ff8b96 100644
--- a/chrome/browser/profiles/gaia_info_update_service.cc
+++ b/chrome/browser/profiles/gaia_info_update_service.cc
@@ -37,12 +37,10 @@
 GAIAInfoUpdateService::GAIAInfoUpdateService(
     signin::IdentityManager* identity_manager,
     ProfileAttributesStorage* profile_attributes_storage,
-    const base::FilePath& profile_path,
-    PrefService* profile_prefs)
+    const base::FilePath& profile_path)
     : identity_manager_(identity_manager),
       profile_attributes_storage_(profile_attributes_storage),
-      profile_path_(profile_path),
-      profile_prefs_(profile_prefs) {
+      profile_path_(profile_path) {
   identity_manager_->AddObserver(this);
 
   if (!ShouldUpdatePrimaryAccount()) {
@@ -94,11 +92,7 @@
   gaia_id_of_profile_attribute_entry_ = info.gaia;
   entry->SetGAIAGivenName(base::UTF8ToUTF16(info.given_name));
   entry->SetGAIAName(base::UTF8ToUTF16(info.full_name));
-
   entry->SetHostedDomain(info.hosted_domain);
-  const base::string16 hosted_domain = base::UTF8ToUTF16(info.hosted_domain);
-  profile_prefs_->SetString(prefs::kGoogleServicesHostedDomain,
-                            base::UTF16ToUTF8(hosted_domain));
 
   if (info.picture_url == kNoPictureURLFound) {
     entry->SetGAIAPicture(std::string(), gfx::Image());
@@ -147,8 +141,6 @@
   entry->SetGAIAGivenName(base::string16());
   entry->SetGAIAPicture(std::string(), gfx::Image());
   entry->SetHostedDomain(std::string());
-  // Unset the cached URL.
-  profile_prefs_->ClearPref(prefs::kGoogleServicesHostedDomain);
 }
 
 void GAIAInfoUpdateService::Shutdown() {
diff --git a/chrome/browser/profiles/gaia_info_update_service.h b/chrome/browser/profiles/gaia_info_update_service.h
index 8f7be0f3..bf32d51 100644
--- a/chrome/browser/profiles/gaia_info_update_service.h
+++ b/chrome/browser/profiles/gaia_info_update_service.h
@@ -26,8 +26,7 @@
  public:
   GAIAInfoUpdateService(signin::IdentityManager* identity_manager,
                         ProfileAttributesStorage* profile_attributes_storage,
-                        const base::FilePath& profile_path,
-                        PrefService* prefs);
+                        const base::FilePath& profile_path);
 
   ~GAIAInfoUpdateService() override;
 
@@ -58,7 +57,6 @@
   signin::IdentityManager* identity_manager_;
   ProfileAttributesStorage* profile_attributes_storage_;
   const base::FilePath profile_path_;
-  PrefService* profile_prefs_;
   // TODO(msalama): remove when |SigninProfileAttributesUpdater| is folded into
   // |GAIAInfoUpdateService|.
   std::string gaia_id_of_profile_attribute_entry_;
diff --git a/chrome/browser/profiles/gaia_info_update_service_factory.cc b/chrome/browser/profiles/gaia_info_update_service_factory.cc
index bf39ce76..53aa77cf 100644
--- a/chrome/browser/profiles/gaia_info_update_service_factory.cc
+++ b/chrome/browser/profiles/gaia_info_update_service_factory.cc
@@ -47,7 +47,7 @@
   return new GAIAInfoUpdateService(
       IdentityManagerFactory::GetForProfile(profile),
       &g_browser_process->profile_manager()->GetProfileAttributesStorage(),
-      profile->GetPath(), profile->GetPrefs());
+      profile->GetPath());
 }
 
 bool GAIAInfoUpdateServiceFactory::ServiceIsNULLWhileTesting() const {
diff --git a/chrome/browser/profiles/gaia_info_update_service_unittest.cc b/chrome/browser/profiles/gaia_info_update_service_unittest.cc
index e40602e..0508d2a 100644
--- a/chrome/browser/profiles/gaia_info_update_service_unittest.cc
+++ b/chrome/browser/profiles/gaia_info_update_service_unittest.cc
@@ -88,7 +88,7 @@
     service_.reset(new GAIAInfoUpdateService(
         identity_test_env_.identity_manager(),
         testing_profile_manager_.profile_attributes_storage(),
-        profile()->GetPath(), profile()->GetPrefs()));
+        profile()->GetPath()));
   }
 
   void TearDown() override {
@@ -166,9 +166,6 @@
   EXPECT_EQ(entry->GetGAIAGivenName(), base::UTF8ToUTF16("Pat"));
   EXPECT_EQ(entry->GetGAIAName(), base::UTF8ToUTF16("Pat Foo"));
   EXPECT_EQ(entry->GetHostedDomain(), kNoHostedDomainFound);
-  EXPECT_EQ(
-      profile()->GetPrefs()->GetString(prefs::kGoogleServicesHostedDomain),
-      kNoHostedDomainFound);
 
   gfx::Image gaia_picture = gfx::test::CreateImage(256, 256);
   signin::SimulateAccountImageFetch(identity_test_env()->identity_manager(),
@@ -183,10 +180,6 @@
   EXPECT_TRUE(entry->GetGAIAName().empty());
   EXPECT_EQ(nullptr, entry->GetGAIAPicture());
   EXPECT_TRUE(entry->GetHostedDomain().empty());
-  EXPECT_TRUE(profile()
-                  ->GetPrefs()
-                  ->GetString(prefs::kGoogleServicesHostedDomain)
-                  .empty());
 }
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
@@ -229,9 +222,6 @@
   EXPECT_EQ(entry->GetGAIAGivenName(), base::UTF8ToUTF16("Pat"));
   EXPECT_EQ(entry->GetGAIAName(), base::UTF8ToUTF16("Pat Foo"));
   EXPECT_EQ(entry->GetHostedDomain(), kNoHostedDomainFound);
-  EXPECT_EQ(
-      profile()->GetPrefs()->GetString(prefs::kGoogleServicesHostedDomain),
-      kNoHostedDomainFound);
   EXPECT_TRUE(gfx::test::AreImagesEqual(gaia_picture, entry->GetAvatarIcon()));
 }
 
@@ -253,9 +243,6 @@
   EXPECT_EQ(entry->GetGAIAGivenName(), base::UTF8ToUTF16("Pat"));
   EXPECT_EQ(entry->GetGAIAName(), base::UTF8ToUTF16("Pat Foo"));
   EXPECT_EQ(entry->GetHostedDomain(), kNoHostedDomainFound);
-  EXPECT_EQ(
-      profile()->GetPrefs()->GetString(prefs::kGoogleServicesHostedDomain),
-      kNoHostedDomainFound);
 
   gfx::Image gaia_picture = gfx::test::CreateImage(256, 256);
   signin::SimulateAccountImageFetch(identity_test_env()->identity_manager(),
@@ -272,10 +259,6 @@
   EXPECT_TRUE(entry->GetGAIAName().empty());
   EXPECT_EQ(nullptr, entry->GetGAIAPicture());
   EXPECT_TRUE(entry->GetHostedDomain().empty());
-  EXPECT_TRUE(profile()
-                  ->GetPrefs()
-                  ->GetString(prefs::kGoogleServicesHostedDomain)
-                  .empty());
 }
 
 TEST_F(GAIAInfoUpdateServiceTest, LogInLogOutLogIn) {
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 0c11f4e0..12bf3d9 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -1413,12 +1413,6 @@
   ChildAccountServiceFactory::GetForProfile(profile)->Init();
   SupervisedUserServiceFactory::GetForProfile(profile)->Init();
 #endif
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
-  // If the lock enabled algorithm changed, update this profile's lock status.
-  // This depends on services which shouldn't be initialized until
-  // DoFinalInitForServices.
-  profiles::UpdateIsProfileLockEnabledIfNeeded(profile);
-#endif
 
   // Activate data reduction proxy. This creates a request context and makes a
   // URL request to check if the data reduction proxy server is reachable.
diff --git a/chrome/browser/profiles/profile_window.cc b/chrome/browser/profiles/profile_window.cc
index 3186fb97..ae751d549 100644
--- a/chrome/browser/profiles/profile_window.cc
+++ b/chrome/browser/profiles/profile_window.cc
@@ -347,21 +347,16 @@
     return false;
   }
 
-  std::string hosted_domain = profile->GetPrefs()->
-      GetString(prefs::kGoogleServicesHostedDomain);
-  // TODO(mlerman): After one release remove any hosted_domain reference to the
-  // pref, since all users will have this in the AccountTrackerService.
-  if (hosted_domain.empty()) {
-    signin::IdentityManager* identity_manager =
-        IdentityManagerFactory::GetForProfile(profile);
-
-    base::Optional<AccountInfo> primary_account_info =
-        identity_manager->FindExtendedAccountInfoForAccountWithRefreshToken(
-            identity_manager->GetPrimaryAccountInfo());
-
-    if (primary_account_info.has_value())
-      hosted_domain = primary_account_info.value().hosted_domain;
-  }
+  signin::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(profile);
+  if (!identity_manager->HasPrimaryAccount())
+    return false;
+  base::Optional<AccountInfo> primary_account_info =
+      identity_manager->FindExtendedAccountInfoForAccountWithRefreshToken(
+          identity_manager->GetPrimaryAccountInfo());
+  std::string hosted_domain = primary_account_info.has_value()
+                                  ? primary_account_info.value().hosted_domain
+                                  : "";
 
   // TODO(mlerman): Prohibit only users who authenticate using SAML. Until then,
   // prohibited users who use hosted domains (aside from google.com).
diff --git a/chrome/browser/profiles/profiles_state.cc b/chrome/browser/profiles/profiles_state.cc
index fe05d39..94e30b2 100644
--- a/chrome/browser/profiles/profiles_state.cc
+++ b/chrome/browser/profiles/profiles_state.cc
@@ -210,14 +210,6 @@
 }
 
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
-void UpdateIsProfileLockEnabledIfNeeded(Profile* profile) {
-  if (!profile->GetPrefs()->GetString(prefs::kGoogleServicesHostedDomain).
-      empty())
-    return;
-
-  UpdateGaiaProfileInfoIfNeeded(profile);
-}
-
 void UpdateGaiaProfileInfoIfNeeded(Profile* profile) {
   DCHECK(profile);
 
diff --git a/chrome/browser/profiles/profiles_state.h b/chrome/browser/profiles/profiles_state.h
index dff8e968..a0ae957 100644
--- a/chrome/browser/profiles/profiles_state.h
+++ b/chrome/browser/profiles/profiles_state.h
@@ -92,10 +92,6 @@
 bool IsProfileLocked(const base::FilePath& profile_path);
 
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
-// If the lock-enabled information for this profile is not up to date, starts
-// an update for the Gaia profile info.
-void UpdateIsProfileLockEnabledIfNeeded(Profile* profile);
-
 // Starts an update for a new version of the Gaia profile picture and other
 // profile info.
 void UpdateGaiaProfileInfoIfNeeded(Profile* profile);
diff --git a/chrome/browser/profiles/reporting_util.cc b/chrome/browser/profiles/reporting_util.cc
index 4c5556a0..78780bc5 100644
--- a/chrome/browser/profiles/reporting_util.cc
+++ b/chrome/browser/profiles/reporting_util.cc
@@ -17,31 +17,31 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
+#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 
-#ifdef OS_CHROMEOS
+#if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/login/users/affiliation.h"
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
-#endif  // OS_CHROMEOS
+#endif  // defined(OS_CHROMEOS)
 
 namespace {
 
-#ifdef OS_CHROMEOS
-// A callback which fetches device dm_token based on user affiliation.
-using DeviceDMTokenCallback = base::RepeatingCallback<std::string(
-    const std::vector<std::string>& user_affiliation_ids)>;
-
 // Returns policy for the given |profile|. If failed to get policy returns
 // nullptr.
 const enterprise_management::PolicyData* GetPolicyData(Profile* profile) {
   if (!profile)
     return nullptr;
 
-  policy::UserCloudPolicyManagerChromeOS* manager =
+  auto* manager =
+#if defined(OS_CHROMEOS)
       profile->GetUserCloudPolicyManagerChromeOS();
+#else
+      profile->GetUserCloudPolicyManager();
+#endif
   if (!manager)
     return nullptr;
 
@@ -52,6 +52,28 @@
   return store->policy();
 }
 
+// Returns User DMToken for a given |profile| if:
+// * |profile| is NOT incognito profile.
+// * |profile| is NOT sign-in screen profile
+// * user corresponding to a |profile| is managed.
+// Otherwise returns empty string. More about DMToken:
+// go/dmserver-domain-model#dmtoken.
+std::string GetUserDmToken(Profile* profile) {
+  if (!profile)
+    return std::string();
+
+  const enterprise_management::PolicyData* policy = GetPolicyData(profile);
+  if (!policy || !policy->has_request_token())
+    return std::string();
+
+  return policy->request_token();
+}
+
+#if defined(OS_CHROMEOS)
+// A callback which fetches device dm_token based on user affiliation.
+using DeviceDMTokenCallback = base::RepeatingCallback<std::string(
+    const std::vector<std::string>& user_affiliation_ids)>;
+
 // Returns the Device DMToken for the given |profile| if:
 // * |profile| is NOT incognito profile
 // * user corresponding to a given |profile| is affiliated.
@@ -81,23 +103,7 @@
   return device_dm_token_callback.Run(user_affiliation_ids);
 }
 
-// Returns User DMToken for a given |profile| if:
-// * |profile| is NOT incognito profile.
-// * |profile| is NOT sign-in screen profile
-// * user corresponding to a |profile| is managed.
-// Otherwise returns empty string. More about DMToken:
-// go/dmserver-domain-model#dmtoken.
-std::string GetUserDmToken(Profile* profile) {
-  if (!profile)
-    return std::string();
-
-  const enterprise_management::PolicyData* policy = GetPolicyData(profile);
-  if (!policy || !policy->has_request_token())
-    return std::string();
-
-  return policy->request_token();
-}
-#endif  // OS_CHROMEOS
+#endif  // defined(OS_CHROMEOS)
 
 }  // namespace
 
@@ -120,22 +126,22 @@
 
   context.SetStringPath("profile.profilePath", profile->GetPath().value());
 
-#ifdef OS_CHROMEOS
   const enterprise_management::PolicyData* policy = GetPolicyData(profile);
 
   if (policy) {
     if (policy->has_device_id())
       context.SetStringPath("profile.clientId", policy->device_id());
 
+#if defined(OS_CHROMEOS)
     std::string device_dm_token = GetDeviceDmToken(profile);
     if (!device_dm_token.empty())
       context.SetStringPath("device.dmToken", device_dm_token);
+#endif
 
     std::string user_dm_token = GetUserDmToken(profile);
     if (!user_dm_token.empty())
       context.SetStringPath("profile.dmToken", user_dm_token);
   }
-#endif  // OS_CHROMEOS
 
   return context;
 }
diff --git a/chrome/browser/push_messaging/budget_database.cc b/chrome/browser/push_messaging/budget_database.cc
index 400d8f7..78b851cb 100644
--- a/chrome/browser/push_messaging/budget_database.cc
+++ b/chrome/browser/push_messaging/budget_database.cc
@@ -11,11 +11,11 @@
 #include "base/task/thread_pool.h"
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/push_messaging/budget.pb.h"
 #include "components/leveldb_proto/public/proto_database_provider.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/push_messaging/budget_database_unittest.cc b/chrome/browser/push_messaging/budget_database_unittest.cc
index 7d714e7..edc60fac 100644
--- a/chrome/browser/push_messaging/budget_database_unittest.cc
+++ b/chrome/browser/push_messaging/budget_database_unittest.cc
@@ -12,12 +12,12 @@
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/simple_test_clock.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/push_messaging/budget.pb.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/leveldb_proto/public/proto_database.h"
 #include "components/leveldb_proto/public/proto_database_provider.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index a6ffbd8..8d4f119 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/gcm/gcm_profile_service_factory.h"
 #include "chrome/browser/gcm/instance_id/instance_id_profile_service_factory.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
@@ -59,6 +58,7 @@
 #include "components/network_session_configurator/common/network_switches.h"
 #include "components/permissions/permission_request_manager.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/browsing_data_remover.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/push_messaging/push_messaging_notification_manager.cc b/chrome/browser/push_messaging/push_messaging_notification_manager.cc
index 3133819..53c6ec4 100644
--- a/chrome/browser/push_messaging/push_messaging_notification_manager.cc
+++ b/chrome/browser/push_messaging/push_messaging_notification_manager.cc
@@ -17,12 +17,12 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/notifications/platform_notification_service_factory.h"
 #include "chrome/browser/notifications/platform_notification_service_impl.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/push_messaging/push_messaging_constants.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/url_formatter/elide_url.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
diff --git a/chrome/browser/reading_list/android/empty_reading_list_manager.cc b/chrome/browser/reading_list/android/empty_reading_list_manager.cc
index 9376e8f..d4e4d81a 100644
--- a/chrome/browser/reading_list/android/empty_reading_list_manager.cc
+++ b/chrome/browser/reading_list/android/empty_reading_list_manager.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/reading_list/android/empty_reading_list_manager.h"
 
+#include "base/logging.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
 
 EmptyReadingListManager::EmptyReadingListManager() = default;
@@ -17,6 +18,7 @@
 const bookmarks::BookmarkNode* EmptyReadingListManager::Add(
     const GURL& url,
     const std::string& title) {
+  LOG(ERROR) << "Try to add reading list with empty reading list backend.";
   return nullptr;
 }
 
diff --git a/chrome/browser/reading_list/android/reading_list_manager.h b/chrome/browser/reading_list/android/reading_list_manager.h
index efa7e53..28b589a3 100644
--- a/chrome/browser/reading_list/android/reading_list_manager.h
+++ b/chrome/browser/reading_list/android/reading_list_manager.h
@@ -47,7 +47,7 @@
   // Adds a reading list article to the unread section, and return the bookmark
   // node representation. The bookmark node is owned by this class. If there is
   // a duplicate URL, a new bookmark node will be created, and the old bookmark
-  // node pointer will be invalidated.
+  // node pointer will be invalidated. May return nullptr on error.
   virtual const bookmarks::BookmarkNode* Add(const GURL& url,
                                              const std::string& title) = 0;
 
diff --git a/chrome/browser/reading_list/android/reading_list_manager_impl.cc b/chrome/browser/reading_list/android/reading_list_manager_impl.cc
index 98b8bcc..b2bf08d 100644
--- a/chrome/browser/reading_list/android/reading_list_manager_impl.cc
+++ b/chrome/browser/reading_list/android/reading_list_manager_impl.cc
@@ -132,13 +132,13 @@
 const BookmarkNode* ReadingListManagerImpl::Add(const GURL& url,
                                                 const std::string& title) {
   DCHECK(reading_list_model_->loaded());
+  if (!reading_list_model_->IsUrlSupported(url))
+    return nullptr;
 
   // Add or swap the reading list entry.
   const auto& new_entry = reading_list_model_->AddEntry(
       url, title, reading_list::ADDED_VIA_CURRENT_APP);
   const auto* node = FindBookmarkByURL(new_entry.URL());
-  DCHECK(node)
-      << "Bookmark node should have been create in ReadingListDidAddEntry().";
   return node;
 }
 
diff --git a/chrome/browser/reading_list/android/reading_list_manager_impl_unittest.cc b/chrome/browser/reading_list/android/reading_list_manager_impl_unittest.cc
index 1ca6a98..57670f5 100644
--- a/chrome/browser/reading_list/android/reading_list_manager_impl_unittest.cc
+++ b/chrome/browser/reading_list/android/reading_list_manager_impl_unittest.cc
@@ -32,6 +32,7 @@
 constexpr char kReadStatusKey[] = "read_status";
 constexpr char kReadStatusRead[] = "true";
 constexpr char kReadStatusUnread[] = "false";
+constexpr char kInvalidUTF8[] = "\xc3\x28";
 
 class MockObserver : public ReadingListManager::Observer {
  public:
@@ -228,6 +229,31 @@
   EXPECT_EQ(url, new_node->url());
 }
 
+// If Add() with an invalid title, nullptr will be returned.
+TEST_F(ReadingListManagerImplTest, AddInvalidTitle) {
+  GURL url(kURL);
+
+  // Use an invalid UTF8 string.
+  base::string16 dummy;
+  EXPECT_FALSE(
+      base::UTF8ToUTF16(kInvalidUTF8, base::size(kInvalidUTF8), &dummy));
+  const auto* new_node = Add(url, std::string(kInvalidUTF8));
+  EXPECT_EQ(nullptr, new_node)
+      << "Should return nullptr when failed to parse the title.";
+}
+
+// If Add() with an invalid URL, nullptr will be returned.
+TEST_F(ReadingListManagerImplTest, AddInvalidURL) {
+  GURL invalid_url("chrome://flags");
+  EXPECT_FALSE(reading_list_model()->IsUrlSupported(invalid_url));
+
+  // Use an invalid URL, the observer method ReadingListDidAddEntry() won't be
+  // invoked.
+  const auto* new_node = manager()->Add(invalid_url, kTitle);
+  EXPECT_EQ(nullptr, new_node)
+      << "Should return nullptr when the URL scheme is not supported.";
+}
+
 // Verifes SetReadStatus()/GetReadStatus() API.
 TEST_F(ReadingListManagerImplTest, ReadStatus) {
   GURL url(kURL);
diff --git a/chrome/browser/resource_coordinator/session_restore_policy.cc b/chrome/browser/resource_coordinator/session_restore_policy.cc
index 2e4409f..d9e83dc 100644
--- a/chrome/browser/resource_coordinator/session_restore_policy.cc
+++ b/chrome/browser/resource_coordinator/session_restore_policy.cc
@@ -18,7 +18,6 @@
 #include "base/sequence_checker.h"
 #include "base/system/sys_info.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/tab_manager_features.h"
 #include "chrome/common/url_constants.h"
@@ -26,6 +25,7 @@
 #include "components/performance_manager/public/graph/graph.h"
 #include "components/performance_manager/public/performance_manager.h"
 #include "components/performance_manager/public/persistence/site_data/site_data_reader.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_controller.h"
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc b/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc
index ed86c99..f2563e97 100644
--- a/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc
@@ -11,7 +11,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit.h"
 #include "chrome/browser/resource_coordinator/tab_activity_watcher.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h"
@@ -26,6 +25,7 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/test_browser_window.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/ukm/content/source_url_recorder.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
diff --git a/chrome/browser/resource_coordinator/tab_metrics_logger.cc b/chrome/browser/resource_coordinator/tab_metrics_logger.cc
index bc09856..0c46381 100644
--- a/chrome/browser/resource_coordinator/tab_metrics_logger.cc
+++ b/chrome/browser/resource_coordinator/tab_metrics_logger.cc
@@ -12,7 +12,6 @@
 #include "base/notreached.h"
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/resource_coordinator/tab_manager_features.h"
@@ -28,6 +27,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/recently_audible_helper.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc b/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc
index b412585d..6dfa9db 100644
--- a/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/test/task_environment.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/resource_coordinator/tab_metrics_event.pb.h"
 #include "chrome/browser/resource_coordinator/tab_ranker/tab_features.h"
 #include "chrome/browser/resource_coordinator/tab_ranker/tab_features_test_helper.h"
@@ -25,6 +24,7 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/test_browser_window.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/web_contents_tester.h"
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index 39009ad..185cb16 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -453,6 +453,7 @@
       "background/cursors_test.js",
       "background/download_handler_test.js",
       "background/editing/editing_test.js",
+      "background/editing/intent_handler_test.js",
       "background/keyboard_handler_test.js",
       "background/live_regions_test.js",
       "background/locale_output_helper_test.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_line.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_line.js
index ca8bf55..2791e5f 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_line.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_line.js
@@ -386,6 +386,11 @@
     return this.startContainerValue_;
   }
 
+  /** @return {!cursors.Cursor} */
+  get startCursor() {
+    return this.start_;
+  }
+
   /** @return {boolean} */
   hasCollapsedSelection() {
     return this.start_.equals(this.end_);
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js
index 4ae374f..05ea0e5 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js
@@ -8,12 +8,18 @@
 
 goog.provide('IntentHandler');
 
+goog.require('constants');
 goog.require('editing.EditableLine');
+goog.require('Output');
 
 goog.scope(function() {
 const AutomationIntent = chrome.automation.AutomationIntent;
+const Dir = constants.Dir;
 const IntentCommandType = chrome.automation.IntentCommandType;
 const IntentTextBoundaryType = chrome.automation.IntentTextBoundaryType;
+const Movement = cursors.Movement;
+const Range = cursors.Range;
+const Unit = cursors.Unit;
 
 /**
  * A stateless class that turns intents into speech.
@@ -95,6 +101,26 @@
         cur.speakLine(prev);
         return true;
 
+      case IntentTextBoundaryType.WORD_END:
+      case IntentTextBoundaryType.WORD_START:
+        const pos = cur.startCursor;
+
+        // When movement goes to the end of a word, we actually want to describe
+        // the word itself; this is considered the previous word so impacts the
+        // movement type below. We can give further context e.g. by saying "end
+        // of word", if we chose to be more verbose.
+        const shouldMoveToPreviousWord =
+            intent.textBoundary === IntentTextBoundaryType.WORD_END;
+        const start = pos.move(
+            Unit.WORD,
+            shouldMoveToPreviousWord ? Movement.DIRECTIONAL : Movement.BOUND,
+            Dir.BACKWARD);
+        const end = pos.move(Unit.WORD, Movement.BOUND, Dir.FORWARD);
+        new Output()
+            .withSpeech(new Range(start, end), null, Output.EventType.NAVIGATE)
+            .go();
+        return true;
+
         // TODO: implement support.
       case IntentTextBoundaryType.FORMAT:
       case IntentTextBoundaryType.OBJECT:
@@ -108,8 +134,6 @@
       case IntentTextBoundaryType.SENTENCE_START:
       case IntentTextBoundaryType.SENTENCE_START_OR_END:
       case IntentTextBoundaryType.WEB_PAGE:
-      case IntentTextBoundaryType.WORD_END:
-      case IntentTextBoundaryType.WORD_START:
       case IntentTextBoundaryType.WORD_START_OR_END:
         break;
     }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js
new file mode 100644
index 0000000..f668a3b
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js
@@ -0,0 +1,136 @@
+// 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.
+
+GEN_INCLUDE([
+  '../../testing/chromevox_next_e2e_test_base.js',
+  '../../../common/testing/assert_additions.js',
+]);
+
+/**
+ * Test fixture for intent handler tests.
+ * These tests are written to be as "unit" test like as possible e.g. mocking
+ * out classes not under test but it runs under a full extension test
+ * environment to get things like extension api literals.
+ */
+ChromeVoxIntentHandlerTest = class extends ChromeVoxNextE2ETest {
+  /** @override */
+  setUp() {
+    window.Dir = constants.Dir;
+    window.IntentTextBoundaryType = chrome.automation.IntentTextBoundaryType;
+    window.Movement = cursors.Movement;
+    window.Unit = cursors.Unit;
+  }
+};
+
+SYNC_TEST_F('ChromeVoxIntentHandlerTest', 'MoveByCharacter', function() {
+  let lastSpoken;
+  ChromeVox.tts.speak = (text) => lastSpoken = text;
+
+  const intent = {textBoundary: IntentTextBoundaryType.CHARACTER};
+  const move = IntentHandler.onMoveSelection.bind(null, intent);
+
+  move({text: 'hello', startOffset: 0});
+  assertEquals('h', lastSpoken);
+  move({text: 'hello', startOffset: 1});
+  assertEquals('e', lastSpoken);
+  move({text: 'hello', startOffset: 2});
+  assertEquals('l', lastSpoken);
+  move({text: 'hello', startOffset: 3});
+  assertEquals('l', lastSpoken);
+  move({text: 'hello', startOffset: 4});
+  assertEquals('o', lastSpoken);
+  move({text: 'hello', startOffset: 5});
+  assertEquals('\n', lastSpoken);
+});
+
+SYNC_TEST_F('ChromeVoxIntentHandlerTest', 'MoveByWord', function() {
+  let calls = [];
+  const fakeLine = new class {
+    constructor() {}
+
+    get startCursor() {
+      return new class {
+        move(...args) {
+          calls.push(['move', ...args]);
+        }
+      };
+    }
+  };
+
+  Output.prototype.withSpeech = function(...args) {
+    calls.push(['withSpeech', ...args]);
+    return this;
+  };
+
+  Output.prototype.go = function() {
+    calls.push(['go']);
+  };
+
+  let intent = {textBoundary: IntentTextBoundaryType.WORD_END};
+  IntentHandler.onMoveSelection(intent, fakeLine);
+  assertEquals(4, calls.length);
+  assertArraysEquals(
+      ['move', Unit.WORD, Movement.DIRECTIONAL, Dir.BACKWARD], calls[0]);
+  assertArraysEquals(
+      ['move', Unit.WORD, Movement.BOUND, Dir.FORWARD], calls[1]);
+  assertArraysEquals(
+      ['withSpeech', {}, null, Output.EventType.NAVIGATE], calls[2]);
+  assertArraysEquals(['go'], calls[3]);
+
+  calls = [];
+  intent = {textBoundary: IntentTextBoundaryType.WORD_START};
+  IntentHandler.onMoveSelection(intent, fakeLine);
+  assertEquals(4, calls.length);
+  assertArraysEquals(
+      ['move', Unit.WORD, Movement.BOUND, Dir.BACKWARD], calls[0]);
+  assertArraysEquals(
+      ['move', Unit.WORD, Movement.BOUND, Dir.FORWARD], calls[1]);
+  assertArraysEquals(
+      ['withSpeech', {}, null, Output.EventType.NAVIGATE], calls[2]);
+  assertArraysEquals(['go'], calls[3]);
+
+  calls = [];
+  intent = {textBoundary: IntentTextBoundaryType.WORD_START_OR_END};
+  IntentHandler.onMoveSelection(intent, fakeLine);
+  assertEquals(0, calls.length);
+});
+
+SYNC_TEST_F('ChromeVoxIntentHandlerTest', 'MoveByLine', function() {
+  const fakeLine = new class {
+    constructor() {
+      this.speakLineCount = 0;
+    }
+
+    speakLine() {
+      this.speakLineCount++;
+    }
+
+    get startCursor() {
+      return new class {
+        move() {}
+      };
+    }
+  };
+
+  Output.prototype.withSpeech = function() {
+    return this;
+  };
+  Output.prototype.go = function() {};
+
+  let intent = {textBoundary: IntentTextBoundaryType.LINE_END};
+  IntentHandler.onMoveSelection(intent, fakeLine);
+  assertEquals(1, fakeLine.speakLineCount);
+
+  intent = {textBoundary: IntentTextBoundaryType.LINE_START};
+  IntentHandler.onMoveSelection(intent, fakeLine);
+  assertEquals(2, fakeLine.speakLineCount);
+
+  intent = {textBoundary: IntentTextBoundaryType.LINE_START_OR_END};
+  IntentHandler.onMoveSelection(intent, fakeLine);
+  assertEquals(3, fakeLine.speakLineCount);
+
+  intent = {textBoundary: IntentTextBoundaryType.WORD_START};
+  IntentHandler.onMoveSelection(intent, fakeLine);
+  assertEquals(3, fakeLine.speakLineCount);
+});
diff --git a/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn b/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
index 1903bc3..220440c 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
+++ b/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
@@ -10,29 +10,33 @@
 component_js_files = [
   "emoji_picker.js",
   "emoji_group.js",
+  "emoji_group_button.js",
   "icons.js",
 ]
 
-polymer_deps =
-    [ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled" ]
-
 resources_grd_file = "$target_gen_dir/resources.grd"
 components_grdp_file = "$target_gen_dir/components.grdp"
+data_grdp_file = "$target_gen_dir/data.grdp"
 
 generate_grd("build_grd") {
-  deps = [ ":build_grdp" ]
+  deps = [
+    ":build_components_grdp",
+    ":build_data_grdp",
+  ]
   grd_prefix = "emoji_picker"
   out_grd = resources_grd_file
   input_files = [
     "index.html",
     "types.js",
-    "emoji_ordering.json",
   ]
-  grdp_files = [ components_grdp_file ]
+  grdp_files = [
+    components_grdp_file,
+    data_grdp_file,
+  ]
   input_files_base_dir = rebase_path(".", "//")
 }
 
-generate_grd("build_grdp") {
+generate_grd("build_components_grdp") {
   deps = [ ":web_components" ]
   grd_prefix = "emoji_picker"
   out_grd = components_grdp_file
@@ -40,6 +44,13 @@
   input_files_base_dir = rebase_path(target_gen_dir, root_build_dir)
 }
 
+generate_grd("build_data_grdp") {
+  grd_prefix = "emoji_picker"
+  out_grd = data_grdp_file
+  input_files = [ "emoji_13_1_ordering.json" ]
+  input_files_base_dir = rebase_path("//third_party/emoji-metadata/src", "//")
+}
+
 grit("resources") {
   # These arguments are needed since the grd is generated at build time.
   enable_input_discovery_for_gn_analyze = false
@@ -56,21 +67,34 @@
 
 js_library("emoji_picker") {
   deps = [
-           ":types",
-           ":icons",
-           ":emoji_group",
-           "//ui/webui/resources/js:load_time_data.m",
-           "//third_party/polymer/v3_0/components-chromium/iron-icon",
-         ] + polymer_deps
+    ":emoji_group",
+    ":emoji_group_button",
+    ":types",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:assert.m",
+  ]
 }
 
 js_library("emoji_group") {
-  deps = [ ":types" ] + polymer_deps
+  deps = [
+    ":types",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
+}
+
+js_library("emoji_group_button") {
+  deps = [
+    ":icons",
+    "//third_party/polymer/v3_0/components-chromium/iron-icon",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
 }
 
 js_library("icons") {
-  deps = [ "//third_party/polymer/v3_0/components-chromium/iron-iconset-svg" ] +
-         polymer_deps
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/iron-iconset-svg",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
 }
 
 js_library("types") {
@@ -80,6 +104,7 @@
   is_polymer3 = true
   deps = [
     ":emoji_group",
+    ":emoji_group_button",
     ":emoji_picker",
     ":icons",
     ":types",
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_group_button.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_group_button.html
new file mode 100644
index 0000000..29022fd
--- /dev/null
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_group_button.html
@@ -0,0 +1,21 @@
+<style>
+  button {
+    background-color: transparent;
+    border: 5px solid transparent;
+    padding: 0;
+    width: 36px;
+  }
+
+  button:hover {
+    color: red;
+  }
+
+  .active {
+    border-bottom: 5px solid blue;
+    color: blue;
+  }
+</style>
+
+<button class$="[[_className(active)]]" on-click="handleClick">
+  <iron-icon icon="[[icon]]"></iron-icon>
+</button>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_group_button.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_group_button.js
new file mode 100644
index 0000000..b0c46bf
--- /dev/null
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_group_button.js
@@ -0,0 +1,47 @@
+// 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 './icons.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
+
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {GROUP_BUTTON_EVENT, GroupButtonEvent} from './types.js';
+
+class EmojiGroupButton extends PolymerElement {
+  static get is() {
+    return 'emoji-group-button';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      /** @type {string} */
+      icon: {type: String},
+      group: {type: String},
+      active: {type: Boolean, value: false},
+    };
+  }
+
+  constructor() {
+    super();
+  }
+
+  handleClick(ev) {
+    /** @type {GroupButtonEvent} */
+    const event = new CustomEvent(
+        GROUP_BUTTON_EVENT,
+        {bubbles: true, composed: true, detail: {group: this.group}});
+    this.dispatchEvent(event);
+  }
+
+  _className(active) {
+    return active ? 'active' : '';
+  }
+}
+
+customElements.define(EmojiGroupButton.is, EmojiGroupButton);
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.html
index 20f790d8..d087a7f 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.html
@@ -8,6 +8,17 @@
     min-height: 343px;
     width: calc(36px * 7);
   }
+
+  .emoji-tabs {
+    display: flex;
+    overflow-x: scroll;
+    width: 100%;
+  }
+
+  .emoji-groups {
+    max-height: 400px;
+    overflow-y: scroll;
+  }
 </style>
 
 <div class="emoji-picker">
@@ -18,21 +29,24 @@
   ></input>
 
   <div class="emoji-tabs">
-    <iron-icon icon="emoji_picker:schedule"></iron-icon>
-    <iron-icon icon="emoji_picker:insert_emoticon"></iron-icon>
-    <iron-icon icon="emoji_picker:emoji_people"></iron-icon>
-    <iron-icon icon="emoji_picker:emoji_nature"></iron-icon>
-    <iron-icon icon="emoji_picker:emoji_food_beverage"></iron-icon>
-    <iron-icon icon="emoji_picker:emoji_transportation"></iron-icon>
-    <iron-icon icon="emoji_picker:emoji_events"></iron-icon>
-    <iron-icon icon="emoji_picker:emoji_objects"></iron-icon>
-    <iron-icon icon="emoji_picker:emoji_symbols"></iron-icon>
-    <iron-icon icon="emoji_picker:flag"></iron-icon>
+    <template is="dom-repeat" items="[[groups]]">
+      <emoji-group-button group="[[item.group]]" active="[[item.active]]"
+        icon="[[item.icon]]"></emoji-group-button>
+    </template>
   </div>
 
   <div class="emoji-groups w-100">
-    <template is="dom-repeat" items="{{emojiData}}">
-      <emoji-group data="[[item]]"></emoji-group>
+    <!--
+      group-history is the "frequently used" group and is populated in code,
+      whereas the other groups are created from the emoji metadata.
+    -->
+    <div id="group-history">
+      <emoji-group data="[[history]]"></emoji-group>
+    </div>
+    <template is="dom-repeat" items="[[emojiData]]">
+      <div id="group-[[index]]">
+        <emoji-group data="[[item]]"></emoji-group>
+      </div>
     </template>
   </div>
 </div>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
index 0527ede..2670e75 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
@@ -2,17 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import './strings.m.js';
-import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import './icons.js';
 import './emoji_group.js';
+import './emoji_group_button.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {assert} from 'chrome://resources/js/assert.m.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {EmojiData} from './types.js';
+import {EmojiData, EmojiGroup, GROUP_BUTTON_EVENT} from './types.js';
 
-const EMOJI_ORDERING_JSON = '/emoji_ordering.json';
+const EMOJI_ORDERING_JSON = '/emoji_13_1_ordering.json';
+
+const GROUP_TABS = [
+  {icon: 'emoji_picker:schedule', group: 'history', active: true},
+  {icon: 'emoji_picker:insert_emoticon', group: '0', active: false},
+  {icon: 'emoji_picker:emoji_people', group: '1', active: false},
+  {icon: 'emoji_picker:emoji_nature', group: '2', active: false},
+  {icon: 'emoji_picker:emoji_food_beverage', group: '3', active: false},
+  {icon: 'emoji_picker:emoji_transportation', group: '4', active: false},
+  {icon: 'emoji_picker:emoji_events', group: '5', active: false},
+  {icon: 'emoji_picker:emoji_objects', group: '6', active: false},
+  {icon: 'emoji_picker:emoji_symbols', group: '7', active: false},
+  {icon: 'emoji_picker:flag', group: '8', active: false},
+];
 
 class EmojiPicker extends PolymerElement {
   static get is() {
@@ -25,9 +37,11 @@
 
   static get properties() {
     return {
-      emoji: {type: Array},
+      groups: {type: Array},
       /** @type {?EmojiData} */
       emojiData: {type: Object},
+      /** @type {EmojiGroup} */
+      history: {type: Object},
       search: {type: String},
     };
   }
@@ -35,14 +49,55 @@
   constructor() {
     super();
 
+    this.groups = GROUP_TABS;
+    this.emojiData = [];
+    // TODO(https://crbug.com/1164828): replace placeholder frequently used
+    // data.
+    this.history = {
+      'group': 'Frequently used',
+      'emoji': [
+        {
+          'base': [128512],
+          'alternates': [],
+        },
+        {
+          'base': [128513],
+          'alternates': [],
+        },
+      ],
+    };
+    this.search = '';
+  }
+
+  ready() {
+    super.ready();
+
     const xhr = new XMLHttpRequest();
     xhr.onloadend = () => this.onEmojiDataLoaded(xhr.responseText);
     xhr.open('GET', EMOJI_ORDERING_JSON);
     xhr.send();
 
-    this.emojiData = [];
-    this.emoji = loadTimeData.getString('emoji').split(',');
-    this.search = '';
+    this.addEventListener(
+        GROUP_BUTTON_EVENT, ev => this.selectGroup(ev.detail.group));
+  }
+
+  /**
+   * @param {string} newGroup
+   */
+  selectGroup(newGroup) {
+    let activeGroup = null;
+    // set active to true for selected group and false for others.
+    this.groups.forEach((g, i) => {
+      const isActive = g.group === newGroup;
+      this.set(['groups', i, 'active'], isActive);
+      if (isActive) {
+        activeGroup = g;
+      }
+    });
+    assert(activeGroup, 'no group button was activated');
+    // scroll to selected group's element.
+    this.shadowRoot.getElementById(`group-${activeGroup.group}`)
+        .scrollIntoView();
   }
 
   _formatEmojiData(emojiData) {
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_ordering.json b/chrome/browser/resources/chromeos/emoji_picker/emoji_test_ordering.json
similarity index 100%
rename from chrome/browser/resources/chromeos/emoji_picker/emoji_ordering.json
rename to chrome/browser/resources/chromeos/emoji_picker/emoji_test_ordering.json
diff --git a/chrome/browser/resources/chromeos/emoji_picker/types.js b/chrome/browser/resources/chromeos/emoji_picker/types.js
index b43121af..d83b712 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/types.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/types.js
@@ -15,4 +15,11 @@
 /**
  * @typedef {Array<EmojiGroup>} EmojiData
  */
-export let EmojiData;
\ No newline at end of file
+export let EmojiData;
+
+/**
+ * @typedef {!CustomEvent<{group: string}>} GroupButtonEvent
+ */
+export let GroupButtonEvent;
+
+export const GROUP_BUTTON_EVENT = 'group-button';
diff --git a/chrome/browser/resources/new_tab_page/app.html b/chrome/browser/resources/new_tab_page/app.html
index 057ffc2df..c9b5252 100644
--- a/chrome/browser/resources/new_tab_page/app.html
+++ b/chrome/browser/resources/new_tab_page/app.html
@@ -82,15 +82,6 @@
     width: var(--ntp-search-box-width);
   }
 
-  :host([modules-enabled_]) ntp-middle-slot-promo {
-    width: var(--ntp-search-box-width);
-  }
-
-  :host(:not([modules-enabled_])) ntp-middle-slot-promo {
-    bottom: 16px;
-    position: fixed;
-  }
-
   ntp-realbox {
     visibility: hidden;
   }
@@ -244,19 +235,6 @@
     font-size: .75rem;
   }
 
-  @keyframes fadein {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-
-  .fadein {
-    animation: 100ms ease-in-out fadein;
-  }
-
   svg {
     position: fixed;
   }
@@ -361,6 +339,13 @@
 <div id="oneGoogleBarOverlayBackdrop"></div>
 <svg>
   <defs>
-    <clipPath id="oneGoogleBarClipPath"></clipPath>
+    <clipPath id="oneGoogleBarClipPath">
+      <!-- Set an initial non-empty clip-path so the OneGoogleBar resize events
+           are processed. When the clip-path is empty, it's possible for the
+           OneGoogleBar to get into a state where it does not send  the
+           'overlayUpdates' message which is used to populate this
+           clip-path. -->
+      <rect x="0" y="0" width="1" height="1"></rect>
+    </clipPath>
   </defs>
 </svg>
diff --git a/chrome/browser/resources/new_tab_page/app.js b/chrome/browser/resources/new_tab_page/app.js
index a054f731..69b35034 100644
--- a/chrome/browser/resources/new_tab_page/app.js
+++ b/chrome/browser/resources/new_tab_page/app.js
@@ -858,19 +858,6 @@
   /** @private */
   onMiddleSlotPromoLoaded_() {
     this.middleSlotPromoLoaded_ = true;
-    // The promo is always shown when modules are enabled since it will not
-    // overlap with other elements.
-    if (this.modulesEnabled_) {
-      return;
-    }
-    const onResize = () => {
-      const promoElement = $$(this, 'ntp-middle-slot-promo');
-      promoElement.hidden =
-          $$(this, '#mostVisited').getBoundingClientRect().bottom >=
-          promoElement.offsetTop;
-    };
-    this.eventTracker_.add(window, 'resize', onResize);
-    onResize();
   }
 
   /** @private */
diff --git a/chrome/browser/resources/new_tab_page/middle_slot_promo.html b/chrome/browser/resources/new_tab_page/middle_slot_promo.html
index f2657ea4..e0b9364 100644
--- a/chrome/browser/resources/new_tab_page/middle_slot_promo.html
+++ b/chrome/browser/resources/new_tab_page/middle_slot_promo.html
@@ -1,15 +1,8 @@
 <style>
   :host {
     font-size: 12px;
-    white-space: pre;
-  }
-
-  :host([modules-enabled_]):host {
-    font-size: 13px;
-  }
-
-  :host(:not([modules-enabled_])):host {
     max-width: 537px;
+    white-space: pre;
   }
 
   #container {
@@ -27,11 +20,6 @@
     padding-inline-start: 8px;
   }
 
-  :host([modules-enabled_]) #container {
-    border-radius: 5px;
-    height: 48px;
-  }
-
   a {
     color: var(--cr-link-color);
     cursor: pointer;
@@ -59,13 +47,6 @@
     width: 24px;
   }
 
-  :host([modules-enabled_]) img {
-    background-color: var(--google-grey-refresh-100);
-    height: 22px;
-    padding: 5px;
-    width: 22px;
-  }
-
   @media (prefers-color-scheme: dark) {
     img {
       background-color: var(--google-grey-200);
@@ -78,4 +59,3 @@
   }
 </style>
 <!-- Promo parts are added by JS. -->
-<div id="container"></div>
diff --git a/chrome/browser/resources/new_tab_page/middle_slot_promo.js b/chrome/browser/resources/new_tab_page/middle_slot_promo.js
index e011aaa..7de830c 100644
--- a/chrome/browser/resources/new_tab_page/middle_slot_promo.js
+++ b/chrome/browser/resources/new_tab_page/middle_slot_promo.js
@@ -4,119 +4,28 @@
 
 import 'chrome://resources/cr_elements/shared_vars_css.m.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
 import {BrowserProxy} from './browser_proxy.js';
 import {ImgElement} from './img.js';
 import {PromoBrowserCommandProxy} from './promo_browser_command_proxy.js';
 
-// Element that requests and renders the middle-slot promo. The element is
-// hidden until the promo is rendered, If no promo exists or the promo is empty,
-// the element remains hidden.
-class MiddleSlotPromoElement extends PolymerElement {
-  static get is() {
-    return 'ntp-middle-slot-promo';
+/**
+ * If a promo exists with content and can be shown, an element containing
+ * the rendered promo is returned with an id #container. Otherwise, null is
+ * returned.
+ * @return {!Promise<Element>}
+ */
+export async function renderPromo() {
+  const browserHandler = BrowserProxy.getInstance().handler;
+  const promoBrowserCommandHandler =
+      PromoBrowserCommandProxy.getInstance().handler;
+  const {promo} = await browserHandler.getPromo();
+  if (!promo) {
+    return null;
   }
 
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
-  static get properties() {
-    return {
-      /** @type {boolean} */
-      hidden: {
-        type: Boolean,
-        value: true,
-        reflectToAttribute: true,
-      },
-
-      /** @private */
-      modulesEnabled_: {
-        type: Boolean,
-        value: () => loadTimeData.getBoolean('modulesEnabled'),
-        reflectToAttribute: true,
-      },
-
-      /**
-       * The list of browser commands to run, if any. Used to decide whether the
-       * promo can be shown.
-       * @type {!Array<number>}
-       * @private
-       */
-      /** @private */
-      commandIds_: {
-        type: Object,
-        value: () => [],
-      },
-    };
-  }
-
-  constructor() {
-    super();
-    /** @private {newTabPage.mojom.PageHandlerRemote} */
-    this.pageHandler_ = BrowserProxy.getInstance().handler;
-  }
-
-  /** @override */
-  connectedCallback() {
-    super.connectedCallback();
-    this.pageHandler_.getPromo().then(({promo}) => {
-      if (promo) {
-        promo.middleSlotParts.forEach(({image, link, text}) => {
-          let el;
-          if (image) {
-            el = new ImgElement();
-            el.autoSrc = image.imageUrl.url;
-            if (image.target) {
-              const anchor = this.createAnchor_(image.target);
-              if (anchor) {
-                anchor.appendChild(el);
-                el = anchor;
-              }
-            }
-            el.classList.add('image');
-          } else if (link) {
-            el = this.createAnchor_(link.url);
-          } else if (text) {
-            el = document.createElement('span');
-          }
-          const linkOrText = link || text;
-          if (el && linkOrText) {
-            el.innerText = linkOrText.text;
-            if (linkOrText.color) {
-              el.style.color = linkOrText.color;
-            }
-          }
-          if (el) {
-            this.$.container.appendChild(el);
-          }
-        });
-        this.maybeShowPromo_().then(canShow => {
-          if (canShow) {
-            this.pageHandler_.onPromoRendered(
-                BrowserProxy.getInstance().now(), promo.logUrl || null);
-            this.hidden = false;
-          }
-          this.dispatchEvent(new Event(
-              'ntp-middle-slot-promo-loaded', {bubbles: true, composed: true}));
-        });
-      } else {
-        // Dispatch this event even if there is no promo as Modules wait for the
-        // promo to be loaded before showing.
-        this.dispatchEvent(new Event(
-            'ntp-middle-slot-promo-loaded', {bubbles: true, composed: true}));
-      }
-    });
-  }
-
-  /**
-   * @param {!url.mojom.Url} target
-   * @return {HTMLAnchorElement}
-   * @private
-   */
-  createAnchor_(target) {
+  const commandIds = [];
+  const createAnchor = target => {
     const commandIdMatch = /^command:(\d+)$/.exec(target.url);
     if (!commandIdMatch && !target.url.startsWith('https://')) {
       return null;
@@ -132,38 +41,98 @@
                .includes(commandId)) {
         commandId = promoBrowserCommand.mojom.Command.kUnknownCommand;
       }
-      this.commandIds_.push(commandId);
+      commandIds.push(commandId);
     }
     const onClick = event => {
       if (commandId !== null) {
-        PromoBrowserCommandProxy.getInstance().handler.executeCommand(
-            commandId, {
-              middleButton: event.button === 1,
-              altKey: event.altKey,
-              ctrlKey: event.ctrlKey,
-              metaKey: event.metaKey,
-              shiftKey: event.shiftKey,
-            });
+        promoBrowserCommandHandler.executeCommand(commandId, {
+          middleButton: event.button === 1,
+          altKey: event.altKey,
+          ctrlKey: event.ctrlKey,
+          metaKey: event.metaKey,
+          shiftKey: event.shiftKey,
+        });
       }
-      this.pageHandler_.onPromoLinkClicked();
+      browserHandler.onPromoLinkClicked();
     };
     // 'auxclick' handles the middle mouse button which does not trigger a
     // 'click' event.
     el.addEventListener('auxclick', onClick);
     el.addEventListener('click', onClick);
     return el;
+  };
+
+  let hasContent = false;
+  const container = document.createElement('div');
+  container.id = 'container';
+  promo.middleSlotParts.forEach(({image, link, text}) => {
+    let el;
+    if (image) {
+      el = new ImgElement();
+      el.autoSrc = image.imageUrl.url;
+      if (image.target) {
+        const anchor = createAnchor(image.target);
+        if (anchor) {
+          anchor.appendChild(el);
+          el = anchor;
+        }
+      }
+      el.classList.add('image');
+    } else if (link) {
+      el = createAnchor(link.url);
+    } else if (text) {
+      el = document.createElement('span');
+    }
+    const linkOrText = link || text;
+    if (el && linkOrText) {
+      el.innerText = linkOrText.text;
+      if (linkOrText.color) {
+        el.style.color = linkOrText.color;
+      }
+    }
+    if (el) {
+      hasContent = true;
+      container.appendChild(el);
+    }
+  });
+
+  const canShow =
+      (await Promise.all(commandIds.map(
+           commandId =>
+               promoBrowserCommandHandler.canShowPromoWithCommand(commandId))))
+          .every(({canShow}) => canShow);
+  if (hasContent && canShow) {
+    browserHandler.onPromoRendered(
+        BrowserProxy.getInstance().now(), promo.logUrl || null);
+    return container;
+  }
+  return null;
+}
+
+// Element that requests and renders the middle-slot promo. The element is
+// hidden until the promo is rendered, If no promo exists or the promo is empty,
+// the element remains hidden.
+class MiddleSlotPromoElement extends PolymerElement {
+  static get is() {
+    return 'ntp-middle-slot-promo';
   }
 
-  /**
-   * @return {!Promise<boolean>} Whether or not the promo can be shown by
-   * checking all command IDs seen in the promo.
-   * @private
-   */
-  async maybeShowPromo_() {
-    const {handler} = PromoBrowserCommandProxy.getInstance();
-    const promises = this.commandIds_.map(
-        commandId => handler.canShowPromoWithCommand(commandId));
-    return (await Promise.all(promises)).every(({canShow}) => canShow);
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  /** @override */
+  ready() {
+    super.ready();
+    this.hidden = true;
+    renderPromo().then(container => {
+      if (container) {
+        this.shadowRoot.appendChild(container);
+        this.hidden = false;
+      }
+      this.dispatchEvent(new Event(
+          'ntp-middle-slot-promo-loaded', {bubbles: true, composed: true}));
+    });
   }
 }
 
diff --git a/chrome/browser/resources/pdf/constants.js b/chrome/browser/resources/pdf/constants.js
index 9baa2a5..df87987 100644
--- a/chrome/browser/resources/pdf/constants.js
+++ b/chrome/browser/resources/pdf/constants.js
@@ -19,12 +19,13 @@
 
 /**
  * @typedef {{
- *   title: string,
  *   author: string,
- *   subject: string,
+ *   canSerializeDocument: boolean,
  *   creator: string,
  *   producer: string,
- *   canSerializeDocument: boolean,
+ *   subject: string,
+ *   title: string,
+ *   version: string,
  * }}
  */
 export let DocumentMetadata;
diff --git a/chrome/browser/resources/pdf/elements/viewer-properties-dialog.html b/chrome/browser/resources/pdf/elements/viewer-properties-dialog.html
index cf85812..7279aad 100644
--- a/chrome/browser/resources/pdf/elements/viewer-properties-dialog.html
+++ b/chrome/browser/resources/pdf/elements/viewer-properties-dialog.html
@@ -88,7 +88,9 @@
       </tr>
       <tr>
         <td class="name">$i18n{propertiesPdfVersion}</td>
-        <td class="value" id="pdf-version">-</td>
+        <td class="value" id="pdf-version">
+          [[getOrPlaceholder_(documentMetadata.version)]]
+        </td>
       </tr>
       <tr>
         <td class="name">$i18n{propertiesPageCount}</td>
diff --git a/chrome/browser/resources/pdf/navigator.js b/chrome/browser/resources/pdf/navigator.js
index 5388ffa..1e37b1e 100644
--- a/chrome/browser/resources/pdf/navigator.js
+++ b/chrome/browser/resources/pdf/navigator.js
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {NativeEventTarget as EventTarget} from 'chrome://resources/js/cr/event_target.m.js';
 import {OpenPdfParamsParser} from './open_pdf_params_parser.js';
 import {Viewport} from './viewport.js';
 
@@ -104,14 +103,6 @@
 
     /** @private {!NavigatorDelegate} */
     this.navigatorDelegate_ = navigatorDelegate;
-
-    /** @private {!EventTarget} */
-    this.eventTarget_ = new EventTarget();
-  }
-
-  /** @return {!EventTarget} */
-  getEventTarget() {
-    return this.eventTarget_;
   }
 
   /**
@@ -120,10 +111,11 @@
    * @param {string} urlString The URL to navigate to.
    * @param {!WindowOpenDisposition} disposition The window open
    *     disposition when navigating to the new URL.
+   * @return {!Promise<void>} When navigation has completed (used for testing).
    */
   navigate(urlString, disposition) {
     if (urlString.length === 0) {
-      return;
+      return Promise.resolve();
     }
 
     // If |urlFragment| starts with '#', then it's for the same URL with a
@@ -145,17 +137,19 @@
     try {
       url = new URL(urlString);
     } catch (err) {
-      return;
+      return Promise.reject(err);
     }
 
     if (!this.isValidUrl_(url)) {
-      return;
+      return Promise.resolve();
     }
 
+    let whenDone = Promise.resolve();
+
     switch (disposition) {
       case WindowOpenDisposition.CURRENT_TAB:
-        this.paramsParser_.getViewportFromUrlParams(
-            url.href, this.onViewportReceived_.bind(this));
+        whenDone = this.paramsParser_.getViewportFromUrlParams(url.href).then(
+            this.onViewportReceived_.bind(this));
         break;
       case WindowOpenDisposition.NEW_BACKGROUND_TAB:
         this.navigatorDelegate_.navigateInNewTab(url.href, false);
@@ -169,15 +163,14 @@
       case WindowOpenDisposition.SAVE_TO_DISK:
         // TODO(jaepark): Alt + left clicking a link in PDF should
         // download the link.
-        this.paramsParser_.getViewportFromUrlParams(
-            url.href, this.onViewportReceived_.bind(this));
+        whenDone = this.paramsParser_.getViewportFromUrlParams(url.href).then(
+            this.onViewportReceived_.bind(this));
         break;
       default:
         break;
     }
 
-    // Dispatch events for tests.
-    this.eventTarget_.dispatchEvent(new CustomEvent('navigate-for-testing'));
+    return whenDone;
   }
 
   /**
diff --git a/chrome/browser/resources/pdf/open_pdf_params_parser.js b/chrome/browser/resources/pdf/open_pdf_params_parser.js
index 1828eaa8..7ded80f 100644
--- a/chrome/browser/resources/pdf/open_pdf_params_parser.js
+++ b/chrome/browser/resources/pdf/open_pdf_params_parser.js
@@ -11,11 +11,11 @@
  *   url: (string|undefined),
  *   zoom: (number|undefined),
  *   view: (!FittingType|undefined),
- *   viewPosition: (!Point|undefined),
+ *   viewPosition: (number|undefined),
  *   position: (!Object|undefined),
  * }}
  */
-let OpenPdfParams;
+export let OpenPdfParams;
 
 // Parses the open pdf parameters passed in the url to set initial viewport
 // settings for opening the pdf.
@@ -224,10 +224,9 @@
    * See http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/
    * pdfs/pdf_open_parameters.pdf for details.
    * @param {string} url that needs to be parsed.
-   * @param {function(!OpenPdfParams)} callback function to be called with
-   *     viewport info.
+   * @return {!Promise<!OpenPdfParams>}
    */
-  getViewportFromUrlParams(url, callback) {
+  async getViewportFromUrlParams(url) {
     const params = {};
     params['url'] = url;
 
@@ -254,22 +253,23 @@
     }
 
     if (params.page === undefined && urlParams.has('nameddest')) {
-      this.getNamedDestinationCallback_(
-              /** @type {string} */ (urlParams.get('nameddest')))
-          .then(data => {
-            if (data.pageNumber !== -1) {
-              params.page = data.pageNumber;
-            }
-            if (data.namedDestinationView) {
-              Object.assign(
-                  params,
-                  this.parseNameddestViewParam_(
-                      /** @type {string} */ (data.namedDestinationView)));
-            }
-            callback(params);
-          });
-    } else {
-      callback(params);
+      const data = await this.getNamedDestinationCallback_(
+          /** @type {string} */ (urlParams.get('nameddest')));
+
+      if (data.pageNumber !== -1) {
+        params.page = data.pageNumber;
+      }
+
+      if (data.namedDestinationView) {
+        Object.assign(
+            params,
+            this.parseNameddestViewParam_(
+                /** @type {string} */ (data.namedDestinationView)));
+      }
+
+      return params;
     }
+
+    return Promise.resolve(params);
   }
 }
diff --git a/chrome/browser/resources/pdf/pdf_viewer_base.js b/chrome/browser/resources/pdf/pdf_viewer_base.js
index 068b345..40cfb5e3 100644
--- a/chrome/browser/resources/pdf/pdf_viewer_base.js
+++ b/chrome/browser/resources/pdf/pdf_viewer_base.js
@@ -12,7 +12,7 @@
 import {FittingType, Point} from './constants.js';
 import {ContentController, MessageData, PluginController, PluginControllerEventType} from './controller.js';
 import {PDFMetrics, UserAction} from './metrics.js';
-import {OpenPdfParamsParser} from './open_pdf_params_parser.js';
+import {OpenPdfParams, OpenPdfParamsParser} from './open_pdf_params_parser.js';
 import {LoadState} from './pdf_scripting_api.js';
 import {DocumentDimensionsMessageData, MessageObject} from './pdf_viewer_utils.js';
 import {Viewport} from './viewport.js';
@@ -318,8 +318,8 @@
       if (this.lastViewportPosition) {
         this.viewport_.position = this.lastViewportPosition;
       }
-      this.paramsParser.getViewportFromUrlParams(
-          this.originalUrl, params => this.handleURLParams_(params));
+      this.paramsParser.getViewportFromUrlParams(this.originalUrl)
+          .then(this.handleURLParams_.bind(this));
       this.setLoadState(LoadState.SUCCESS);
       this.sendDocumentLoadedMessage();
       while (this.delayedScriptingMessages_.length > 0) {
@@ -497,7 +497,7 @@
    * Handle open pdf parameters. This function updates the viewport as per
    * the parameters mentioned in the url while opening pdf. The order is
    * important as later actions can override the effects of previous actions.
-   * @param {Object} params The open params passed in the URL.
+   * @param {!OpenPdfParams} params The open params passed in the URL.
    * @private
    */
   handleURLParams_(params) {
diff --git a/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js b/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
index 4942b9f..276bf6a 100644
--- a/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
+++ b/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
@@ -194,6 +194,9 @@
     // </if>
 
     // <if expr="chromeos">
+    /** Opens the diagnostics page. */
+    openDiagnostics() {}
+
     /** Opens the OS help page. */
     openOsHelpPage() {}
 
@@ -308,6 +311,11 @@
 
     // <if expr="chromeos">
     /** @override */
+    openDiagnostics() {
+      chrome.send('openDiagnostics');
+    }
+
+    /** @override */
     openOsHelpPage() {
       chrome.send('openOsHelpPage');
     }
diff --git a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.html b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.html
index a02f118..a5c8652 100644
--- a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.html
@@ -183,6 +183,12 @@
               deep-link-focus-id$="[[Setting.kReportAnIssue]]">
           </cr-link-row>
 </if>
+          <cr-link-row class="hr" id="diagnostics"
+              on-click="onDiagnosticsClick_"
+              hidden$="[[!showDiagnosticsApp_]]"
+              label="$i18n{aboutDiagnostics}" external
+              deep-link-focus-id$="[[Setting.kDiagnostics]]">
+          </cr-link-row>
           <cr-link-row class="hr" id="detailed-build-info-trigger"
               on-click="onDetailedBuildInfoClick_"
               label="$i18n{aboutDetailedBuildInfo}"
diff --git a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js
index 916177b..e2a905b 100644
--- a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js
@@ -114,6 +114,14 @@
           'currentUpdateStatusEvent_, hasCheckedForUpdates_, hasEndOfLife_)',
     },
 
+    /** @private */
+    showDiagnosticsApp_: {
+      type: Boolean,
+      value: function() {
+        return loadTimeData.getBoolean('diagnosticsAppEnabled');
+      }
+    },
+
     /** @private {!Map<string, string>} */
     focusConfig_: {
       type: Object,
@@ -168,6 +176,7 @@
         chromeos.settings.mojom.Setting.kGetHelpWithChromeOs,
         chromeos.settings.mojom.Setting.kReportAnIssue,
         chromeos.settings.mojom.Setting.kTermsOfService,
+        chromeos.settings.mojom.Setting.kDiagnostics,
       ]),
     },
   },
@@ -301,6 +310,13 @@
   },
 
   /** @private */
+  onDiagnosticsClick_() {
+    assert(this.showDiagnosticsApp_);
+    this.aboutBrowserProxy_.openDiagnostics();
+    settings.recordSettingChange(chromeos.settings.mojom.Setting.kDiagnostics);
+  },
+
+  /** @private */
   onRelaunchClick_() {
     settings.recordSettingChange();
     this.lifetimeBrowserProxy_.relaunch();
diff --git a/chrome/browser/resources/settings/site_settings/chooser_exception_list.js b/chrome/browser/resources/settings/site_settings/chooser_exception_list.js
index 89328e6..bbab832 100644
--- a/chrome/browser/resources/settings/site_settings/chooser_exception_list.js
+++ b/chrome/browser/resources/settings/site_settings/chooser_exception_list.js
@@ -197,7 +197,7 @@
 
     if (!this.updateList(
             'chooserExceptions', x => x.displayName, exceptions,
-            true /* uidBasedUpdate */)) {
+            true /* identityBasedUpdate= */)) {
       // The chooser objects have not been changed, so check if their site
       // permissions have changed. The |exceptions| and |this.chooserExceptions|
       // arrays should be the same length.
diff --git a/chrome/browser/resources/signin/profile_picker/BUILD.gn b/chrome/browser/resources/signin/profile_picker/BUILD.gn
index 1cd3987..bed524dd 100644
--- a/chrome/browser/resources/signin/profile_picker/BUILD.gn
+++ b/chrome/browser/resources/signin/profile_picker/BUILD.gn
@@ -213,6 +213,7 @@
     ":profile_card_menu",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
+    "//ui/webui/resources/js:i18n_behavior.m",
   ]
 }
 
diff --git a/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.js b/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.js
index 304dea62..c953358 100644
--- a/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.js
+++ b/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.js
@@ -129,6 +129,9 @@
    * @param {string} profileName
    */
   setProfileName(profilePath, profileName) {}
+
+  /** Records impression of a sign-in promo to metrics. */
+  recordSignInPromoImpression() {}
 }
 
 /** @implements {ManageProfilesBrowserProxy} */
@@ -195,6 +198,11 @@
   setProfileName(profilePath, profileName) {
     chrome.send('setProfileName', [profilePath, profileName]);
   }
+
+  /** @override */
+  recordSignInPromoImpression() {
+    chrome.send('recordSignInPromoImpression');
+  }
 }
 
 addSingletonGetter(ManageProfilesBrowserProxyImpl);
diff --git a/chrome/browser/resources/signin/profile_picker/profile_card.html b/chrome/browser/resources/signin/profile_picker/profile_card.html
index 7add1911..78207f4 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_card.html
+++ b/chrome/browser/resources/signin/profile_picker/profile_card.html
@@ -135,7 +135,7 @@
 
 <div id="profileCardContainer">
   <cr-button on-click="onProfileClick_"
-      aria-label="[[profileState.localProfileName]]">
+      aria-label="[[i18n('profileCardButtonLabel', profileState.localProfileName)]]">
     <div id="avatarContainer">
       <img class="profile-avatar" alt="" src="[[profileState.avatarIcon]]">
       <div id="iconContainer" hidden="[[!profileState.isManaged]]">
diff --git a/chrome/browser/resources/signin/profile_picker/profile_card.js b/chrome/browser/resources/signin/profile_picker/profile_card.js
index 8c2803b7..feba5dd 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_card.js
+++ b/chrome/browser/resources/signin/profile_picker/profile_card.js
@@ -8,7 +8,9 @@
 import './profile_picker_shared_css.js';
 import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
 
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 import {ManageProfilesBrowserProxy, ManageProfilesBrowserProxyImpl, ProfileState} from './manage_profiles_browser_proxy.js';
 
 Polymer({
@@ -16,6 +18,8 @@
 
   _template: html`{__html_template__}`,
 
+  behaviors: [I18nBehavior],
+
   properties: {
     /**  @type {!ProfileState} */
     profileState: {
diff --git a/chrome/browser/resources/signin/profile_picker/profile_creation_flow/profile_type_choice.js b/chrome/browser/resources/signin/profile_picker/profile_creation_flow/profile_type_choice.js
index 708aeb29..aecfe98 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_creation_flow/profile_type_choice.js
+++ b/chrome/browser/resources/signin/profile_picker/profile_creation_flow/profile_type_choice.js
@@ -11,7 +11,7 @@
 import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {AutogeneratedThemeColorInfo} from '../manage_profiles_browser_proxy.js';
+import {AutogeneratedThemeColorInfo, ManageProfilesBrowserProxyImpl} from '../manage_profiles_browser_proxy.js';
 import {navigateToPreviousRoute, navigateToStep, ProfileCreationSteps, Routes} from '../navigation_behavior.js';
 
 Polymer({
@@ -43,6 +43,7 @@
         'load-signin-finished',
         success => this.handleLoadSigninFinished_(success));
     FocusOutlineManager.forDocument(document);
+    ManageProfilesBrowserProxyImpl.getInstance().recordSignInPromoImpression();
   },
 
   /** @private */
diff --git a/chrome/browser/resources/tab_search/BUILD.gn b/chrome/browser/resources/tab_search/BUILD.gn
index a32940b..ab330aa6 100644
--- a/chrome/browser/resources/tab_search/BUILD.gn
+++ b/chrome/browser/resources/tab_search/BUILD.gn
@@ -182,6 +182,7 @@
   deps = [
     "//third_party/polymer/v3_0/components-chromium/iron-selector:iron-selector",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:list_property_update_behavior.m",
   ]
 }
 
diff --git a/chrome/browser/resources/tab_search/app.html b/chrome/browser/resources/tab_search/app.html
index c054c21..78cc37b 100644
--- a/chrome/browser/resources/tab_search/app.html
+++ b/chrome/browser/resources/tab_search/app.html
@@ -58,7 +58,7 @@
     search-result-text="[[searchResultText_]]">
 </tab-search-search-field>
 <div hidden="[[!filteredOpenTabs_.length]]">
-  <infinite-list id="tabsList" items="[[filteredOpenTabs_]]" >
+  <infinite-list id="tabsList" items="[[filteredOpenTabs_]]">
     <template is="dom-repeat">
       <tab-search-item id="[[item.tab.tabId]]" aria-label="[[ariaLabel_(item)]]"
           class="mwb-list-item" data="[[item]]" on-click="onItemClick_"
diff --git a/chrome/browser/resources/tab_search/app.js b/chrome/browser/resources/tab_search/app.js
index 6f154ac1..3d28fb4 100644
--- a/chrome/browser/resources/tab_search/app.js
+++ b/chrome/browser/resources/tab_search/app.js
@@ -40,10 +40,9 @@
         value: '',
       },
 
-      /** @private {?Array<!WindowTabs>} */
+      /** @private {?Array<!TabData>}*/
       openTabs_: {
         type: Array,
-        observer: 'openTabsChanged_',
       },
 
       /** @private {!Array<!TabData>} */
@@ -169,7 +168,7 @@
           'Tabs.TabSearch.WebUI.TabListDataReceived',
           Math.round(Date.now() - getTabsStartTimestamp));
 
-      this.openTabs_ = profileTabs.windows;
+      this.openTabsChanged_(profileTabs.windows);
     });
   }
 
@@ -178,19 +177,13 @@
    * @private
    */
   onTabUpdated_(updatedTab) {
-    const updatedTabId = updatedTab.tabId;
-    const windows = this.openTabs_;
-    if (windows) {
-      for (const window of windows) {
-        const {tabs} = window;
-        for (let i = 0; i < tabs.length; ++i) {
-          // Replace the tab with the same tabId and trigger rerender.
-          if (tabs[i].tabId === updatedTabId) {
-            tabs[i] = updatedTab;
-            this.openTabs_ = windows.concat();
-            return;
-          }
-        }
+    // Replace the tab with the same tabId and trigger rerender.
+    for (let i = 0; i < this.openTabs_.length; ++i) {
+      if (this.openTabs_[i].tab.tabId === updatedTab.tabId) {
+        this.openTabs_[i] =
+            this.tabData_(updatedTab, this.openTabs_[i].inActiveWindow);
+        this.updateFilteredTabs_(this.openTabs_);
+        return;
       }
     }
   }
@@ -200,14 +193,21 @@
    * @private
    */
   onTabsRemoved_(tabIds) {
-    const windows = this.openTabs_;
-    if (windows) {
-      const ids = new Set(tabIds);
-      for (const window of windows) {
-        window.tabs = window.tabs.filter(tab => (!ids.has(tab.tabId)));
-      }
-      this.openTabs_ = windows.concat();
+    if (!this.openTabs_) {
+      return;
     }
+
+    const ids = new Set(tabIds);
+    // Splicing in descending index order to avoid affecting preceding indices
+    // that are to be removed.
+    for (let i = this.openTabs_.length - 1; i >= 0; i--) {
+      if (ids.has(this.openTabs_[i].tab.tabId)) {
+        this.openTabs_.splice(i, 1);
+      }
+    }
+
+    this.filteredOpenTabs_ =
+        this.filteredOpenTabs_.filter(tabData => !ids.has(tabData.tab.tabId));
   }
 
   /**
@@ -304,11 +304,17 @@
   }
 
   /**
-   * @param {!Array<!WindowTabs>} newOpenTabs
+   * @param {!Array<!WindowTabs>} newOpenWindowTabs
    * @private
    */
-  openTabsChanged_(newOpenTabs) {
-    this.updateFilteredTabs_(newOpenTabs);
+  openTabsChanged_(newOpenWindowTabs) {
+    this.openTabs_ = [];
+    newOpenWindowTabs.forEach(({active, tabs}) => {
+      tabs.forEach(tab => {
+        this.openTabs_.push(this.tabData_(tab, active));
+      });
+    });
+    this.updateFilteredTabs_(this.openTabs_);
 
     // If there was no previously selected index, set the first item as
     // selected; else retain the currently selected index. If the list
@@ -415,19 +421,22 @@
   }
 
   /**
-   * @param {!Array<!WindowTabs>} windowTabs
+   * @param {!Tab} tab
+   * @param {boolean} inActiveWindow
+   * @return {!TabData}
    * @private
    */
-  updateFilteredTabs_(windowTabs) {
-    const result = [];
-    windowTabs.forEach(window => {
-      window.tabs.forEach(tab => {
-        const hostname = new URL(tab.url).hostname;
-        const inActiveWindow = window.active;
-        result.push({hostname, inActiveWindow, tab});
-      });
-    });
-    result.sort((a, b) => {
+  tabData_(tab, inActiveWindow) {
+    const hostname = new URL(tab.url).hostname;
+    return /** @type {!TabData} */ ({hostname, inActiveWindow, tab});
+  }
+
+  /**
+   * @param {!Array<!TabData>} tabs
+   * @private
+   */
+  updateFilteredTabs_(tabs) {
+    tabs.sort((a, b) => {
       // Move the active tab to the bottom of the list
       // because it's not likely users want to click on it.
       if (this.moveActiveTabToBottom_) {
@@ -444,8 +453,9 @@
               a.tab.lastActiveTimeTicks.internalValue) :
           0;
     });
+
     this.filteredOpenTabs_ =
-        fuzzySearch(this.searchText_, result, this.fuzzySearchOptions_);
+        fuzzySearch(this.searchText_, tabs, this.fuzzySearchOptions_);
     this.searchResultText_ = this.getA11ySearchResultText_();
   }
 
diff --git a/chrome/browser/resources/tab_search/fuzzy_search.js b/chrome/browser/resources/tab_search/fuzzy_search.js
index 88f08205..b2a6950 100644
--- a/chrome/browser/resources/tab_search/fuzzy_search.js
+++ b/chrome/browser/resources/tab_search/fuzzy_search.js
@@ -11,11 +11,12 @@
  * @param {string} input
  * @param {!Array<!TabData>} records
  * @param {!Object} options
- * @return {!Array<!TabData>}
+ * @return {!Array<!TabData>} A new array of entries satisfying the input. If no
+ *     search input is present, returns a shallow copy of the records.
  */
 export function fuzzySearch(input, records, options) {
   if (input.length === 0) {
-    return records;
+    return [...records];
   }
   // Fuse does not handle exact match searches well. It indiscriminately
   // searches for direct matches that appear anywhere in the string. This
diff --git a/chrome/browser/resources/tab_search/infinite_list.html b/chrome/browser/resources/tab_search/infinite_list.html
index 2778c25bc..55b5dfe2 100644
--- a/chrome/browser/resources/tab_search/infinite_list.html
+++ b/chrome/browser/resources/tab_search/infinite_list.html
@@ -9,7 +9,6 @@
 </style>
 <div id="items">
   <iron-selector id="selector" on-keydown="onKeyDown_"
-      on-iron-items-changed="updateScrollerSize_"
       on-iron-select="onSelectedChanged_" role="listbox"
       selected-class="selected">
     <slot></slot>
diff --git a/chrome/browser/resources/tab_search/infinite_list.js b/chrome/browser/resources/tab_search/infinite_list.js
index 96e232d..38deb66 100644
--- a/chrome/browser/resources/tab_search/infinite_list.js
+++ b/chrome/browser/resources/tab_search/infinite_list.js
@@ -20,6 +20,7 @@
 import 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js';
 
 import {assert, assertInstanceof} from 'chrome://resources/js/assert.m.js';
+import {updateListProperty} from 'chrome://resources/js/list_property_update_behavior.m.js';
 import {listenOnce} from 'chrome://resources/js/util.m.js';
 import {afterNextRender, DomRepeat, html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
@@ -92,9 +93,9 @@
 
   /** @private */
   getDomItems_() {
-    const selectorChildren = this.$.selector.children;
+    const selector = /** @type {!IronSelectorElement} */ (this.$.selector);
     return Array.prototype.slice.call(
-        selectorChildren, 0, selectorChildren.length - 1);
+        selector.children, 0, selector.children.length - 1);
   }
 
   /**
@@ -155,6 +156,7 @@
       if (aboveScrollTopItemCount + this.chunkItemThreshold >
           this.domRepeat_.items.length) {
         this.ensureDomItemsAvailableStartingAt_(aboveScrollTopItemCount);
+        this.updateScrollerSize_();
       }
     }
   }
@@ -189,12 +191,13 @@
    * @private
    */
   domItemAverageHeight_() {
-    if (!this.$.selector.items || this.$.selector.items.length === 0) {
+    const selector = /** @type {!IronSelectorElement} */ (this.$.selector);
+    if (!selector.items || selector.items.length === 0) {
       return 0;
     }
 
-    const domItemCount = this.$.selector.items.length;
-    const lastDomItem = this.$.selector.items[domItemCount - 1];
+    const domItemCount = selector.items.length;
+    const lastDomItem = selector.items[domItemCount - 1];
     return (lastDomItem.offsetTop + lastDomItem.offsetHeight) / domItemCount;
   }
 
@@ -202,17 +205,32 @@
    * Ensures that when the items property changes, only a chunk of the items
    * needed to fill the current scroll position view are added to the DOM, thus
    * improving rendering performance.
+   *
+   * @param {!Array} newItems
+   * @param {!Array} oldItems
    * @private
    */
-  onItemsChanged_() {
-    if (this.domRepeat_ && this.items) {
-      const domItemAvgHeight = this.domItemAverageHeight_();
-      const aboveScrollTopItemCount = domItemAvgHeight !== 0 ?
-          Math.round(this.scrollTop / domItemAvgHeight) :
-          0;
+  onItemsChanged_(newItems, oldItems) {
+    if (!this.domRepeat_) {
+      return;
+    }
 
+    if (!oldItems || oldItems.length === 0) {
       this.domRepeat_.set('items', []);
-      this.ensureDomItemsAvailableStartingAt_(aboveScrollTopItemCount);
+      this.ensureDomItemsAvailableStartingAt_(0);
+      listenOnce(this.$.selector, 'iron-items-changed', () => {
+        this.updateScrollerSize_();
+      });
+
+      return;
+    }
+
+    updateListProperty(
+        this.domRepeat_, 'items', tabData => tabData,
+        newItems.slice(0, this.domRepeat_.items.length),
+        true /* identityBasedUpdate= */);
+
+    if (newItems.length !== oldItems.length) {
       this.updateScrollerSize_();
     }
   }
diff --git a/chrome/browser/resources/tools/optimize_webui.py b/chrome/browser/resources/tools/optimize_webui.py
index 8d9c7da..ddada8e 100755
--- a/chrome/browser/resources/tools/optimize_webui.py
+++ b/chrome/browser/resources/tools/optimize_webui.py
@@ -119,7 +119,8 @@
   # TODO(dbeam): can we make this stricter?
   return url
 
-def _request_list_path(out_path, host):
+def _request_list_path(out_path, host_url):
+  host = host_url[host_url.find('://') + 3:-1]
   return os.path.join(out_path, host + '_requestlist.txt')
 
 # Get a list of all files that were bundled with polymer-bundler and update the
@@ -142,7 +143,7 @@
   # current working directory.
   url_mappings = _URL_MAPPINGS + [
       ('/', os.path.relpath(in_path, _CWD)),
-      ('chrome://%s/' % args.host, os.path.relpath(in_path, _CWD)),
+      (args.host_url, os.path.relpath(in_path, _CWD)),
   ]
 
   deps = [_undo_mapping(url_mappings, u) for u in request_list]
@@ -158,10 +159,8 @@
 # Autogenerate a rollup config file so that we can import the plugin and
 # pass it information about the location of the directories and files to exclude
 # from the bundle.
-def _generate_rollup_config(tmp_out_dir, path_to_plugin, in_path, host,
+def _generate_rollup_config(tmp_out_dir, path_to_plugin, in_path, host_url,
                             excludes, external_paths):
-  scheme_end_index = host.find('://')
-  host_url = 'chrome://%s/' % host if scheme_end_index == -1 else host
   rollup_config_file = os.path.join(tmp_out_dir, 'rollup.config.js')
   config_content = r'''
     import plugin from '{plugin_path}';
@@ -211,7 +210,7 @@
   path_to_plugin = os.path.join(
       os.path.abspath(_HERE_PATH), 'rollup_plugin.js')
   rollup_config_file = _generate_rollup_config(tmp_out_dir, path_to_plugin,
-                                               in_path, args.host, excludes,
+                                               in_path, args.host_url, excludes,
                                                external_paths)
   rollup_args = [os.path.join(in_path, f) for f in args.js_module_in_files]
 
@@ -282,7 +281,7 @@
       [
        '--manifest-out', manifest_out_path,
        '--root', in_path,
-       '--redirect', 'chrome://%s/|%s' % (args.host, in_path + '/'),
+       '--redirect', '%s|%s' % (args.host_url, in_path + '/'),
        '--out-dir', os.path.relpath(tmp_out_dir, _CWD).replace('\\', '/'),
        '--shell', args.html_in_files[0],
       ] + in_html_args)
@@ -325,7 +324,7 @@
 def _optimize(in_folder, args):
   in_path = os.path.normpath(os.path.join(_CWD, in_folder)).replace('\\', '/')
   out_path = os.path.join(_CWD, args.out_folder).replace('\\', '/')
-  manifest_out_path = _request_list_path(out_path, args.host)
+  manifest_out_path = _request_list_path(out_path, args.host_url)
   tmp_out_dir = tempfile.mkdtemp(dir=out_path).replace('\\', '/')
 
   excludes = _BASE_EXCLUDES + [
@@ -333,8 +332,8 @@
     # URL for both the relative URL and chrome:// URL syntax.
     'strings.js',
     'strings.m.js',
-    'chrome://%s/strings.js' % args.host,
-    'chrome://%s/strings.m.js' % args.host,
+    '%s/strings.js' % args.host_url,
+    '%s/strings.m.js' % args.host_url,
   ]
   excludes.extend(args.exclude or [])
   external_paths = args.external_paths or []
@@ -347,7 +346,8 @@
                                  external_paths)
     else:
       # Ensure Polymer 2 and Polymer 3 request lists don't collide.
-      manifest_out_path = _request_list_path(out_path, args.host + '-v2')
+      manifest_out_path = _request_list_path(out_path,
+                                             args.host_url[:-1] + '-v2/')
       pcb_out_paths = [os.path.join(out_path, f) for f in args.html_out_files]
       bundled_paths = _bundle_v2(tmp_out_dir, in_path, out_path,
                                  manifest_out_path, args, excludes)
@@ -397,6 +397,11 @@
   args.depfile = os.path.normpath(args.depfile)
   args.input = os.path.normpath(args.input)
   args.out_folder = os.path.normpath(args.out_folder)
+  scheme_end_index = args.host.find('://')
+  if (scheme_end_index == -1):
+    args.host_url = 'chrome://%s/' % args.host
+  else:
+    args.host_url = args.host
 
   manifest_out_path = _optimize(args.input, args)
 
diff --git a/chrome/browser/resources/tools/optimize_webui_test.py b/chrome/browser/resources/tools/optimize_webui_test.py
index 1015fe6..0ec62373e 100755
--- a/chrome/browser/resources/tools/optimize_webui_test.py
+++ b/chrome/browser/resources/tools/optimize_webui_test.py
@@ -53,7 +53,6 @@
     # TODO(dbeam): make it possible to _run_optimize twice? Is that useful?
     args = input_args + [
       '--depfile', os.path.join(self._out_folder, 'depfile.d'),
-      '--host', 'fake-host',
       '--input', self._tmp_src_dir,
       '--out_folder', self._out_folder,
     ]
@@ -157,6 +156,7 @@
   def testSimpleOptimize(self):
     self._write_files_to_src_dir()
     args = [
+      '--host', 'fake-host',
       '--html_in_files', 'ui.html',
       '--html_out_files', 'fast.html',
       '--js_out_files', 'fast.js',
@@ -172,6 +172,7 @@
   def testV3SimpleOptimize(self):
     self._write_v3_files_to_src_dir()
     args = [
+      '--host', 'fake-host',
       '--js_module_in_files', 'ui.js',
       '--js_out_files', 'ui.rollup.js',
     ]
@@ -185,6 +186,7 @@
     resources_path = os.path.join(
         'gen', 'ui', 'webui', 'resources', 'preprocessed')
     args = [
+      '--host', 'fake-host',
       '--js_module_in_files', 'ui.js',
       '--js_out_files', 'ui.rollup.js',
       '--external_paths', 'chrome://resources|%s' % resources_path,
@@ -215,6 +217,7 @@
 ''')
 
     args = [
+      '--host', 'fake-host',
       '--js_module_in_files', 'ui.js', 'lazy.js',
       '--js_out_files', 'ui.rollup.js', 'lazy.rollup.js', 'shared.rollup.js',
       '--out-manifest', os.path.join(self._out_folder, 'out_manifest.json'),
@@ -259,6 +262,7 @@
     resources_path = os.path.join(
         'gen', 'ui', 'webui', 'resources', 'preprocessed')
     args = [
+      '--host', 'fake-host',
       '--js_module_in_files', 'ui.js',
       '--js_out_files', 'ui.rollup.js',
       '--external_paths',
@@ -284,5 +288,23 @@
                      'external_element_dep.js')),
         depfile_d)
 
+  def testV3SimpleOptimizeExcludes(self):
+    self._write_v3_files_to_src_dir()
+    args = [
+      '--host', 'chrome-extension://myextensionid/',
+      '--js_module_in_files', 'ui.js',
+      '--js_out_files', 'ui.rollup.js',
+      '--exclude', 'element_in_dir/element_in_dir.js',
+    ]
+    self._run_optimize(args)
+
+    output_js = self._read_out_file('ui.rollup.js')
+    self.assertIn('yay', output_js)
+    self.assertNotIn('hello from element_in_dir', output_js)
+    depfile_d = self._read_out_file('depfile.d')
+    self.assertIn('element.js', depfile_d)
+    self.assertNotIn('element_in_dir', depfile_d)
+
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.cc
index e85bf92..cf93e40 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.cc
@@ -53,8 +53,9 @@
   content_size_ = expected_content_size;
   result_ = expected_result;
   username_ = expected_username;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
-      .WillOnce([this](content::BrowserContext* context, base::Value& report,
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
+      .WillOnce([this](content::BrowserContext* context,
+                       bool include_device_info, base::Value& report,
                        base::OnceCallback<void(bool)>& callback) {
         ValidateReport(&report);
         if (!done_closure_.is_null())
@@ -82,8 +83,9 @@
   content_size_ = expected_content_size;
   result_ = expected_result;
   username_ = expected_username;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
-      .WillOnce([this](content::BrowserContext* context, base::Value& report,
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
+      .WillOnce([this](content::BrowserContext* context,
+                       bool include_device_info, base::Value& report,
                        base::OnceCallback<void(bool)>& callback) {
         ValidateReport(&report);
         if (!done_closure_.is_null())
@@ -112,8 +114,9 @@
   content_size_ = expected_content_size;
   result_ = expected_result;
   username_ = expected_username;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
-      .WillOnce([this](content::BrowserContext* context, base::Value& report,
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
+      .WillOnce([this](content::BrowserContext* context,
+                       bool include_device_info, base::Value& report,
                        base::OnceCallback<void(bool)>& callback) {
         ValidateReport(&report);
         if (!done_closure_.is_null())
@@ -144,13 +147,15 @@
   content_size_ = expected_content_size;
   result_ = expected_result;
   username_ = expected_username;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
-      .WillOnce([this](content::BrowserContext* context, base::Value& report,
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
+      .WillOnce([this](content::BrowserContext* context,
+                       bool include_device_info, base::Value& report,
                        base::OnceCallback<void(bool)>& callback) {
         ValidateReport(&report);
       })
       .WillOnce([this, expected_dlp_verdict](
-                    content::BrowserContext* context, base::Value& report,
+                    content::BrowserContext* context, bool include_device_info,
+                    base::Value& report,
                     base::OnceCallback<void(bool)>& callback) {
         event_key_ = SafeBrowsingPrivateEventRouter::kKeySensitiveDataEvent;
         threat_type_ = base::nullopt;
@@ -184,13 +189,15 @@
   result_ = expected_result;
   dlp_verdict_ = expected_dlp_verdict;
   username_ = expected_username;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
-      .WillOnce([this](content::BrowserContext* context, base::Value& report,
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
+      .WillOnce([this](content::BrowserContext* context,
+                       bool include_device_info, base::Value& report,
                        base::OnceCallback<void(bool)>& callback) {
         ValidateReport(&report);
       })
       .WillOnce([this, expected_threat_type](
-                    content::BrowserContext* context, base::Value& report,
+                    content::BrowserContext* context, bool include_device_info,
+                    base::Value& report,
                     base::OnceCallback<void(bool)>& callback) {
         event_key_ = SafeBrowsingPrivateEventRouter::kKeyDangerousDownloadEvent;
         threat_type_ = expected_threat_type;
@@ -221,8 +228,9 @@
   content_size_ = expected_content_size;
   result_ = expected_result;
   username_ = expected_username;
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _))
-      .WillOnce([this](content::BrowserContext* context, base::Value& report,
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _))
+      .WillOnce([this](content::BrowserContext* context,
+                       bool include_device_info, base::Value& report,
                        base::OnceCallback<void(bool)>& callback) {
         ValidateReport(&report);
         if (!done_closure_.is_null())
@@ -335,7 +343,7 @@
 }
 
 void EventReportValidator::ExpectNoReport() {
-  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _)).Times(0);
+  EXPECT_CALL(*client_, UploadSecurityEventReport_(_, _, _, _)).Times(0);
 }
 
 void EventReportValidator::SetDoneClosure(base::RepeatingClosure closure) {
diff --git a/chrome/browser/search_engines/template_url_service_unittest.cc b/chrome/browser/search_engines/template_url_service_unittest.cc
index fd2058df..0a169bf 100644
--- a/chrome/browser/search_engines/template_url_service_unittest.cc
+++ b/chrome/browser/search_engines/template_url_service_unittest.cc
@@ -1732,7 +1732,7 @@
 // Checks that correct priority is applied when resolving conflicts between the
 // omnibox extension, search engine extension and user search engines with same
 // keyword.
-TEST_F(TemplateURLServiceTest, CheckEnginesWithSameKeywords) {
+TEST_F(TemplateURLServiceTest, KeywordConflictNonReplaceableEngines) {
   test_util()->VerifyLoad();
   // TemplateURLData used for user engines.
   std::unique_ptr<TemplateURLData> turl_data =
@@ -1845,7 +1845,7 @@
          "behavior on ApplyDefaultSearchChangeNoMetrics.";
 }
 
-TEST_F(TemplateURLServiceTest, ConflictingReplaceableEnginesShouldOverwrite) {
+TEST_F(TemplateURLServiceTest, ReplaceableEngineUpdateHandlesKeywordConflicts) {
   test_util()->VerifyLoad();
   // Add 2 replaceable user engine with different keywords.
   TemplateURL* user1 =
@@ -1869,6 +1869,33 @@
   EXPECT_EQ(user2, model()->GetTemplateURLForKeyword(ASCIIToUTF16("user2")));
 }
 
+// Verifies that we favor prepopulated engines over other safe_for_autoreplace()
+// engines, even if they are newer. https://crbug.com/1164024
+TEST_F(TemplateURLServiceTest, KeywordConflictFavorsPrepopulatedEngines) {
+  test_util()->VerifyLoad();
+
+  // Add prepopulated engine with prepopulate_id == 42.
+  TemplateURL* prepopulated = model()->Add(CreateKeywordWithDate(
+      model(), "prepopulated", "common_keyword", "http://test1", std::string(),
+      std::string(), std::string(), true, 42, "UTF-8",
+      base::Time::FromTimeT(0)));
+  ASSERT_TRUE(prepopulated);
+  TemplateURLData prepopulated_data = prepopulated->data();
+
+  // Add a newer autogenerated engine with the same keyword.
+  TemplateURL* newer_autogenerated_engine = AddKeywordWithDate(
+      "autogenerated", "common_keyword", "http://test2", std::string(),
+      std::string(), std::string(), true, "UTF-8", base::Time::FromTimeT(20));
+
+  // Verify that the prepopulated engine was added, and the newer autogenerated
+  // engine was discarded. Also check that data has not changed.
+  EXPECT_EQ(nullptr, newer_autogenerated_engine);
+  EXPECT_EQ(prepopulated,
+            model()->GetTemplateURLForKeyword(ASCIIToUTF16("common_keyword")));
+  EXPECT_TRUE(TemplateURL::MatchesData(prepopulated, &prepopulated_data,
+                                       model()->search_terms_data()));
+}
+
 TEST_F(TemplateURLServiceTest, CheckNonreplaceableEnginesKeywordsConflicts) {
   test_util()->VerifyLoad();
 
diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc
index 1a5d8bf3..d56c8c8 100644
--- a/chrome/browser/spellchecker/spellcheck_service.cc
+++ b/chrome/browser/spellchecker/spellcheck_service.cc
@@ -527,18 +527,6 @@
          (!hunspell_dictionaries_.empty() || enable_if_uninitialized);
 }
 
-bool SpellcheckService::LoadExternalDictionary(std::string language,
-                                               std::string locale,
-                                               std::string path,
-                                               DictionaryFormat format) {
-  return false;
-}
-
-bool SpellcheckService::UnloadExternalDictionary(
-    const std::string& /* path */) {
-  return false;
-}
-
 void SpellcheckService::Observe(int type,
                                 const content::NotificationSource& source,
                                 const content::NotificationDetails& details) {
diff --git a/chrome/browser/spellchecker/spellcheck_service.h b/chrome/browser/spellchecker/spellcheck_service.h
index 29ea018..a810ed51 100644
--- a/chrome/browser/spellchecker/spellcheck_service.h
+++ b/chrome/browser/spellchecker/spellcheck_service.h
@@ -62,13 +62,6 @@
     BDICT_CORRUPTED,
   };
 
-  // Dictionary format used for loading an external dictionary.
-  enum DictionaryFormat {
-    DICT_HUNSPELL,
-    DICT_TEXT,
-    DICT_UNKNOWN,
-  };
-
   // A dictionary that can be used for spellcheck.
   struct Dictionary {
     // The shortest unambiguous identifier for a language from
@@ -141,17 +134,6 @@
   // dictionaries available.
   bool IsSpellcheckEnabled() const;
 
-  // Load a dictionary from a given path. Format specifies how the dictionary
-  // is stored. Return value is true if successful.
-  bool LoadExternalDictionary(std::string language,
-                              std::string locale,
-                              std::string path,
-                              DictionaryFormat format);
-
-  // Unload a dictionary. The path is given to identify the dictionary.
-  // Return value is true if successful.
-  bool UnloadExternalDictionary(const std::string& /* path */);
-
   // NotificationProfile implementation.
   void Observe(int type,
                const content::NotificationSource& source,
diff --git a/chrome/browser/ssl/typed_navigation_upgrade_throttle_browsertest.cc b/chrome/browser/ssl/typed_navigation_upgrade_throttle_browsertest.cc
index a145ef2b..025696b 100644
--- a/chrome/browser/ssl/typed_navigation_upgrade_throttle_browsertest.cc
+++ b/chrome/browser/ssl/typed_navigation_upgrade_throttle_browsertest.cc
@@ -48,13 +48,22 @@
 const char* const kSiteWithNetError = "https://site-with-net-error.com";
 const char* const kSiteWithNetErrorOverHttp = "http://site-with-net-error.com";
 
+// Site (likely on an intranet) that contains a non-registerable or
+// non-assignable domain name (eg: a gTLD that has not been assigned by IANA)
+// that therefore is unlikely to support HTTPS.
+const char* const kNonUniqueHostname1 = "http://testpage";
+const char* const kNonUniqueHostname2 = "http://site.test";
+
 const char kNetErrorHistogram[] = "Net.ErrorPageCounts";
 
 enum class NavigationExpectation {
   // Test should expect a successful navigation to HTTPS.
   kExpectHttps,
   // Test should expect a fallback navigation to HTTP.
-  kExpectHttp
+  kExpectHttp,
+  // Test should expect a search query navigation. This happens when the user
+  // enters a non-URL query such as "testpage".
+  kExpectSearch
 };
 
 std::string GetURLWithoutScheme(const GURL& url) {
@@ -142,7 +151,10 @@
         params->url_request.url == GURL(kSiteWithGoodHttps) ||
         params->url_request.url == GURL(kSiteWithBadHttpsOverHttp) ||
         params->url_request.url == GURL(kSiteWithSlowHttpsOverHttp) ||
-        params->url_request.url == GURL(kSiteWithNetErrorOverHttp)) {
+        params->url_request.url == GURL(kSiteWithNetErrorOverHttp) ||
+        params->url_request.url == GURL(kNonUniqueHostname1) ||
+        params->url_request.url == GURL(kNonUniqueHostname2) ||
+        params->url_request.url == GURL("http://127.0.0.1")) {
       std::string headers =
           "HTTP/1.1 200 OK\nContent-Type: text/html; charset=utf-8\n";
       std::string body = "<html><title>Success</title>Hello world</html>";
@@ -182,43 +194,78 @@
     omnibox()->OnAfterPossibleChange(true);
   }
 
+  // Type |hostname| in the URL bar and hit enter. The navigation shouldn't be
+  // upgraded to HTTPS. Expect a search query to be issued if
+  // |expect_search_query| is true. Otherwise, the final URL will be an HTTP
+  // URL.
+  void TypeUrlAndExceptNoUpgrade(const std::string& hostname,
+                                 bool expect_search_query) {
+    base::HistogramTester histograms;
+    TypeUrlAndCheckNavigation(hostname, histograms,
+                              expect_search_query
+                                  ? NavigationExpectation::kExpectSearch
+                                  : NavigationExpectation::kExpectHttp,
+                              1);
+    histograms.ExpectTotalCount(TypedNavigationUpgradeThrottle::kHistogramName,
+                                0);
+  }
+
+  // Type |hostname| in the URL bar and hit enter. The navigation should
+  // initially be upgraded to HTTPS but then fall back to HTTP because the HTTPS
+  // URL wasn't available (e.g. had an SSL or net error).
+  void TypeUrlAndExpectHttpFallback(const std::string& hostname,
+                                    const base::HistogramTester& histograms) {
+    // There should be two navigations: One for the initial HTTPS
+    // navigation (which will be cancelled because of the timeout, or SSL or net
+    // errors) and one for the fallback HTTP navigation (which will succeed).
+    TypeUrlAndCheckNavigation(hostname, histograms,
+                              NavigationExpectation::kExpectHttp, 2);
+  }
+
+  // Type |hostname| in the URL bar and hit enter. The navigation should
+  // be upgraded to HTTPS and the HTTPS URL should successfully load.
+  void TypeUrlAndExpectHttps(const std::string& hostname,
+                             const base::HistogramTester& histograms) {
+    TypeUrlAndCheckNavigation(hostname, histograms,
+                              NavigationExpectation::kExpectHttps, 1);
+  }
+
   void TypeUrlAndCheckNavigation(const std::string& hostname,
                                  const base::HistogramTester& histograms,
-                                 NavigationExpectation expectation) {
+                                 NavigationExpectation expectation,
+                                 size_t num_expected_navigations) {
     const GURL http_url(std::string("http://") + hostname);
     const GURL https_url(std::string("https://") + hostname);
     SetOmniboxText(hostname);
+    PressEnterAndWaitForNavigations(num_expected_navigations);
 
-    // Wait for navigations.
-    // - If the expectation is to successfully load the HTTPS URL, there should
-    // be a single navigation.
-    // - Otherwise, there should be two navigations: One for the initial HTTPS
-    // navigation (which will be cancelled because of the timeout, or SSL or net
-    // errors) and one for the fallback HTTP navigation (which will succeed).
-    PressEnterAndWaitForNavigations(
-        expectation == NavigationExpectation::kExpectHttps ? 1 : 2);
-
+    ui_test_utils::HistoryEnumerator enumerator(browser()->profile());
     content::WebContents* contents =
         browser()->tab_strip_model()->GetActiveWebContents();
-    const GURL expected_url = expectation == NavigationExpectation::kExpectHttps
-                                  ? https_url
-                                  : http_url;
-    EXPECT_EQ(expected_url, contents->GetLastCommittedURL());
+    if (expectation != NavigationExpectation::kExpectSearch) {
+      const GURL expected_url =
+          expectation == NavigationExpectation::kExpectHttps ? https_url
+                                                             : http_url;
+      EXPECT_EQ(expected_url, contents->GetLastCommittedURL());
+
+      // Should have either the HTTP or the HTTPS URL in history, but not both.
+      if (expectation == NavigationExpectation::kExpectHttp) {
+        EXPECT_TRUE(base::Contains(enumerator.urls(), http_url));
+        EXPECT_FALSE(base::Contains(enumerator.urls(), https_url));
+      } else {
+        EXPECT_TRUE(base::Contains(enumerator.urls(), https_url));
+        EXPECT_FALSE(base::Contains(enumerator.urls(), http_url));
+      }
+    } else {
+      // The user entered a search query.
+      EXPECT_EQ("www.google.com", contents->GetLastCommittedURL().host());
+      EXPECT_FALSE(base::Contains(enumerator.urls(), https_url));
+    }
 
     // Should never hit an error page.
     histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(),
                                 0);
     histograms.ExpectTotalCount(kNetErrorHistogram, 0);
-
-    // Should have either the HTTP or the HTTPS URL in history, but not both.
-    ui_test_utils::HistoryEnumerator enumerator(browser()->profile());
-    if (expectation == NavigationExpectation::kExpectHttp) {
-      EXPECT_TRUE(base::Contains(enumerator.urls(), http_url));
-      EXPECT_FALSE(base::Contains(enumerator.urls(), https_url));
-    } else {
-      EXPECT_TRUE(base::Contains(enumerator.urls(), https_url));
-      EXPECT_FALSE(base::Contains(enumerator.urls(), http_url));
-    }
   }
 
   void PressEnterAndWaitForNavigations(size_t num_navigations) {
@@ -328,21 +375,50 @@
     return;
   }
   base::HistogramTester histograms;
-  const GURL url(kSiteWithHttp);
+  const GURL http_url(kSiteWithHttp);
 
   // Type "test-site.com".
-  SetOmniboxText(GetURLWithoutScheme(url));
+  SetOmniboxText(GetURLWithoutScheme(http_url));
   PressEnterAndWaitForNavigations(1);
 
   content::WebContents* contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_EQ(url, contents->GetLastCommittedURL());
+  EXPECT_EQ(http_url, contents->GetLastCommittedURL());
   EXPECT_FALSE(chrome_browser_interstitials::IsShowingInterstitial(contents));
 
   histograms.ExpectTotalCount(TypedNavigationUpgradeThrottle::kHistogramName,
                               0);
 }
 
+// Test the case when the user types a search keyword. The keyword may or may
+// not be a non-unique hostname. The navigation should always result in a
+// search and we should never upgrade it to https.
+IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
+                       SearchQuery_ShouldNotUpgrade) {
+  TypeUrlAndExceptNoUpgrade("testpage", /*expect_search_query=*/true);
+}
+
+// Same as SearchQuery_ShouldNotUpgrade but with two words. This is a definite
+// search query, and can never be a hostname.
+IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
+                       SearchQuery_TwoWords_ShouldNotUpgrade) {
+  TypeUrlAndExceptNoUpgrade("test page", /*expect_search_query=*/true);
+}
+
+// Test the case when the user types a non-unique hostname. We shouldn't upgrade
+// it to https.
+IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
+                       NonUniqueHostnameTypedWithoutScheme_ShouldNotUpgrade) {
+  TypeUrlAndExceptNoUpgrade("site.test", /*expect_search_query=*/false);
+}
+
+// Test the case when the user types an IP address. We shouldn't upgrade it to
+// https.
+IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
+                       IPAddressTypedWithoutScheme_ShouldNotUpgrade) {
+  TypeUrlAndExceptNoUpgrade("127.0.0.1", /*expect_search_query=*/false);
+}
+
 // If the feature is enabled, typing a URL in the omnibox without a scheme
 // should load the HTTPS version.
 IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
@@ -356,8 +432,7 @@
 
   // Type "site-with-good-https.com".
   const GURL url(kSiteWithGoodHttps);
-  TypeUrlAndCheckNavigation(url.host(), histograms,
-                            NavigationExpectation::kExpectHttps);
+  TypeUrlAndExpectHttps(url.host(), histograms);
 
   histograms.ExpectTotalCount(TypedNavigationUpgradeThrottle::kHistogramName,
                               2);
@@ -375,8 +450,7 @@
   // without going through the upgrade.
   // Type "site-with-good-https.com".
   SetOmniboxText(url.host());
-  TypeUrlAndCheckNavigation(url.host(), histograms,
-                            NavigationExpectation::kExpectHttps);
+  TypeUrlAndExpectHttps(url.host(), histograms);
 
   // Since the throttle wasn't involved in the second navigation, histogram
   // values shouldn't change.
@@ -407,8 +481,7 @@
 
   // Type "site-with-bad-https.com".
   const GURL url(kSiteWithBadHttps);
-  TypeUrlAndCheckNavigation(url.host(), histograms,
-                            NavigationExpectation::kExpectHttp);
+  TypeUrlAndExpectHttpFallback(url.host(), histograms);
 
   histograms.ExpectTotalCount(TypedNavigationUpgradeThrottle::kHistogramName,
                               2);
@@ -436,10 +509,8 @@
   }
   base::HistogramTester histograms;
   // Type "site-with-net-error.com".
-  const GURL https_url(kSiteWithNetError);
   const GURL http_url(kSiteWithNetErrorOverHttp);
-  TypeUrlAndCheckNavigation(http_url.host(), histograms,
-                            NavigationExpectation::kExpectHttp);
+  TypeUrlAndExpectHttpFallback(http_url.host(), histograms);
 
   histograms.ExpectTotalCount(TypedNavigationUpgradeThrottle::kHistogramName,
                               2);
@@ -453,10 +524,6 @@
       TypedNavigationUpgradeThrottle::kHistogramName,
       TypedNavigationUpgradeThrottle::Event::kHttpsLoadTimedOut, 0);
 
-  ui_test_utils::HistoryEnumerator enumerator(browser()->profile());
-  EXPECT_TRUE(base::Contains(enumerator.urls(), http_url));
-  EXPECT_FALSE(base::Contains(enumerator.urls(), https_url));
-
   // TODO(meacer): Try again and check that the histogram counts doubled. Doing
   // that currently fails on lacros because this time the navigation never gets
   // upgraded (probably because of an issue in the autocomplete logic).
@@ -488,8 +555,7 @@
 
   // Type "site-with-slow-https.com".
   const GURL url(kSiteWithSlowHttps);
-  TypeUrlAndCheckNavigation(url.host(), histograms,
-                            NavigationExpectation::kExpectHttp);
+  TypeUrlAndExpectHttpFallback(url.host(), histograms);
 
   histograms.ExpectTotalCount(TypedNavigationUpgradeThrottle::kHistogramName,
                               2);
diff --git a/chrome/browser/supervised_user/child_accounts/child_account_service.cc b/chrome/browser/supervised_user/child_accounts/child_account_service.cc
index 38d4f1c9..09fdbdd 100644
--- a/chrome/browser/supervised_user/child_accounts/child_account_service.cc
+++ b/chrome/browser/supervised_user/child_accounts/child_account_service.cc
@@ -161,7 +161,7 @@
         SupervisedUserSettingsServiceFactory::GetForKey(
             profile_->GetProfileKey());
 
-    // In contrast to legacy SUs, child account SUs must sign in.
+    // In contrast to deprecated legacy SUs, child account SUs must sign in.
     settings_service->SetLocalSetting(supervised_users::kSigninAllowed,
                                       std::make_unique<base::Value>(true));
 
@@ -341,9 +341,9 @@
   user_manager::User* user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
   if (user) {
-    // Note that supervised user is allowed to change type due to legacy
-    // initialization.
-    if (user->GetType() != user_manager::USER_TYPE_SUPERVISED) {
+    // Note that deprecated legacy supervised users are allowed to change type
+    // due to legacy initialization.
+    if (user->GetType() != user_manager::USER_TYPE_SUPERVISED_DEPRECATED) {
       if (is_child != (user->GetType() == user_manager::USER_TYPE_CHILD))
         LOG(FATAL) << "User child flag has changed: " << is_child;
     }
diff --git a/chrome/browser/tab_contents/navigation_metrics_recorder.cc b/chrome/browser/tab_contents/navigation_metrics_recorder.cc
index 6817aa91..a7c4b05 100644
--- a/chrome/browser/tab_contents/navigation_metrics_recorder.cc
+++ b/chrome/browser/tab_contents/navigation_metrics_recorder.cc
@@ -6,11 +6,11 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "components/navigation_metrics/navigation_metrics.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_entry.h"
diff --git a/chrome/browser/tab_contents/navigation_metrics_recorder_browsertest.cc b/chrome/browser/tab_contents/navigation_metrics_recorder_browsertest.cc
index 637561f..5f1e747 100644
--- a/chrome/browser/tab_contents/navigation_metrics_recorder_browsertest.cc
+++ b/chrome/browser/tab_contents/navigation_metrics_recorder_browsertest.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "base/test/metrics/histogram_tester.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/tab_contents/navigation_metrics_recorder.h"
 #include "chrome/browser/ui/browser.h"
@@ -13,6 +12,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/navigation_metrics/navigation_metrics.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/tflite_experiment/tflite_experiment_keyed_service_browsertest.cc b/chrome/browser/tflite_experiment/tflite_experiment_keyed_service_browsertest.cc
index 3e8461ba..d06f7d0 100644
--- a/chrome/browser/tflite_experiment/tflite_experiment_keyed_service_browsertest.cc
+++ b/chrome/browser/tflite_experiment/tflite_experiment_keyed_service_browsertest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/tflite_experiment/tflite_experiment_keyed_service.h"
 
 #include "base/command_line.h"
+#include "base/files/file_util.h"
 #include "base/json/json_reader.h"
 #include "base/path_service.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
@@ -110,7 +111,8 @@
 
     // Set TFLite model path.
     base::PathService::Get(chrome::DIR_TEST_DATA, &g_test_data_directory);
-    g_test_data_directory = g_test_data_directory.Append(kTFLiteModelName);
+    g_test_data_directory =
+        g_test_data_directory.Append(FILE_PATH_LITERAL(kTFLiteModelName));
     cmd->AppendSwitchASCII(tflite_experiment::switches::kTFLiteModelPath,
                            g_test_data_directory.value());
 
diff --git a/chrome/browser/tflite_experiment/tflite_experiment_observer.cc b/chrome/browser/tflite_experiment/tflite_experiment_observer.cc
index 8aa7aef9..c556759 100644
--- a/chrome/browser/tflite_experiment/tflite_experiment_observer.cc
+++ b/chrome/browser/tflite_experiment/tflite_experiment_observer.cc
@@ -4,12 +4,15 @@
 
 #include "chrome/browser/tflite_experiment/tflite_experiment_observer.h"
 
+#include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/json/json_writer.h"
 #include "base/metrics/histogram_macros_local.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/task/task_traits.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/tflite_experiment/tflite_experiment_keyed_service.h"
 #include "chrome/browser/tflite_experiment/tflite_experiment_keyed_service_factory.h"
@@ -124,7 +127,11 @@
                                    const std::string& data) {
   if (!log_path)
     return;
+#if defined(OS_WIN)
+  base::FilePath log_file = base::FilePath(base::UTF8ToWide(log_path.value()));
+#else
   base::FilePath log_file = base::FilePath(log_path.value());
+#endif
   base::AppendToFile(log_file, data.c_str(), data.size());
 }
 
@@ -133,7 +140,11 @@
     base::Optional<std::string> log_path) {
   if (!log_path)
     return;
+#if defined(OS_WIN)
+  base::FilePath log_file = base::FilePath(base::UTF8ToWide(log_path.value()));
+#else
   base::FilePath log_file = base::FilePath(log_path.value());
+#endif
   base::WriteFile(log_file, "", 0);
 }
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index b9be6cf..3607a31 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2719,6 +2719,7 @@
       "//components/services/app_service/public/cpp:instance_update",
       "//components/session_manager/core",
       "//components/user_manager",
+      "//extensions/browser/api/vpn_provider",
       "//google_apis/drive",
       "//media/capture:capture_lib",
       "//mojo/public/js:resources_grit",
diff --git a/chrome/browser/ui/ash/capture_mode_browsertest.cc b/chrome/browser/ui/ash/capture_mode_browsertest.cc
new file mode 100644
index 0000000..78fcb9d
--- /dev/null
+++ b/chrome/browser/ui/ash/capture_mode_browsertest.cc
@@ -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.
+
+#include "ash/public/cpp/ash_features.h"
+#include "ash/public/cpp/capture_mode_test_api.h"
+#include "ash/public/cpp/test/shell_test_api.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/test/browser_test.h"
+
+class CaptureModeBrowserTest : public InProcessBrowserTest {
+ public:
+  CaptureModeBrowserTest() = default;
+  CaptureModeBrowserTest(const CaptureModeBrowserTest&) = delete;
+  CaptureModeBrowserTest& operator=(const CaptureModeBrowserTest&) = delete;
+  ~CaptureModeBrowserTest() override = default;
+
+  // InProcessBrowserTest:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(ash::features::kCaptureMode);
+    InProcessBrowserTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(CaptureModeBrowserTest, ContextMenuStaysOpen) {
+  ash::ShellTestApi shell_test_api;
+  shell_test_api.ShowContextMenu();
+  ASSERT_TRUE(shell_test_api.IsContextMenuShown());
+
+  ash::CaptureModeTestApi().StartForWindow(/*for_video=*/false);
+  EXPECT_TRUE(shell_test_api.IsContextMenuShown());
+}
diff --git a/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc b/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc
index 73d8d6a..1794734 100644
--- a/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc
@@ -173,3 +173,7 @@
     mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) {
   content::GetAudioService().BindStreamFactory(std::move(receiver));
 }
+
+void ChromeCaptureModeDelegate::OnSessionStateChanged(bool started) {
+  is_session_active_ = started;
+}
diff --git a/chrome/browser/ui/ash/chrome_capture_mode_delegate.h b/chrome/browser/ui/ash/chrome_capture_mode_delegate.h
index c550d6d..1be6af9 100644
--- a/chrome/browser/ui/ash/chrome_capture_mode_delegate.h
+++ b/chrome/browser/ui/ash/chrome_capture_mode_delegate.h
@@ -20,6 +20,8 @@
 
   static ChromeCaptureModeDelegate* Get();
 
+  bool is_session_active() const { return is_session_active_; }
+
   // Sets |is_screen_capture_locked_| to the given |locked|, and interrupts any
   // on going video capture.
   void SetIsScreenCaptureLocked(bool locked);
@@ -47,6 +49,7 @@
       override;
   void BindAudioStreamFactory(
       mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) override;
+  void OnSessionStateChanged(bool started) override;
 
  private:
   // Used to temporarily disable capture mode in certain cases for which neither
@@ -60,6 +63,9 @@
   // locked.
   // This is only non-null during recording.
   base::OnceClosure interrupt_video_recording_callback_;
+
+  // True when a capture mode session is currently active.
+  bool is_session_active_ = false;
 };
 
 #endif  // CHROME_BROWSER_UI_ASH_CHROME_CAPTURE_MODE_DELEGATE_H_
diff --git a/chrome/browser/ui/ash/chrome_launcher_prefs.cc b/chrome/browser/ui/ash/chrome_launcher_prefs.cc
index b0848b3f..9eb27d15 100644
--- a/chrome/browser/ui/ash/chrome_launcher_prefs.cc
+++ b/chrome/browser/ui/ash/chrome_launcher_prefs.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/web_app_id.h"
+#include "chrome/browser/web_applications/components/web_app_id_constants.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/pref_names.h"
@@ -49,24 +50,62 @@
 
 // Chrome is pinned explicitly.
 const char* kDefaultPinnedApps[] = {
-    extension_misc::kFilesManagerAppId, extension_misc::kGmailAppId,
-    extension_misc::kGoogleDocAppId, extension_misc::kYoutubeAppId,
-    arc::kPlayStoreAppId};
+    extension_misc::kFilesManagerAppId,
+
+    extension_misc::kGmailAppId,
+    web_app::kGmailAppId,
+
+    extension_misc::kGoogleDocAppId,
+    web_app::kGoogleDocsAppId,
+
+    extension_misc::kYoutubeAppId,
+    web_app::kYoutubeAppId,
+
+    arc::kPlayStoreAppId,
+};
 
 const char* kDefaultPinnedApps7Apps[] = {
-    extension_misc::kFilesManagerAppId, extension_misc::kGmailAppId,
-    extension_misc::kGoogleDocAppId,    extension_misc::kGooglePhotosAppId,
-    extension_misc::kYoutubeAppId,      arc::kPlayStoreAppId};
+    extension_misc::kFilesManagerAppId,
 
-const char* kDefaultPinnedApps10Apps[] = {extension_misc::kFilesManagerAppId,
-                                          extension_misc::kGmailAppId,
-                                          extension_misc::kCalendarAppId,
-                                          extension_misc::kGoogleDocAppId,
-                                          extension_misc::kGoogleSheetsAppId,
-                                          extension_misc::kGoogleSlidesAppId,
-                                          extension_misc::kCameraAppId,
-                                          extension_misc::kGooglePhotosAppId,
-                                          arc::kPlayStoreAppId};
+    extension_misc::kGmailAppId,
+    web_app::kGmailAppId,
+
+    extension_misc::kGoogleDocAppId,
+    web_app::kGoogleDocsAppId,
+
+    extension_misc::kGooglePhotosAppId,
+
+    extension_misc::kYoutubeAppId,
+    web_app::kYoutubeAppId,
+
+    arc::kPlayStoreAppId,
+};
+
+const char* kDefaultPinnedApps10Apps[] = {
+    extension_misc::kFilesManagerAppId,
+
+    extension_misc::kGmailAppId,
+    web_app::kGmailAppId,
+
+    extension_misc::kCalendarAppId,
+    web_app::kGoogleCalendarAppId,
+
+    extension_misc::kGoogleDocAppId,
+    web_app::kGoogleDocsAppId,
+
+    extension_misc::kGoogleSheetsAppId,
+    web_app::kGoogleSheetsAppId,
+
+    extension_misc::kGoogleSlidesAppId,
+    web_app::kGoogleSlidesAppId,
+
+    extension_misc::kCameraAppId,
+    web_app::kCameraAppId,
+
+    extension_misc::kGooglePhotosAppId,
+
+    arc::kPlayStoreAppId,
+};
 
 const char* kTabletFormFactorDefaultPinnedApps[] = {
     extension_misc::kFilesManagerAppId, arc::kGmailAppId,
diff --git a/chrome/browser/ui/ash/clipboard_history_browsertest.cc b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
index a6bdf78..02385390 100644
--- a/chrome/browser/ui/ash/clipboard_history_browsertest.cc
+++ b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
@@ -158,8 +158,15 @@
     run_loop.Run();
   }
 
-  void ShowContextMenuViaAccelerator() {
+  void ShowContextMenuViaAccelerator(bool wait_for_selection) {
     PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN);
+    if (!wait_for_selection)
+      return;
+
+    base::RunLoop run_loop;
+    GetClipboardHistoryController()
+        ->set_initial_item_selected_callback_for_test(run_loop.QuitClosure());
+    run_loop.Run();
   }
 
   const views::MenuItemView* GetMenuItemViewForIndex(int index) const {
@@ -268,7 +275,7 @@
   SetClipboardText("B");
   SetClipboardText("C");
 
-  ShowContextMenuViaAccelerator();
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
   ASSERT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
   ASSERT_EQ(3, GetContextMenu()->GetMenuItemsCount());
 
@@ -319,7 +326,7 @@
 
   SetClipboardText("A");
   SetClipboardText("B");
-  ShowContextMenuViaAccelerator();
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
 
   // Verify the default state right after the menu shows.
   ASSERT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
@@ -393,7 +400,7 @@
   LoginUser(account_id1_);
 
   SetClipboardText("A");
-  ShowContextMenuViaAccelerator();
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
 
   // Verify the default state right after the menu shows.
   ASSERT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
@@ -427,7 +434,7 @@
 
   // No clipboard data. So the clipboard history menu should not show.
   ASSERT_TRUE(GetClipboardItems().empty());
-  ShowContextMenuViaAccelerator();
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/false);
   EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
 
   SetClipboardText("test");
@@ -435,7 +442,7 @@
   const gfx::Point mouse_location =
       ash::Shell::Get()->GetPrimaryRootWindow()->bounds().CenterPoint();
   GetEventGenerator()->MoveMouseTo(mouse_location);
-  ShowContextMenuViaAccelerator();
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
 
   // Verifies that the menu is anchored at the cursor's location.
   ASSERT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
@@ -459,7 +466,7 @@
   SetClipboardText("C");
 
   // Show the menu.
-  ShowContextMenuViaAccelerator();
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
   ASSERT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
   ASSERT_EQ(3, GetContextMenu()->GetMenuItemsCount());
 
@@ -485,7 +492,7 @@
 
   // Trigger the accelerator of opening the clipboard history menu. No menu
   // shows because of the empty history data.
-  ShowContextMenuViaAccelerator();
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/false);
   EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
 }
 
@@ -637,7 +644,7 @@
                        VerifyResponseToGestures) {
   SetClipboardText("A");
   SetClipboardText("B");
-  ShowContextMenuViaAccelerator();
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
   ASSERT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
 
   // Tap at the second menu item view. Verify that "A" is pasted.
@@ -655,7 +662,7 @@
                        DeleteButtonShowAfterLongPress) {
   SetClipboardText("A");
   SetClipboardText("B");
-  ShowContextMenuViaAccelerator();
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
   ASSERT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
 
   ash::ClipboardHistoryItemView* second_item_view =
@@ -690,9 +697,7 @@
   SetClipboardText("B");
   SetClipboardText("C");
 
-  // Verify we can paste the first history item via the COMMAND+V shortcut.
-  PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN);
-
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
   EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
   histogram_tester.ExpectTotalCount(
       "Ash.ClipboardHistory.ContextMenu.DisplayFormatShown", 3);
@@ -707,10 +712,10 @@
   textfield_->SetText(base::string16());
   EXPECT_TRUE(textfield_->GetText().empty());
 
-  // Verify we can paste the first history item via the COMMAND+V shortcut.
-  PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN);
-
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
   EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
+
+  // Verify we can paste the first history item via the COMMAND+V shortcut.
   PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN);
 
   EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
@@ -719,9 +724,7 @@
   textfield_->SetText(base::string16());
   EXPECT_TRUE(textfield_->GetText().empty());
 
-  // Verify we can paste the first history item via the COMMAND+V shortcut.
-  PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN);
-
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
   EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
 
   PressAndRelease(ui::KeyboardCode::VKEY_DOWN);
@@ -735,9 +738,7 @@
 
   EXPECT_TRUE(textfield_->GetText().empty());
 
-  // Verify we can paste the last history item via the COMMAND+V shortcut.
-  PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN);
-
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
   EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
 
   PressAndRelease(ui::KeyboardCode::VKEY_DOWN);
@@ -757,8 +758,7 @@
 
   // Verify we can traverse clipboard history and paste the first history item
   // while holding down the COMMAND key.
-  Press(ui::KeyboardCode::VKEY_COMMAND);
-  PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN);
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
   EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
   PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN);
   EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
@@ -770,8 +770,7 @@
 
   // Verify we can traverse clipboard history and paste the last history item
   // while holding down the COMMAND key.
-  Press(ui::KeyboardCode::VKEY_COMMAND);
-  PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN);
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
   EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
   PressAndRelease(ui::KeyboardCode::VKEY_DOWN, ui::EF_COMMAND_DOWN);
   PressAndRelease(ui::KeyboardCode::VKEY_DOWN, ui::EF_COMMAND_DOWN);
@@ -854,7 +853,7 @@
   SetClipboardTextWithInaccessibleSrc("B");
   EXPECT_TRUE(VerifyClipboardTextData({"B", "A"}));
 
-  ShowContextMenuViaAccelerator();
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
   EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
 
   // Verify that the text is pasted into `textfield_` after the mouse click at
@@ -874,7 +873,7 @@
   // Re-show the multipaste menu since the menu is closed after the previous
   // mouse click.
   ASSERT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
-  ShowContextMenuViaAccelerator();
+  ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
 
   // Move mouse to `inaccessible_menu_item_view` then click the left button.
   const views::MenuItemView* inaccessible_menu_item_view =
diff --git a/chrome/browser/ui/ash/network/enrollment_dialog_view.cc b/chrome/browser/ui/ash/network/enrollment_dialog_view.cc
index f1865675..1840990f 100644
--- a/chrome/browser/ui/ash/network/enrollment_dialog_view.cc
+++ b/chrome/browser/ui/ash/network/enrollment_dialog_view.cc
@@ -262,7 +262,7 @@
     case LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT:
     case LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED:
       return false;
-    case LoginState::LOGGED_IN_USER_SUPERVISED:
+    case LoginState::LOGGED_IN_USER_SUPERVISED_DEPRECATED:
       return true;
     case LoginState::LOGGED_IN_USER_KIOSK_APP:
       return false;
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index 333bfb6c..5246dbb 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -473,6 +473,14 @@
   chromeos::scanning::RecordScanAppEntryPoint(entry_point);
 }
 
+void ShowDiagnosticsApp(Profile* profile) {
+  // TODO(joonbug): Add entry point tracking
+  DCHECK(base::FeatureList::IsEnabled(chromeos::features::kDiagnosticsApp));
+
+  LaunchSystemWebApp(profile, web_app::SystemAppType::DIAGNOSTICS,
+                     GURL(chrome::kChromeUIDiagnosticsAppURL));
+}
+
 GURL GetOSSettingsUrl(const std::string& sub_page) {
   DCHECK(sub_page.empty() || chromeos::settings::IsOSSettingsSubPage(sub_page))
       << sub_page;
diff --git a/chrome/browser/ui/chrome_pages.h b/chrome/browser/ui/chrome_pages.h
index 61bc833..16d3f1d 100644
--- a/chrome/browser/ui/chrome_pages.h
+++ b/chrome/browser/ui/chrome_pages.h
@@ -164,6 +164,8 @@
 
 void ShowScanningApp(Profile* profile,
                      chromeos::scanning::ScanAppEntryPoint entry_point);
+
+void ShowDiagnosticsApp(Profile* profile);
 #endif
 
 #if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 6df56ea..1a711416 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -25,7 +25,6 @@
 #include "chrome/browser/content_settings/sound_content_setting_observer.h"
 #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_tab_helper.h"
 #include "chrome/browser/engagement/site_engagement_helper.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/external_protocol/external_protocol_observer.h"
 #include "chrome/browser/favicon/favicon_utils.h"
 #include "chrome/browser/history/history_tab_helper.h"
@@ -109,6 +108,7 @@
 #include "components/performance_manager/public/performance_manager.h"
 #include "components/permissions/features.h"
 #include "components/permissions/permission_request_manager.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/sync/engine/sync_engine_switches.h"
 #include "components/tracing/common/tracing_switches.h"
 #include "components/ukm/content/source_url_recorder.h"
diff --git a/chrome/browser/ui/thumbnails/thumbnail_image.cc b/chrome/browser/ui/thumbnails/thumbnail_image.cc
index 32730e140..171f1df 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_image.cc
+++ b/chrome/browser/ui/thumbnails/thumbnail_image.cc
@@ -4,8 +4,10 @@
 
 #include "chrome/browser/ui/thumbnails/thumbnail_image.h"
 
+#include <algorithm>
 #include <utility>
 
+#include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
@@ -15,15 +17,12 @@
 #include "ui/gfx/codec/jpeg_codec.h"
 #include "ui/gfx/skia_util.h"
 
-void ThumbnailImage::Observer::OnThumbnailImageAvailable(
-    gfx::ImageSkia thumbnail_image) {}
+ThumbnailImage::Subscription::Subscription(
+    scoped_refptr<ThumbnailImage> thumbnail)
+    : thumbnail_(std::move(thumbnail)) {}
 
-void ThumbnailImage::Observer::OnCompressedThumbnailDataAvailable(
-    CompressedThumbnailData thumbnail_data) {}
-
-base::Optional<gfx::Size> ThumbnailImage::Observer::GetThumbnailSizeHint()
-    const {
-  return base::nullopt;
+ThumbnailImage::Subscription::~Subscription() {
+  thumbnail_->HandleSubscriptionDestroyed(this);
 }
 
 ThumbnailImage::Delegate::~Delegate() {
@@ -46,29 +45,17 @@
     delegate_->thumbnail_ = nullptr;
 }
 
-void ThumbnailImage::AddObserver(Observer* observer) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(observer);
-  if (!observers_.HasObserver(observer)) {
-    const bool is_first_observer = !observers_.might_have_observers();
-    observers_.AddObserver(observer);
-    if (is_first_observer && delegate_)
-      delegate_->ThumbnailImageBeingObservedChanged(true);
-  }
-}
+std::unique_ptr<ThumbnailImage::Subscription> ThumbnailImage::Subscribe() {
+  // Use explicit new since Subscription constructor is private.
+  auto subscription =
+      base::WrapUnique(new Subscription(base::WrapRefCounted(this)));
+  subscribers_.insert(subscribers_.end(), subscription.get());
 
-void ThumbnailImage::RemoveObserver(Observer* observer) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(observer);
-  if (observers_.HasObserver(observer)) {
-    observers_.RemoveObserver(observer);
-    if (delegate_ && !observers_.might_have_observers())
-      delegate_->ThumbnailImageBeingObservedChanged(false);
-  }
-}
+  // Notify |delegate_| if this is the first subscriber.
+  if (subscribers_.size() == 1)
+    delegate_->ThumbnailImageBeingObservedChanged(true);
 
-bool ThumbnailImage::HasObserver(const Observer* observer) const {
-  return observers_.HasObserver(observer);
+  return subscription;
 }
 
 void ThumbnailImage::AssignSkBitmap(SkBitmap bitmap) {
@@ -147,18 +134,22 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (async_operation_finished_callback_)
     async_operation_finished_callback_.Run();
-  for (auto& observer : observers_) {
-    auto size_hint = observer.GetThumbnailSizeHint();
-    observer.OnThumbnailImageAvailable(
-        size_hint ? CropPreviewImage(image, *size_hint) : image);
+
+  for (Subscription* subscription : subscribers_) {
+    auto size_hint = subscription->size_hint_;
+    if (subscription->uncompressed_image_callback_)
+      subscription->uncompressed_image_callback_.Run(
+          size_hint ? CropPreviewImage(image, *size_hint) : image);
   }
 }
 
 void ThumbnailImage::NotifyCompressedDataObservers(
     CompressedThumbnailData data) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  for (auto& observer : observers_)
-    observer.OnCompressedThumbnailDataAvailable(data);
+  for (Subscription* subscription : subscribers_) {
+    if (subscription->compressed_image_callback_)
+      subscription->compressed_image_callback_.Run(data);
+  }
 }
 
 // static
@@ -216,3 +207,17 @@
   source_image.bitmap()->extractSubset(&cropped, gfx::RectToSkIRect(clip_rect));
   return gfx::ImageSkia::CreateFrom1xBitmap(cropped);
 }
+
+void ThumbnailImage::HandleSubscriptionDestroyed(Subscription* subscription) {
+  // The order of |subscribers_| does not matter. We can simply swap
+  // |subscription| in |subscribers_| with the last element, then pop it
+  // off the back.
+  auto it = std::find(subscribers_.begin(), subscribers_.end(), subscription);
+  DCHECK(it != subscribers_.end());
+  std::swap(*it, *(subscribers_.end() - 1));
+  subscribers_.pop_back();
+
+  // If that was the last subscriber, tell |delegate_| (if it still exists).
+  if (delegate_ && subscribers_.empty())
+    delegate_->ThumbnailImageBeingObservedChanged(false);
+}
diff --git a/chrome/browser/ui/thumbnails/thumbnail_image.h b/chrome/browser/ui/thumbnails/thumbnail_image.h
index dbad55c..20646d41 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_image.h
+++ b/chrome/browser/ui/thumbnails/thumbnail_image.h
@@ -5,10 +5,12 @@
 #ifndef CHROME_BROWSER_UI_THUMBNAILS_THUMBNAIL_IMAGE_H_
 #define CHROME_BROWSER_UI_THUMBNAILS_THUMBNAIL_IMAGE_H_
 
+#include <memory>
 #include <utility>
 #include <vector>
 
 #include "base/callback.h"
+#include "base/containers/flat_set.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
@@ -29,16 +31,27 @@
   using CompressedThumbnailData =
       scoped_refptr<base::RefCountedData<std::vector<uint8_t>>>;
 
-  // Observes uncompressed and/or compressed versions of the thumbnail image as
-  // they are available.
-  class Observer : public base::CheckedObserver {
+  class Subscription {
    public:
-    // Receives uncompressed thumbnail image data. Default is no-op.
-    virtual void OnThumbnailImageAvailable(gfx::ImageSkia thumbnail_image);
+    Subscription() = delete;
+    ~Subscription();
 
-    // Receives compressed thumbnail image data. Default is no-op.
-    virtual void OnCompressedThumbnailDataAvailable(
-        CompressedThumbnailData thumbnail_data);
+    using UncompressedImageCallback =
+        base::RepeatingCallback<void(gfx::ImageSkia)>;
+    using CompressedImageCallback =
+        base::RepeatingCallback<void(CompressedThumbnailData)>;
+
+    // Set callbacks to receive image data. Subscribers are not allowed
+    // to unsubscribe (by destroying |this|) from the callback. If
+    // necessary, post a task to destroy it soon after.
+
+    void SetUncompressedImageCallback(UncompressedImageCallback callback) {
+      uncompressed_image_callback_ = std::move(callback);
+    }
+
+    void SetCompressedImageCallback(CompressedImageCallback callback) {
+      compressed_image_callback_ = std::move(callback);
+    }
 
     // Provides a desired aspect ratio and minimum size that the observer will
     // accept. If not specified, or if available thumbnail data is smaller in
@@ -54,7 +67,20 @@
     // image passed to OnThumbnailImageAvailable fits the needs of the observer
     // for display purposes, without the observer having to further crop the
     // image. The default is unspecified.
-    virtual base::Optional<gfx::Size> GetThumbnailSizeHint() const;
+    void SetSizeHint(const base::Optional<gfx::Size>& size_hint) {
+      size_hint_ = size_hint;
+    }
+
+   private:
+    friend class ThumbnailImage;
+
+    explicit Subscription(scoped_refptr<ThumbnailImage> thumbnail);
+
+    scoped_refptr<ThumbnailImage> thumbnail_;
+    base::Optional<gfx::Size> size_hint_;
+
+    UncompressedImageCallback uncompressed_image_callback_;
+    CompressedImageCallback compressed_image_callback_;
   };
 
   // Represents the endpoint
@@ -77,9 +103,14 @@
 
   bool has_data() const { return data_.get(); }
 
-  void AddObserver(Observer* observer);
-  void RemoveObserver(Observer* observer);
-  bool HasObserver(const Observer* observer) const;
+  // Subscribe to thumbnail updates. See |Subscription| to set a
+  // callback and conigure additional options.
+  //
+  // Even if a callback is not set, the subscription influences
+  // thumbnail capture. It should be destroyed when updates are not
+  // needed. It is designed to be stored in base::Optional, created and
+  // destroyed as needed.
+  std::unique_ptr<Subscription> Subscribe();
 
   // Sets the SkBitmap data and notifies observers with the resulting image.
   void AssignSkBitmap(SkBitmap bitmap);
@@ -130,6 +161,8 @@
   static gfx::ImageSkia CropPreviewImage(const gfx::ImageSkia& source_image,
                                          const gfx::Size& minimum_size);
 
+  void HandleSubscriptionDestroyed(Subscription* subscription);
+
   Delegate* delegate_;
 
   // This is a scoped_refptr to immutable data. Once set, the wrapped
@@ -138,7 +171,12 @@
   // the old data.
   CompressedThumbnailData data_;
 
-  base::ObserverList<Observer> observers_;
+  // Subscriptions are inserted on |Subscribe()| calls and removed when
+  // they are destroyed via callback. The order of subscriber
+  // notification doesn't matter, so don't maintain any ordering. Since
+  // the number of subscribers for a given thumbnail is expected to be
+  // small, doing a linear search to remove a subscriber is fine.
+  std::vector<Subscription*> subscribers_;
 
   // Called when an asynchronous operation (such as encoding image data upon
   // assignment or decoding image data for observers) finishes or fails.
diff --git a/chrome/browser/ui/thumbnails/thumbnail_image_unittest.cc b/chrome/browser/ui/thumbnails/thumbnail_image_unittest.cc
index 308715a..f97c137 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_image_unittest.cc
+++ b/chrome/browser/ui/thumbnails/thumbnail_image_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
 #include "base/scoped_observer.h"
 #include "base/test/task_environment.h"
@@ -20,81 +21,56 @@
 constexpr int kTestBitmapWidth = 200;
 constexpr int kTestBitmapHeight = 123;
 
-// Waits for thumbnail images (or compressed data) and can report how many
-// images it has received.
-class TestThumbnailImageObserver : public ThumbnailImage::Observer {
+template <typename... T>
+base::RepeatingCallback<void(T...)> IgnoreArgs(
+    base::RepeatingCallback<void()> cb) {
+  auto helper = [](T...) {};
+  return base::BindRepeating(helper).Then(std::move(cb));
+}
+
+class CallbackWaiter {
  public:
-  // Wait for the uncompressed thumbnail image.
-  void WaitForImage() {
-    if (new_image_count_ > last_image_count_) {
-      last_image_count_ = new_image_count_;
+  CallbackWaiter() {
+    callback_ = base::BindRepeating(&CallbackWaiter::HandleCallback,
+                                    weak_ptr_factory_.GetWeakPtr());
+  }
+
+  base::RepeatingClosure callback() { return callback_; }
+
+  bool called() const { return called_; }
+
+  void Reset() { called_ = false; }
+
+  void Wait() {
+    if (called_)
       return;
-    }
 
-    // Need a fresh loop since we may have quit out of the last one.
-    run_loop_ = std::make_unique<base::RunLoop>();
-    waiting_for_image_ = true;
-    run_loop_->Run();
-  }
-
-  // Wait for compressed thumbnail data.
-  void WaitForCompressedData() {
-    if (new_compressed_count_ > last_compressed_count_) {
-      last_compressed_count_ = new_compressed_count_;
-      return;
-    }
-
-    // Need a fresh loop since we may have quit out of the last one.
-    run_loop_ = std::make_unique<base::RunLoop>();
-    waiting_for_data_ = true;
-    run_loop_->Run();
-  }
-
-  int new_image_count() const { return new_image_count_; }
-  gfx::ImageSkia thumbnail_image() const { return thumbnail_image_; }
-  int new_compressed_count() const { return new_compressed_count_; }
-  ThumbnailImage::CompressedThumbnailData compressed_data() const {
-    return compressed_data_;
-  }
-
-  ScopedObserver<ThumbnailImage, ThumbnailImage::Observer>* scoped_observer() {
-    return &scoped_observer_;
+    base::RunLoop run_loop;
+    quit_closure_ = run_loop.QuitClosure();
+    run_loop.Run();
   }
 
  private:
-  // ThumbnailImage::Observer:
-  void OnThumbnailImageAvailable(gfx::ImageSkia thumbnail_image) override {
-    ++new_image_count_;
-    thumbnail_image_ = thumbnail_image;
-    if (waiting_for_image_) {
-      last_image_count_ = new_image_count_;
-      run_loop_->Quit();
-      waiting_for_image_ = false;
-    }
+  void HandleCallback() {
+    if (quit_closure_)
+      std::move(quit_closure_).Run();
+    called_ = true;
   }
 
-  void OnCompressedThumbnailDataAvailable(
-      ThumbnailImage::CompressedThumbnailData thumbnail_data) override {
-    ++new_compressed_count_;
-    compressed_data_ = thumbnail_data;
-    if (waiting_for_data_) {
-      last_compressed_count_ = new_compressed_count_;
-      run_loop_->Quit();
-      waiting_for_data_ = false;
-    }
-  }
+  base::RepeatingClosure callback_;
+  base::OnceClosure quit_closure_;
+  bool called_ = false;
 
-  ScopedObserver<ThumbnailImage, ThumbnailImage::Observer> scoped_observer_{
-      this};
-  int new_image_count_ = 0;
-  int last_image_count_ = 0;
-  gfx::ImageSkia thumbnail_image_;
-  int new_compressed_count_ = 0;
-  int last_compressed_count_ = 0;
-  ThumbnailImage::CompressedThumbnailData compressed_data_;
-  bool waiting_for_image_ = false;
-  bool waiting_for_data_ = false;
-  std::unique_ptr<base::RunLoop> run_loop_;
+  base::WeakPtrFactory<CallbackWaiter> weak_ptr_factory_{this};
+};
+
+class StubDelegate : public ThumbnailImage::Delegate {
+ public:
+  StubDelegate() = default;
+  ~StubDelegate() override = default;
+
+  // ThumbnailImage::Delegate:
+  void ThumbnailImageBeingObservedChanged(bool is_being_observed) override {}
 };
 
 }  // anonymous namespace
@@ -128,169 +104,228 @@
   DISALLOW_COPY_AND_ASSIGN(ThumbnailImageTest);
 };
 
-TEST_F(ThumbnailImageTest, Add_Remove_Observer) {
+using Subscription = ThumbnailImage::Subscription;
+
+TEST_F(ThumbnailImageTest, AddRemoveSubscriber) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
   EXPECT_FALSE(is_being_observed());
-  TestThumbnailImageObserver observer;
-  image->AddObserver(&observer);
-  EXPECT_TRUE(image->HasObserver(&observer));
+
+  std::unique_ptr<Subscription> subscription = image->Subscribe();
   EXPECT_TRUE(is_being_observed());
-  image->RemoveObserver(&observer);
-  EXPECT_FALSE(image->HasObserver(&observer));
+
+  subscription.reset();
   EXPECT_FALSE(is_being_observed());
 }
 
-TEST_F(ThumbnailImageTest, Add_Remove_MultipleObservers) {
+TEST_F(ThumbnailImageTest, AddRemoveMultipleObservers) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
   EXPECT_FALSE(is_being_observed());
-  TestThumbnailImageObserver observer;
-  TestThumbnailImageObserver observer2;
-  image->AddObserver(&observer);
-  EXPECT_TRUE(image->HasObserver(&observer));
+
+  std::unique_ptr<Subscription> subscription1 = image->Subscribe();
   EXPECT_TRUE(is_being_observed());
-  image->AddObserver(&observer2);
-  EXPECT_TRUE(image->HasObserver(&observer2));
+
+  std::unique_ptr<Subscription> subscription2 = image->Subscribe();
   EXPECT_TRUE(is_being_observed());
-  image->RemoveObserver(&observer);
-  EXPECT_FALSE(image->HasObserver(&observer));
-  EXPECT_TRUE(image->HasObserver(&observer2));
+
+  subscription1.reset();
   EXPECT_TRUE(is_being_observed());
-  image->RemoveObserver(&observer2);
-  EXPECT_FALSE(image->HasObserver(&observer2));
+
+  subscription2.reset();
   EXPECT_FALSE(is_being_observed());
 }
 
-TEST_F(ThumbnailImageTest, AssignSkBitmap_NotifiesObservers) {
+TEST_F(ThumbnailImageTest, AssignSkBitmapNotifiesObservers) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
-  TestThumbnailImageObserver observer;
-  TestThumbnailImageObserver observer2;
-  observer.scoped_observer()->Add(image.get());
-  observer2.scoped_observer()->Add(image.get());
+
+  std::unique_ptr<Subscription> subscription1 = image->Subscribe();
+  std::unique_ptr<Subscription> subscription2 = image->Subscribe();
+
+  CallbackWaiter waiter1;
+  subscription1->SetUncompressedImageCallback(
+      IgnoreArgs<gfx::ImageSkia>(waiter1.callback()));
+
+  CallbackWaiter waiter2;
+  subscription2->SetUncompressedImageCallback(
+      IgnoreArgs<gfx::ImageSkia>(waiter2.callback()));
 
   SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
   image->AssignSkBitmap(bitmap);
-  observer.WaitForImage();
-  observer2.WaitForImage();
-  EXPECT_EQ(1, observer.new_image_count());
-  EXPECT_EQ(1, observer2.new_image_count());
-  EXPECT_FALSE(observer.thumbnail_image().isNull());
-  EXPECT_FALSE(observer2.thumbnail_image().isNull());
-  EXPECT_EQ(gfx::Size(kTestBitmapWidth, kTestBitmapHeight),
-            observer.thumbnail_image().size());
+
+  waiter1.Wait();
+  waiter2.Wait();
+  EXPECT_TRUE(waiter1.called());
+  EXPECT_TRUE(waiter2.called());
 }
 
 TEST_F(ThumbnailImageTest, AssignSkBitmap_NotifiesObserversAgain) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
-  TestThumbnailImageObserver observer;
-  TestThumbnailImageObserver observer2;
-  observer.scoped_observer()->Add(image.get());
-  observer2.scoped_observer()->Add(image.get());
+
+  std::unique_ptr<Subscription> subscription1 = image->Subscribe();
+  std::unique_ptr<Subscription> subscription2 = image->Subscribe();
+
+  CallbackWaiter waiter1;
+  subscription1->SetUncompressedImageCallback(
+      IgnoreArgs<gfx::ImageSkia>(waiter1.callback()));
+
+  CallbackWaiter waiter2;
+  subscription2->SetUncompressedImageCallback(
+      IgnoreArgs<gfx::ImageSkia>(waiter2.callback()));
 
   SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
   image->AssignSkBitmap(bitmap);
-  observer.WaitForImage();
-  observer2.WaitForImage();
+
+  waiter1.Wait();
+  waiter2.Wait();
+  EXPECT_TRUE(waiter1.called());
+  EXPECT_TRUE(waiter2.called());
+
+  waiter1.Reset();
+  waiter2.Reset();
+
   image->AssignSkBitmap(bitmap);
-  observer.WaitForImage();
-  observer2.WaitForImage();
-  EXPECT_EQ(2, observer.new_image_count());
-  EXPECT_EQ(2, observer2.new_image_count());
-  EXPECT_FALSE(observer.thumbnail_image().isNull());
-  EXPECT_FALSE(observer2.thumbnail_image().isNull());
-  EXPECT_EQ(gfx::Size(kTestBitmapWidth, kTestBitmapHeight),
-            observer.thumbnail_image().size());
+
+  waiter1.Wait();
+  waiter2.Wait();
+  EXPECT_TRUE(waiter1.called());
+  EXPECT_TRUE(waiter2.called());
 }
 
 TEST_F(ThumbnailImageTest, AssignSkBitmap_NotifiesCompressedObservers) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
-  TestThumbnailImageObserver observer;
-  TestThumbnailImageObserver observer2;
-  observer.scoped_observer()->Add(image.get());
-  observer2.scoped_observer()->Add(image.get());
+
+  std::unique_ptr<Subscription> subscription1 = image->Subscribe();
+  std::unique_ptr<Subscription> subscription2 = image->Subscribe();
+
+  CallbackWaiter waiter1;
+  subscription1->SetCompressedImageCallback(
+      IgnoreArgs<ThumbnailImage::CompressedThumbnailData>(waiter1.callback()));
+
+  CallbackWaiter waiter2;
+  subscription2->SetCompressedImageCallback(
+      IgnoreArgs<ThumbnailImage::CompressedThumbnailData>(waiter2.callback()));
 
   SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
-  auto compressed = Compress(bitmap);
-
   image->AssignSkBitmap(bitmap);
-  observer.WaitForCompressedData();
-  observer2.WaitForCompressedData();
-  EXPECT_EQ(1, observer.new_compressed_count());
-  EXPECT_EQ(1, observer2.new_compressed_count());
-  EXPECT_TRUE(observer.compressed_data());
-  EXPECT_TRUE(observer2.compressed_data());
-  EXPECT_EQ(compressed, observer.compressed_data()->data);
-  EXPECT_EQ(compressed, observer2.compressed_data()->data);
+
+  waiter1.Wait();
+  waiter2.Wait();
+  EXPECT_TRUE(waiter1.called());
+  EXPECT_TRUE(waiter2.called());
 }
 
 TEST_F(ThumbnailImageTest, AssignSkBitmap_NotifiesCompressedObserversAgain) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
-  TestThumbnailImageObserver observer;
-  TestThumbnailImageObserver observer2;
-  observer.scoped_observer()->Add(image.get());
-  observer2.scoped_observer()->Add(image.get());
+
+  std::unique_ptr<Subscription> subscription1 = image->Subscribe();
+  std::unique_ptr<Subscription> subscription2 = image->Subscribe();
+
+  CallbackWaiter waiter1;
+  subscription1->SetCompressedImageCallback(
+      IgnoreArgs<ThumbnailImage::CompressedThumbnailData>(waiter1.callback()));
+
+  CallbackWaiter waiter2;
+  subscription2->SetCompressedImageCallback(
+      IgnoreArgs<ThumbnailImage::CompressedThumbnailData>(waiter2.callback()));
 
   SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
-  auto compressed = Compress(bitmap);
+  image->AssignSkBitmap(bitmap);
+
+  waiter1.Wait();
+  waiter2.Wait();
+  EXPECT_TRUE(waiter1.called());
+  EXPECT_TRUE(waiter2.called());
+
+  waiter1.Reset();
+  waiter2.Reset();
 
   image->AssignSkBitmap(bitmap);
-  observer.WaitForCompressedData();
-  observer2.WaitForCompressedData();
-  image->AssignSkBitmap(bitmap);
-  observer.WaitForCompressedData();
-  observer2.WaitForCompressedData();
-  EXPECT_EQ(2, observer.new_compressed_count());
-  EXPECT_EQ(2, observer2.new_compressed_count());
-  EXPECT_TRUE(observer.compressed_data());
-  EXPECT_TRUE(observer2.compressed_data());
-  EXPECT_EQ(compressed, observer.compressed_data()->data);
-  EXPECT_EQ(compressed, observer2.compressed_data()->data);
+
+  waiter1.Wait();
+  waiter2.Wait();
+  EXPECT_TRUE(waiter1.called());
+  EXPECT_TRUE(waiter2.called());
 }
 
 TEST_F(ThumbnailImageTest, RequestThumbnailImage) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
-  TestThumbnailImageObserver observer;
-  observer.scoped_observer()->Add(image.get());
+
+  std::unique_ptr<Subscription> subscription1 = image->Subscribe();
+
+  CallbackWaiter waiter1;
+  subscription1->SetUncompressedImageCallback(
+      IgnoreArgs<gfx::ImageSkia>(waiter1.callback()));
 
   SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
   image->AssignSkBitmap(bitmap);
-  observer.WaitForImage();
+  waiter1.Wait();
+  EXPECT_TRUE(waiter1.called());
+  waiter1.Reset();
 
-  TestThumbnailImageObserver observer2;
-  observer2.scoped_observer()->Add(image.get());
+  std::unique_ptr<Subscription> subscription2 = image->Subscribe();
+
+  CallbackWaiter waiter2;
+  subscription2->SetUncompressedImageCallback(
+      IgnoreArgs<gfx::ImageSkia>(waiter2.callback()));
+
   image->RequestThumbnailImage();
-  observer.WaitForImage();
-  observer2.WaitForImage();
-  EXPECT_EQ(2, observer.new_image_count());
-  EXPECT_EQ(1, observer2.new_image_count());
-  EXPECT_FALSE(observer2.thumbnail_image().isNull());
-  EXPECT_EQ(gfx::Size(kTestBitmapWidth, kTestBitmapHeight),
-            observer2.thumbnail_image().size());
+  waiter1.Wait();
+  waiter2.Wait();
+  EXPECT_TRUE(waiter1.called());
+  EXPECT_TRUE(waiter2.called());
 }
 
 TEST_F(ThumbnailImageTest, RequestCompressedThumbnailData) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
-  TestThumbnailImageObserver observer;
-  observer.scoped_observer()->Add(image.get());
 
-  SkBitmap bitmap = CreateBitmap(kTestBitmapHeight, kTestBitmapHeight);
+  std::unique_ptr<Subscription> subscription = image->Subscribe();
+
+  CallbackWaiter waiter;
+  subscription->SetCompressedImageCallback(
+      IgnoreArgs<ThumbnailImage::CompressedThumbnailData>(waiter.callback()));
+
+  SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
   image->AssignSkBitmap(bitmap);
-  observer.WaitForImage();
+  waiter.Wait();
+  EXPECT_TRUE(waiter.called());
+  waiter.Reset();
 
-  const int count_before_request = observer.new_compressed_count();
   image->RequestCompressedThumbnailData();
-  EXPECT_EQ(count_before_request + 1, observer.new_compressed_count());
+  waiter.Wait();
+  EXPECT_TRUE(waiter.called());
 }
 
 TEST_F(ThumbnailImageTest, ClearThumbnailWhileNotifyingObservers) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
-  TestThumbnailImageObserver observer;
-  observer.scoped_observer()->Add(image.get());
+
+  std::unique_ptr<Subscription> subscription = image->Subscribe();
+
+  CallbackWaiter waiter;
+  subscription->SetUncompressedImageCallback(
+      IgnoreArgs<gfx::ImageSkia>(waiter.callback()));
 
   SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
   image->AssignSkBitmap(bitmap);
-  observer.WaitForImage();
+  waiter.Wait();
+  EXPECT_TRUE(waiter.called());
+  waiter.Reset();
 
   image->RequestThumbnailImage();
   image->ClearData();
-  observer.WaitForImage();
+  waiter.Wait();
+  EXPECT_TRUE(waiter.called());
+}
+
+// Makes sure a null dereference does not happen. Regression test for
+// crbug.com/1159701.
+TEST_F(ThumbnailImageTest, UnsubscribeAfterDelegateDestroyed) {
+  auto delegate = std::make_unique<StubDelegate>();
+  auto image = base::MakeRefCounted<ThumbnailImage>(delegate.get());
+
+  std::unique_ptr<Subscription> subscription = image->Subscribe();
+
+  // Normally |image| will notify its delegate when the last
+  // subscription is destroyed. When there is no delegate it shouldn't
+  // do anything.
+  delegate.reset();
+  subscription.reset();
 }
diff --git a/chrome/browser/ui/thumbnails/thumbnail_tab_helper_browsertest.cc b/chrome/browser/ui/thumbnails/thumbnail_tab_helper_browsertest.cc
index 3e1ce62..6ecc30d0 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_tab_helper_browsertest.cc
+++ b/chrome/browser/ui/thumbnails/thumbnail_tab_helper_browsertest.cc
@@ -29,37 +29,30 @@
 
 namespace {
 
-class ThumbnailWaiter : public ThumbnailImage::Observer {
+class ThumbnailWaiter {
  public:
   ThumbnailWaiter() = default;
-  ~ThumbnailWaiter() override = default;
+  ~ThumbnailWaiter() = default;
 
   base::Optional<gfx::ImageSkia> WaitForThumbnail(ThumbnailImage* thumbnail) {
-    DCHECK(!thumbnail_);
-    thumbnail_ = thumbnail;
-    scoped_observer_.Add(thumbnail);
-    thumbnail_->RequestThumbnailImage();
+    std::unique_ptr<ThumbnailImage::Subscription> subscription =
+        thumbnail->Subscribe();
+    subscription->SetUncompressedImageCallback(base::BindRepeating(
+        &ThumbnailWaiter::ThumbnailImageCallback, base::Unretained(this)));
+    thumbnail->RequestThumbnailImage();
     run_loop_.Run();
     return image_;
   }
 
  protected:
-  // ThumbnailImage::Observer:
-  void OnThumbnailImageAvailable(gfx::ImageSkia thumbnail_image) override {
-    if (thumbnail_) {
-      scoped_observer_.Remove(thumbnail_);
-      thumbnail_ = nullptr;
-      image_ = thumbnail_image;
-      run_loop_.Quit();
-    }
+  void ThumbnailImageCallback(gfx::ImageSkia thumbnail_image) {
+    image_ = std::move(thumbnail_image);
+    run_loop_.Quit();
   }
 
  private:
   base::RunLoop run_loop_;
-  ThumbnailImage* thumbnail_ = nullptr;
   base::Optional<gfx::ImageSkia> image_;
-  ScopedObserver<ThumbnailImage, ThumbnailImage::Observer> scoped_observer_{
-      this};
 };
 
 }  // anonymous namespace
diff --git a/chrome/browser/ui/views/DEPS b/chrome/browser/ui/views/DEPS
index 7200cc38..35bccc4 100644
--- a/chrome/browser/ui/views/DEPS
+++ b/chrome/browser/ui/views/DEPS
@@ -7,8 +7,8 @@
 
 specific_include_rules = {
   "chrome_views_delegate_chromeos\.cc": [
+    "+ash/public/cpp/ash_features.h",
     "+ash/shell.h",
-    "+ash/wm/window_state.h",
   ],
   "qrcode_generator\.*": [
     "+chrome/services/qrcode_generator/public/cpp/qrcode_generator_service.h"
diff --git a/chrome/browser/ui/views/chrome_views_delegate.h b/chrome/browser/ui/views/chrome_views_delegate.h
index 7ea8195..c8f7f20e 100644
--- a/chrome/browser/ui/views/chrome_views_delegate.h
+++ b/chrome/browser/ui/views/chrome_views_delegate.h
@@ -36,6 +36,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   ProcessMenuAcceleratorResult ProcessAcceleratorWhileMenuShowing(
       const ui::Accelerator& accelerator) override;
+  bool ShouldCloseMenuIfMouseCaptureLost() const override;
   std::unique_ptr<views::NonClientFrameView> CreateDefaultNonClientFrameView(
       views::Widget* widget) override;
 #endif
diff --git a/chrome/browser/ui/views/chrome_views_delegate_chromeos.cc b/chrome/browser/ui/views/chrome_views_delegate_chromeos.cc
index 3a88c87..ffdc7de 100644
--- a/chrome/browser/ui/views/chrome_views_delegate_chromeos.cc
+++ b/chrome/browser/ui/views/chrome_views_delegate_chromeos.cc
@@ -5,10 +5,12 @@
 #include "chrome/browser/ui/views/chrome_views_delegate.h"
 
 #include "ash/public/cpp/accelerators.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/shell.h"
 #include "base/bind.h"
 #include "base/task/current_thread.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/ui/ash/chrome_capture_mode_delegate.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 
@@ -37,6 +39,12 @@
   return views::ViewsDelegate::ProcessMenuAcceleratorResult::LEAVE_MENU_OPEN;
 }
 
+bool ChromeViewsDelegate::ShouldCloseMenuIfMouseCaptureLost() const {
+  // Menu closes unless an ongoing screen capture session is underway.
+  return !(ash::features::IsCaptureModeEnabled() &&
+           ChromeCaptureModeDelegate::Get()->is_session_active());
+}
+
 std::unique_ptr<views::NonClientFrameView>
 ChromeViewsDelegate::CreateDefaultNonClientFrameView(views::Widget* widget) {
   return ash::Shell::Get()->CreateDefaultNonClientFrameView(widget);
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view_unittest.cc b/chrome/browser/ui/views/extensions/extensions_menu_view_unittest.cc
index 75502bb..0bdaee81 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view_unittest.cc
@@ -43,6 +43,7 @@
 #include "ui/gfx/geometry/point.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/layout/animating_layout_manager_test_util.h"
+#include "ui/views/view_utils.h"
 #include "ui/views/widget/widget.h"
 
 namespace {
@@ -196,7 +197,7 @@
   std::vector<ToolbarActionView*> result;
   for (views::View* child : extensions_container()->children()) {
     // Ensure we don't downcast the ExtensionsToolbarButton.
-    if (child->GetClassName() == ToolbarActionView::kClassName) {
+    if (views::IsViewClass<ToolbarActionView>(child)) {
       ToolbarActionView* const action = static_cast<ToolbarActionView*>(child);
 #if defined(OS_MAC)
       // TODO(crbug.com/1045212): Use IsActionVisibleOnToolbar() because it
@@ -533,7 +534,7 @@
   // Since the extension is removed it's no longer visible on the toolbar or in
   // the menu.
   for (views::View* child : extensions_container()->children())
-    EXPECT_NE(ToolbarActionView::kClassName, child->GetClassName());
+    EXPECT_FALSE(views::IsViewClass<ToolbarActionView>(child));
   EXPECT_EQ(0u, extensions_menu()->extensions_menu_items_for_testing().size());
 }
 
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_browsertest.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_browsertest.cc
index c96cbb8..e378ea09 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "net/dns/mock_host_resolver.h"
 #include "ui/views/layout/animating_layout_manager_test_util.h"
+#include "ui/views/view_utils.h"
 
 ExtensionsToolbarBrowserTest::ExtensionsToolbarBrowserTest(bool enable_flag) {
   if (enable_flag) {
@@ -80,7 +81,7 @@
 ExtensionsToolbarBrowserTest::GetToolbarActionViews() const {
   std::vector<ToolbarActionView*> views;
   for (auto* view : GetExtensionsToolbarContainer()->children()) {
-    if (view->GetClassName() == ToolbarActionView::kClassName)
+    if (views::IsViewClass<ToolbarActionView>(view))
       views.push_back(static_cast<ToolbarActionView*>(view));
   }
   return views;
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
index 9826b68..84d6c8b 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
@@ -87,7 +87,7 @@
   }
   extensions_button_->SetID(VIEW_ID_EXTENSIONS_MENU_BUTTON);
   AddMainButton(extensions_button_);
-  target_layout_manager()
+  GetTargetLayoutManager()
       ->SetFlexAllocationOrder(views::FlexAllocationOrder::kReverse)
       .SetDefault(views::kFlexBehaviorKey,
                   hide_icon_flex_specification.WithOrder(3));
@@ -136,7 +136,7 @@
   anchored_widgets_.push_back({widget, extension_id});
   widget->AddObserver(this);
   UpdateIconVisibility(extension_id);
-  animating_layout_manager()->PostOrQueueAction(base::BindOnce(
+  GetAnimatingLayoutManager()->PostOrQueueAction(base::BindOnce(
       &ExtensionsToolbarContainer::AnchorAndShowWidgetImmediately,
       weak_ptr_factory_.GetWeakPtr(), widget));
 }
@@ -207,9 +207,9 @@
 
   if (must_show ||
       (CanShowIconInToolbar() && model_->IsActionPinned(extension_id)))
-    animating_layout_manager()->FadeIn(action_view);
+    GetAnimatingLayoutManager()->FadeIn(action_view);
   else
-    animating_layout_manager()->FadeOut(action_view);
+    GetAnimatingLayoutManager()->FadeOut(action_view);
 }
 
 void ExtensionsToolbarContainer::AnchorAndShowWidgetImmediately(
@@ -349,7 +349,7 @@
   DCHECK(!popped_out_action_);
   popped_out_action_ = action;
   UpdateIconVisibility(action->GetId());
-  animating_layout_manager()->PostOrQueueAction(std::move(closure));
+  GetAnimatingLayoutManager()->PostOrQueueAction(std::move(closure));
   UpdateContainerVisibility();
 }
 
@@ -629,7 +629,7 @@
       drop_info_->action_id;
   drop_info_.reset();
   ReorderViews();
-  animating_layout_manager()->PostOrQueueAction(base::BindOnce(
+  GetAnimatingLayoutManager()->PostOrQueueAction(base::BindOnce(
       &ExtensionsToolbarContainer::SetExtensionIconVisibility,
       weak_ptr_factory_.GetWeakPtr(), dragged_extension_id, true));
 }
@@ -703,7 +703,7 @@
   // Layout animation does not handle host view visibility changing; requires
   // resetting.
   if (was_visible != GetVisible())
-    animating_layout_manager()->ResetLayout();
+    GetAnimatingLayoutManager()->ResetLayout();
 
   if (!was_visible && GetVisible() && GetOnVisibleCallbackForTesting())
     std::move(GetOnVisibleCallbackForTesting()).Run();
@@ -719,7 +719,7 @@
   if (display_mode_ != DisplayMode::kAutoHide)
     return true;
 
-  if (animating_layout_manager()->is_animating())
+  if (GetAnimatingLayoutManager()->is_animating())
     return true;
 
   // Is menu showing.
@@ -738,7 +738,7 @@
 }
 
 void ExtensionsToolbarContainer::UpdateContainerVisibilityAfterAnimation() {
-  animating_layout_manager()->PostOrQueueAction(
+  GetAnimatingLayoutManager()->PostOrQueueAction(
       base::BindOnce(&ExtensionsToolbarContainer::UpdateContainerVisibility,
                      weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index faaa693..27b5324 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -1549,7 +1549,7 @@
   // location.
   //
   // Not used on the Mac, which has a normal menu bar.
-  if (toolbar_->IsAppMenuFocused()) {
+  if (toolbar_->GetAppMenuFocused()) {
     RestoreFocus();
   } else {
     DCHECK(!immersive_mode_controller_->IsEnabled());
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
index f9b51dc..103b692 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -492,7 +492,6 @@
   frame_background_->set_frame_color(frame_color);
   frame_background_->set_use_custom_frame(frame()->UseCustomFrame());
   frame_background_->set_is_active(active);
-  frame_background_->set_incognito(browser_view()->IsIncognito());
   frame_background_->set_theme_image(GetFrameImage());
   const int y_inset =
       browser_view()->IsTabStripVisible()
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc b/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
index 168b3e03..e9c78c8e 100644
--- a/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
@@ -882,7 +882,13 @@
   EXPECT_TRUE(IsPlayingSessionDisplayedFirst());
 }
 
-IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest, LiveCaption) {
+// Flaky on Mac: crbug.com/1163666
+#if defined(OS_MAC)
+#define MAYBE_LiveCaption DISABLED_LiveCaption
+#else
+#define MAYBE_LiveCaption LiveCaption
+#endif  // defined(OS_MAC)
+IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest, MAYBE_LiveCaption) {
   // Open a tab and play media.
   OpenTestURL();
   StartPlayback();
diff --git a/chrome/browser/ui/views/hover_button_controller.cc b/chrome/browser/ui/views/hover_button_controller.cc
index aaa3335..5d6cfa1 100644
--- a/chrome/browser/ui/views/hover_button_controller.cc
+++ b/chrome/browser/ui/views/hover_button_controller.cc
@@ -58,9 +58,9 @@
 
 void HoverButtonController::OnGestureEvent(ui::GestureEvent* event) {
   if (event->type() == ui::ET_GESTURE_TAP) {
+    button()->SetState(views::Button::STATE_NORMAL);
     if (callback_)
       callback_.Run(*event);
-    button()->SetState(views::Button::STATE_NORMAL);
   } else {
     ButtonController::OnGestureEvent(event);
   }
diff --git a/chrome/browser/ui/views/hover_button_unittest.cc b/chrome/browser/ui/views/hover_button_unittest.cc
index c212ab0..4d79e60 100644
--- a/chrome/browser/ui/views/hover_button_unittest.cc
+++ b/chrome/browser/ui/views/hover_button_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/accessibility/ax_enums.mojom.h"
@@ -159,4 +160,31 @@
   widget()->Close();
 }
 
+// No touch on desktop Mac.
+#if !defined(OS_MAC) || defined(USE_AURA)
+
+// Tests that tapping hover button does not crash if the tap handler removes the
+// button from views hierarchy.
+TEST_F(HoverButtonTest, TapGestureThatDeletesTheButton) {
+  bool clicked = false;
+  HoverButton* button = widget()->SetContentsView(std::make_unique<HoverButton>(
+      base::BindRepeating(
+          [](bool* clicked, views::Widget* widget) {
+            *clicked = true;
+            // Update the widget contents view, which deletes the hover button.
+            widget->SetContentsView(std::make_unique<views::View>());
+          },
+          &clicked, widget()),
+      CreateIcon(), base::ASCIIToUTF16("Title"), base::string16()));
+  button->SetBoundsRect(gfx::Rect(100, 100, 200, 200));
+  widget()->Show();
+
+  generator()->GestureTapAt(gfx::Point(150, 150));
+  EXPECT_TRUE(clicked);
+
+  widget()->Close();
+}
+
+#endif  // !defined(OS_MAC) || defined(USE_AURA)
+
 }  // namespace
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
index 8cbbac9f..9841681 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -30,6 +30,7 @@
 #include "ui/views/border.h"
 #include "ui/views/controls/highlight_path_generator.h"
 #include "ui/views/controls/image_view.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 #include "ui/views/widget/widget.h"
 
 namespace {
@@ -439,10 +440,6 @@
   return end_padding;
 }
 
-const char* IconLabelBubbleView::GetClassName() const {
-  return "IconLabelBubbleView";
-}
-
 void IconLabelBubbleView::SetUpForAnimation() {
   SetInkDropMode(InkDropMode::ON);
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
@@ -566,3 +563,12 @@
       gfx::Insets(GetLayoutConstant(LOCATION_BAR_CHILD_INTERIOR_PADDING),
                   GetLayoutInsets(LOCATION_BAR_ICON_INTERIOR_PADDING).left())));
 }
+
+BEGIN_METADATA(IconLabelBubbleView, views::LabelButton)
+ADD_READONLY_PROPERTY_METADATA(SkColor, ForegroundColor)
+ADD_READONLY_PROPERTY_METADATA(double, AnimationValue)
+ADD_READONLY_PROPERTY_METADATA(int, InternalSpacing)
+ADD_READONLY_PROPERTY_METADATA(int, ExtraInternalSpacing)
+ADD_READONLY_PROPERTY_METADATA(int, WidthBetweenIconAndSeparator)
+ADD_READONLY_PROPERTY_METADATA(int, EndPaddingWithSeparator)
+END_METADATA
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
index 02a3dd2c..f5d9df0 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
@@ -20,6 +20,7 @@
 #include "ui/views/animation/ink_drop_observer.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/metadata/metadata_header_macros.h"
 #include "ui/views/widget/widget_observer.h"
 
 namespace gfx {
@@ -41,6 +42,8 @@
 class IconLabelBubbleView : public views::InkDropObserver,
                             public views::LabelButton {
  public:
+  METADATA_HEADER(IconLabelBubbleView);
+
   static constexpr int kTrailingPaddingPreMd = 2;
 
   class Delegate {
@@ -172,10 +175,6 @@
   // the animation is set to fully shown or fully hidden.
   void ResetSlideAnimation(bool show);
 
-  // Returns true iff the slide animation has started, has not ended and is
-  // currently paused.
-  bool is_animation_paused() const { return is_animation_paused_; }
-
   // Slide animation for label.
   gfx::SlideAnimation slide_animation_{this};
 
@@ -218,9 +217,6 @@
   // separator width.
   int GetEndPaddingWithSeparator() const;
 
-  // views::View:
-  const char* GetClassName() const override;
-
   // Disables highlights and calls Show on the slide animation, should not be
   // called directly, use AnimateIn() instead, which handles label visibility.
   void ShowAnimation();
diff --git a/chrome/browser/ui/views/location_bar/star_view.cc b/chrome/browser/ui/views/location_bar/star_view.cc
index 9edaafeb..b725178 100644
--- a/chrome/browser/ui/views/location_bar/star_view.cc
+++ b/chrome/browser/ui/views/location_bar/star_view.cc
@@ -77,7 +77,7 @@
   OnExecuting(source);
   if (base::FeatureList::IsEnabled(reading_list::switches::kReadLater)) {
     menu_model_ = std::make_unique<StarMenuModel>(
-        this, active(), chrome::CanMoveActiveTabToReadLater(browser_),
+        this, GetActive(), chrome::CanMoveActiveTabToReadLater(browser_),
         chrome::IsCurrentTabUnreadInReadLater(browser_));
     menu_runner_ = std::make_unique<views::MenuRunner>(
         menu_model_.get(),
@@ -95,12 +95,12 @@
 }
 
 const gfx::VectorIcon& StarView::GetVectorIcon() const {
-  return active() ? omnibox::kStarActiveIcon : omnibox::kStarIcon;
+  return GetActive() ? omnibox::kStarActiveIcon : omnibox::kStarIcon;
 }
 
 base::string16 StarView::GetTextForTooltipAndAccessibleName() const {
-  return l10n_util::GetStringUTF16(active() ? IDS_TOOLTIP_STARRED
-                                            : IDS_TOOLTIP_STAR);
+  return l10n_util::GetStringUTF16(GetActive() ? IDS_TOOLTIP_STARRED
+                                               : IDS_TOOLTIP_STAR);
 }
 
 const char* StarView::GetClassName() const {
diff --git a/chrome/browser/ui/views/location_bar/star_view_interactive_uitest.cc b/chrome/browser/ui/views/location_bar/star_view_interactive_uitest.cc
index 2ffd82d..dd5f664 100644
--- a/chrome/browser/ui/views/location_bar/star_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/location_bar/star_view_interactive_uitest.cc
@@ -66,7 +66,7 @@
 
   // The page should not initiall be bookmarked.
   EXPECT_FALSE(bookmark_model->IsBookmarked(current_url));
-  EXPECT_FALSE(star_icon->active());
+  EXPECT_FALSE(star_icon->GetActive());
 
   ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                                ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
@@ -79,7 +79,7 @@
   static_cast<views::View*>(star_icon)->OnMouseReleased(released_event);
 
   EXPECT_TRUE(bookmark_model->IsBookmarked(current_url));
-  EXPECT_TRUE(star_icon->active());
+  EXPECT_TRUE(star_icon->GetActive());
 }
 
 // Verify that clicking the bookmark star a second time hides the bookmark
@@ -176,13 +176,13 @@
 
   // The page should not initially be bookmarked.
   EXPECT_FALSE(bookmark_model->IsBookmarked(current_url));
-  EXPECT_FALSE(star_icon->active());
+  EXPECT_FALSE(star_icon->GetActive());
 
   OpenStarViewMenu(star_icon);
 
   // The page should not be bookmarked when the menu is opened.
   EXPECT_FALSE(bookmark_model->IsBookmarked(current_url));
-  EXPECT_FALSE(star_icon->active());
+  EXPECT_FALSE(star_icon->GetActive());
 
   StarMenuModel* menu_model = star_icon->menu_model_for_test();
 
@@ -193,7 +193,7 @@
   menu_model->ActivatedAt(bookmark_command_index);
 
   EXPECT_TRUE(bookmark_model->IsBookmarked(current_url));
-  EXPECT_TRUE(star_icon->active());
+  EXPECT_TRUE(star_icon->GetActive());
 }
 
 // Verifies clicking the Read Later button in the StarView's menu saves the page
@@ -212,13 +212,13 @@
 
   // The page should not initially be in model.
   EXPECT_EQ(reading_list_model->GetEntryByURL(current_url), nullptr);
-  EXPECT_FALSE(star_icon->active());
+  EXPECT_FALSE(star_icon->GetActive());
 
   OpenStarViewMenu(star_icon);
 
   // The page should not be bookmarked when the menu is opened.
   EXPECT_EQ(reading_list_model->GetEntryByURL(current_url), nullptr);
-  EXPECT_FALSE(star_icon->active());
+  EXPECT_FALSE(star_icon->GetActive());
 
   StarMenuModel* menu_model = star_icon->menu_model_for_test();
 
@@ -229,7 +229,7 @@
   menu_model->ActivatedAt(read_later_command_index);
 
   EXPECT_NE(reading_list_model->GetEntryByURL(current_url), nullptr);
-  EXPECT_FALSE(star_icon->active());
+  EXPECT_FALSE(star_icon->GetActive());
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_container.cc b/chrome/browser/ui/views/page_action/page_action_icon_container.cc
index 9ef2b28c..8da1bcf9 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_container.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_container.cc
@@ -7,11 +7,7 @@
 #include "chrome/browser/ui/views/page_action/page_action_icon_controller.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_params.h"
 #include "ui/views/layout/box_layout.h"
-
-// static
-const char
-    PageActionIconContainerView::kPageActionIconContainerViewClassName[] =
-        "PageActionIconContainerView";
+#include "ui/views/metadata/metadata_impl_macros.h"
 
 PageActionIconContainerView::PageActionIconContainerView(
     const PageActionIconParams& params)
@@ -28,10 +24,6 @@
 
 PageActionIconContainerView::~PageActionIconContainerView() = default;
 
-const char* PageActionIconContainerView::GetClassName() const {
-  return kPageActionIconContainerViewClassName;
-}
-
 void PageActionIconContainerView::ChildPreferredSizeChanged(
     views::View* child) {
   PreferredSizeChanged();
@@ -40,3 +32,6 @@
 void PageActionIconContainerView::AddPageActionIcon(views::View* icon) {
   AddChildView(icon);
 }
+
+BEGIN_METADATA(PageActionIconContainerView, views::View)
+END_METADATA
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_container.h b/chrome/browser/ui/views/page_action/page_action_icon_container.h
index 85447e4..002dfab8 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_container.h
+++ b/chrome/browser/ui/views/page_action/page_action_icon_container.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_PAGE_ACTION_PAGE_ACTION_ICON_CONTAINER_H_
 #define CHROME_BROWSER_UI_VIEWS_PAGE_ACTION_PAGE_ACTION_ICON_CONTAINER_H_
 
+#include "ui/views/metadata/metadata_header_macros.h"
 #include "ui/views/view.h"
 
 class PageActionIconController;
@@ -22,16 +23,15 @@
 class PageActionIconContainerView : public views::View,
                                     public PageActionIconContainer {
  public:
+  METADATA_HEADER(PageActionIconContainerView);
   explicit PageActionIconContainerView(const PageActionIconParams& params);
+  PageActionIconContainerView(const PageActionIconContainerView&) = delete;
+  PageActionIconContainerView& operator=(const PageActionIconContainerView&) =
+      delete;
   ~PageActionIconContainerView() override;
 
   PageActionIconController* controller() { return controller_.get(); }
 
-  // views::View:
-  const char* GetClassName() const override;
-
-  static const char kPageActionIconContainerViewClassName[];
-
  private:
   // views::View:
   void ChildPreferredSizeChanged(views::View* child) override;
@@ -40,8 +40,6 @@
   void AddPageActionIcon(views::View* icon) override;
 
   std::unique_ptr<PageActionIconController> controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(PageActionIconContainerView);
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_PAGE_ACTION_PAGE_ACTION_ICON_CONTAINER_H_
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.cc b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
index 7b363dc6..9aa36d6c 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_view.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
@@ -24,6 +24,7 @@
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/controls/button/button_controller.h"
 #include "ui/views/controls/focus_ring.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 #include "ui/views/style/platform_style.h"
 
 float PageActionIconView::Delegate::GetPageActionInkDropVisibleOpacity() const {
@@ -181,13 +182,16 @@
   IconLabelBubbleView::OnTouchUiChanged();
 }
 
-const char* PageActionIconView::GetClassName() const {
-  return "PageActionIconView";
-}
-
 void PageActionIconView::SetIconColor(SkColor icon_color) {
+  if (icon_color_ == icon_color)
+    return;
   icon_color_ = icon_color;
   UpdateIconImage();
+  OnPropertyChanged(&icon_color_, views::kPropertyEffectsNone);
+}
+
+SkColor PageActionIconView::GetIconColor() const {
+  return icon_color_;
 }
 
 void PageActionIconView::SetActive(bool active) {
@@ -195,6 +199,11 @@
     return;
   active_ = active;
   UpdateIconImage();
+  OnPropertyChanged(&active_, views::kPropertyEffectsNone);
+}
+
+bool PageActionIconView::GetActive() const {
+  return active_;
 }
 
 void PageActionIconView::Update() {
@@ -247,3 +256,8 @@
   if (new_insets != GetInsets())
     SetBorder(views::CreateEmptyBorder(new_insets));
 }
+
+BEGIN_METADATA(PageActionIconView, IconLabelBubbleView)
+ADD_PROPERTY_METADATA(SkColor, IconColor)
+ADD_PROPERTY_METADATA(bool, Active)
+END_METADATA
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.h b/chrome/browser/ui/views/page_action/page_action_icon_view.h
index b2c3c05..02f108c 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_view.h
+++ b/chrome/browser/ui/views/page_action/page_action_icon_view.h
@@ -7,7 +7,6 @@
 
 #include <memory>
 
-#include "base/macros.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/omnibox/omnibox_theme.h"
 #include "chrome/browser/ui/views/location_bar/icon_label_bubble_view.h"
@@ -16,6 +15,7 @@
 #include "ui/gfx/image/image_skia.h"
 #include "ui/views/animation/ink_drop_host_view.h"
 #include "ui/views/controls/image_view.h"
+#include "ui/views/metadata/metadata_header_macros.h"
 
 class CommandUpdater;
 class OmniboxView;
@@ -37,6 +37,8 @@
 // shows a bubble when clicked.
 class PageActionIconView : public IconLabelBubbleView {
  public:
+  METADATA_HEADER(PageActionIconView);
+
   class Delegate {
    public:
     // Gets the opacity to use for the ink highlight.
@@ -56,15 +58,18 @@
     virtual const OmniboxView* GetOmniboxView() const;
   };
 
+  PageActionIconView(const PageActionIconView&) = delete;
+  PageActionIconView& operator=(const PageActionIconView&) = delete;
   ~PageActionIconView() override;
 
   // Updates the color of the icon, this must be set before the icon is drawn.
   void SetIconColor(SkColor icon_color);
+  SkColor GetIconColor() const;
 
   // Sets the active state of the icon. An active icon will be displayed in a
   // "call to action" color.
   void SetActive(bool active);
-  bool active() const { return active_; }
+  bool GetActive() const;
 
   // Hide the icon on user input in progress and invokes UpdateImpl().
   void Update();
@@ -135,7 +140,6 @@
 
   // IconLabelBubbleView:
   void OnTouchUiChanged() override;
-  const char* GetClassName() const override;
 
   // Updates the icon image after some state has changed.
   virtual void UpdateIconImage();
@@ -181,8 +185,6 @@
 
   // The loading indicator, showing a throbber animation on top of the icon.
   PageActionIconLoadingIndicatorView* loading_indicator_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(PageActionIconView);
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_PAGE_ACTION_PAGE_ACTION_ICON_VIEW_H_
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view.cc b/chrome/browser/ui/views/page_action/pwa_install_view.cc
index 07f5f0f..8cd6e0f 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view.cc
@@ -14,7 +14,6 @@
 #include "base/time/time.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/banners/app_banner_manager.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/user_education/feature_promo_controller.h"
 #include "chrome/browser/ui/user_education/feature_promo_text_replacements.h"
@@ -28,8 +27,10 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/omnibox/browser/vector_icons.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/webapps/installable/installable_metrics.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 
 namespace {
 
@@ -188,10 +189,6 @@
       webapps::AppBannerManager::GetInstallableWebAppName(web_contents));
 }
 
-const char* PwaInstallView::GetClassName() const {
-  return "PwaInstallView";
-}
-
 bool PwaInstallView::ShouldShowIph(content::WebContents* web_contents,
                                    webapps::AppBannerManager* manager) {
   auto start_url = manager->GetManifestStartUrl();
@@ -207,3 +204,6 @@
   return score > kIphSiteEngagementThresholdParam.Get() &&
          web_app::ShouldShowIph(profile->GetPrefs(), app_id);
 }
+
+BEGIN_METADATA(PwaInstallView, PageActionIconView)
+END_METADATA
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view.h b/chrome/browser/ui/views/page_action/pwa_install_view.h
index 3a07455..8e59282 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view.h
+++ b/chrome/browser/ui/views/page_action/pwa_install_view.h
@@ -5,8 +5,8 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_PAGE_ACTION_PWA_INSTALL_VIEW_H_
 #define CHROME_BROWSER_UI_VIEWS_PAGE_ACTION_PWA_INSTALL_VIEW_H_
 
-#include "base/macros.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
+#include "ui/views/metadata/metadata_header_macros.h"
 
 namespace webapps {
 class AppBannerManager;
@@ -16,10 +16,13 @@
 // installability checks and can be installed.
 class PwaInstallView : public PageActionIconView {
  public:
+  METADATA_HEADER(PwaInstallView);
   explicit PwaInstallView(
       CommandUpdater* command_updater,
       IconLabelBubbleView::Delegate* icon_label_bubble_delegate,
       PageActionIconView::Delegate* page_action_icon_delegate);
+  PwaInstallView(const PwaInstallView&) = delete;
+  PwaInstallView& operator=(const PwaInstallView&) = delete;
   ~PwaInstallView() override;
 
  protected:
@@ -29,7 +32,6 @@
   views::BubbleDialogDelegate* GetBubble() const override;
   const gfx::VectorIcon& GetVectorIcon() const override;
   base::string16 GetTextForTooltipAndAccessibleName() const override;
-  const char* GetClassName() const override;
 
  private:
   // Called when IPH is closed.
@@ -43,8 +45,6 @@
                      webapps::AppBannerManager* manager);
 
   base::WeakPtrFactory<PwaInstallView> weak_ptr_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(PwaInstallView);
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_PAGE_ACTION_PWA_INSTALL_VIEW_H_
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
index c876d10..8e6d6e3 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
@@ -11,7 +11,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/banners/test_app_banner_manager_desktop.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_dialogs.h"
@@ -36,6 +35,7 @@
 #include "components/feature_engagement/public/feature_list.h"
 #include "components/omnibox/browser/omnibox_popup_model.h"
 #include "components/omnibox/browser/omnibox_view.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/webapps/installable/installable_metrics.h"
 #include "content/public/common/referrer.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ui/views/page_action/zoom_view.cc b/chrome/browser/ui/views/page_action/zoom_view.cc
index 07ef9946..481e3fa 100644
--- a/chrome/browser/ui/views/page_action/zoom_view.cc
+++ b/chrome/browser/ui/views/page_action/zoom_view.cc
@@ -15,6 +15,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/events/event.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 
 ZoomView::ZoomView(IconLabelBubbleView::Delegate* icon_label_bubble_delegate,
                    PageActionIconView::Delegate* page_action_icon_delegate)
@@ -113,6 +114,5 @@
                                     base::FormatPercent(current_zoom_percent_));
 }
 
-const char* ZoomView::GetClassName() const {
-  return "ZoomView";
-}
+BEGIN_METADATA(ZoomView, PageActionIconView)
+END_METADATA
diff --git a/chrome/browser/ui/views/page_action/zoom_view.h b/chrome/browser/ui/views/page_action/zoom_view.h
index d7bc4f3c..5489307 100644
--- a/chrome/browser/ui/views/page_action/zoom_view.h
+++ b/chrome/browser/ui/views/page_action/zoom_view.h
@@ -5,18 +5,21 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_PAGE_ACTION_ZOOM_VIEW_H_
 #define CHROME_BROWSER_UI_VIEWS_PAGE_ACTION_ZOOM_VIEW_H_
 
-#include "base/macros.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
+#include "ui/views/metadata/metadata_header_macros.h"
 
 // View for the zoom icon in the Omnibox.
 class ZoomView : public PageActionIconView {
  public:
+  METADATA_HEADER(ZoomView);
   // Clicking on the ZoomView shows a ZoomBubbleView, which requires the current
   // WebContents. Because the current WebContents changes as the user switches
   // tabs, a LocationBarView::Delegate is supplied to queried for the current
   // WebContents when needed.
   ZoomView(IconLabelBubbleView::Delegate* icon_label_bubble_delegate,
            PageActionIconView::Delegate* page_action_icon_delegate);
+  ZoomView(const ZoomView&) = delete;
+  ZoomView& operator=(const ZoomView&) = delete;
   ~ZoomView() override;
 
   // Updates the image and its tooltip appropriately, hiding or showing the icon
@@ -30,7 +33,6 @@
   views::BubbleDialogDelegate* GetBubble() const override;
   const gfx::VectorIcon& GetVectorIcon() const override;
   base::string16 GetTextForTooltipAndAccessibleName() const override;
-  const char* GetClassName() const override;
 
  private:
   bool ShouldBeVisible(bool can_show_bubble) const;
@@ -39,8 +41,6 @@
   const gfx::VectorIcon* icon_ = nullptr;
 
   int current_zoom_percent_ = 100;
-
-  DISALLOW_COPY_AND_ASSIGN(ZoomView);
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_PAGE_ACTION_ZOOM_VIEW_H_
diff --git a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
index ecf5e225..94e32b4 100644
--- a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
@@ -13,7 +13,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/history_test_utils.h"
 #include "chrome/browser/reputation/reputation_service.h"
@@ -39,6 +38,7 @@
 #include "components/security_state/core/features.h"
 #include "components/security_state/core/security_state.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/strings/grit/components_chromium_strings.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/ukm/test_ukm_recorder.h"
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
index d16b5e3c..bd5b9f28 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
@@ -193,7 +193,7 @@
 }
 
 bool AvatarToolbarButton::IsParentHighlighted() const {
-  return parent_ && parent_->IsHighlighted();
+  return parent_ && parent_->GetHighlighted();
 }
 
 void AvatarToolbarButton::AddObserver(Observer* observer) {
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.cc b/chrome/browser/ui/views/profiles/profile_picker_view.cc
index a1fe350..8aa69b0c 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.cc
@@ -464,11 +464,6 @@
   // browser crashes before finishing the flow.
   entry->SetIsEphemeral(true);
 
-  // TODO(crbug.com/1126913): Record also that we show the sign-in promo
-  // (it has to be plumbed from js to profile_picker_handler.cc):
-  //   signin_metrics::RecordSigninImpressionUserActionForAccessPoint(
-  //       signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER);
-
   // Record that the sign in process starts (its end is recorded automatically
   // by the instance of DiceTurnSyncOnHelper constructed later on).
   signin_metrics::RecordSigninUserActionForAccessPoint(
diff --git a/chrome/browser/ui/views/reader_mode/reader_mode_icon_view.cc b/chrome/browser/ui/views/reader_mode/reader_mode_icon_view.cc
index 01fb8b9..5c4d8ce 100644
--- a/chrome/browser/ui/views/reader_mode/reader_mode_icon_view.cc
+++ b/chrome/browser/ui/views/reader_mode/reader_mode_icon_view.cc
@@ -126,12 +126,12 @@
 }
 
 const gfx::VectorIcon& ReaderModeIconView::GetVectorIcon() const {
-  return active() ? kReaderModeIcon : kReaderModeDisabledIcon;
+  return GetActive() ? kReaderModeIcon : kReaderModeDisabledIcon;
 }
 
 base::string16 ReaderModeIconView::GetTextForTooltipAndAccessibleName() const {
-  return l10n_util::GetStringUTF16(active() ? IDS_EXIT_DISTILLED_PAGE
-                                            : IDS_DISTILL_PAGE);
+  return l10n_util::GetStringUTF16(GetActive() ? IDS_EXIT_DISTILLED_PAGE
+                                               : IDS_DISTILL_PAGE);
 }
 
 const char* ReaderModeIconView::GetClassName() const {
@@ -146,7 +146,7 @@
 
 void ReaderModeIconView::OnExecuting(
     PageActionIconView::ExecuteSource execute_source) {
-  if (active()) {
+  if (GetActive()) {
     dom_distiller::UMAHelper::RecordReaderModeExit(
         dom_distiller::UMAHelper::ReaderModeEntryPoint::kOmniboxIcon);
   } else {
diff --git a/chrome/browser/ui/views/sad_tab_view_interactive_uitest.cc b/chrome/browser/ui/views/sad_tab_view_interactive_uitest.cc
index ddef173..903cb26 100644
--- a/chrome/browser/ui/views/sad_tab_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/sad_tab_view_interactive_uitest.cc
@@ -98,7 +98,10 @@
   }
 
   bool IsFocusedViewInsideBrowserToolbar() {
-    return IsFocusedViewInsideViewClass(ToolbarView::kViewClassName);
+    return IsFocusedViewInsideViewClass(
+        BrowserView::GetBrowserViewForBrowser(browser())
+            ->toolbar()
+            ->GetClassName());
   }
 
   bool IsFocusedViewOnActionButtonInSadTab() {
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index 7c623cc..6105ff8 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -951,8 +951,15 @@
 // tabs joining the same group as the tab in the second position. Then dragging
 // the tabs over two to the right will result in the tabs joining the same group
 // as the last tab.
+// Flaky on windows. http://crbug.com/1164561
+#if defined(OS_WIN)
+#define MAYBE_DragMultipleTabsRightIntoGroup \
+  DISABLED_DragMultipleTabsRightIntoGroup
+#else
+#define MAYBE_DragMultipleTabsRightIntoGroup DragMultipleTabsRightIntoGroup
+#endif
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
-                       DragMultipleTabsRightIntoGroup) {
+                       MAYBE_DragMultipleTabsRightIntoGroup) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
   TabStripModel* model = browser()->tab_strip_model();
   TabGroupModel* group_model = model->group_model();
@@ -2052,7 +2059,8 @@
 
 }  // namespace
 
-#if defined(OS_MAC) /* && defined(ARCH_CPU_ARM64) */
+// Flaky. http://crbug.com/1128774
+#if defined(OS_MAC) || defined(OS_WIN)
 // Bulk-disabled for arm64 bot stabilization: https://crbug.com/1154345
 // These were flaking on all macs, so commented out ARCH_ above for
 // crbug.com/1160917 too.
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
index 5f68959..312b2f9a 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -396,12 +396,11 @@
 // Maintains a set of thumbnails to watch, ensuring the capture count on the
 // associated WebContents stays nonzero until a valid thumbnail has been
 // captured.
-class TabHoverCardBubbleView::ThumbnailObserver
-    : public ThumbnailImage::Observer {
+class TabHoverCardBubbleView::ThumbnailObserver {
  public:
   explicit ThumbnailObserver(TabHoverCardBubbleView* hover_card)
       : hover_card_(hover_card) {}
-  ~ThumbnailObserver() override = default;
+  ~ThumbnailObserver() = default;
 
   // Begin watching the specified thumbnail image for updates. Ideally, should
   // trigger the associated WebContents to load (if not loaded already) and
@@ -411,13 +410,17 @@
     if (current_image_ == thumbnail_image)
       return;
 
-    scoped_observation_.Reset();
+    subscription_.reset();
     current_image_ = std::move(thumbnail_image);
+    if (!current_image_)
+      return;
 
-    if (current_image_) {
-      scoped_observation_.Observe(current_image_.get());
-      current_image_->RequestThumbnailImage();
-    }
+    subscription_ = current_image_->Subscribe();
+    subscription_->SetSizeHint(TabStyle::GetPreviewImageSize());
+    subscription_->SetUncompressedImageCallback(base::BindRepeating(
+        &ThumbnailObserver::ThumbnailImageCallback, base::Unretained(this)));
+
+    current_image_->RequestThumbnailImage();
   }
 
   // Returns the current (most recent) thumbnail being watched.
@@ -425,18 +428,13 @@
     return current_image_;
   }
 
-  base::Optional<gfx::Size> GetThumbnailSizeHint() const override {
-    return TabStyle::GetPreviewImageSize();
-  }
-
-  void OnThumbnailImageAvailable(gfx::ImageSkia preview_image) override {
+  void ThumbnailImageCallback(gfx::ImageSkia preview_image) {
     hover_card_->OnThumbnailImageAvailable(std::move(preview_image));
   }
 
   scoped_refptr<ThumbnailImage> current_image_;
+  std::unique_ptr<ThumbnailImage::Subscription> subscription_;
   TabHoverCardBubbleView* const hover_card_;
-  base::ScopedObservation<ThumbnailImage, ThumbnailImage::Observer>
-      scoped_observation_{this};
 };
 
 TabHoverCardBubbleView::TabHoverCardBubbleView(Tab* tab)
diff --git a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
index 711a9e1..07cad2c9 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
@@ -41,6 +41,7 @@
 #include "ui/views/animation/ink_drop_highlight.h"
 #include "ui/views/animation/ink_drop_state.h"
 #include "ui/views/controls/button/label_button_border.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 #include "ui/views/metrics.h"
 #include "ui/views/view.h"
 #include "ui/views/view_class_properties.h"
@@ -172,10 +173,6 @@
   SetHighlight(text, color);
 }
 
-const char* BrowserAppMenuButton::GetClassName() const {
-  return "BrowserAppMenuButton";
-}
-
 bool BrowserAppMenuButton::GetDropFormats(
     int* formats,
     std::set<ui::ClipboardFormatType>* format_types) {
@@ -231,3 +228,6 @@
   UpdateColorsAndInsets();
   PreferredSizeChanged();
 }
+
+BEGIN_METADATA(BrowserAppMenuButton, AppMenuButton)
+END_METADATA
diff --git a/chrome/browser/ui/views/toolbar/browser_app_menu_button.h b/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
index 7dcc240..32add0f9 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/toolbar/app_menu_icon_controller.h"
 #include "chrome/browser/ui/user_education/feature_promo_controller.h"
 #include "chrome/browser/ui/views/frame/app_menu_button.h"
+#include "ui/views/metadata/metadata_header_macros.h"
 #include "ui/views/view.h"
 
 class ToolbarView;
@@ -23,6 +24,7 @@
 // windows, which is implemented in WebAppMenuButton).
 class BrowserAppMenuButton : public AppMenuButton {
  public:
+  METADATA_HEADER(BrowserAppMenuButton);
   BrowserAppMenuButton(PressedCallback callback, ToolbarView* toolbar_view);
   BrowserAppMenuButton(const BrowserAppMenuButton&) = delete;
   BrowserAppMenuButton& operator=(const BrowserAppMenuButton&) = delete;
@@ -31,10 +33,6 @@
   void SetTypeAndSeverity(
       AppMenuIconController::TypeAndSeverity type_and_severity);
 
-  AppMenuIconController::Severity severity() {
-    return type_and_severity_.severity;
-  }
-
   // Shows the app menu. |run_types| denotes the MenuRunner::RunTypes associated
   // with the menu.
   void ShowMenu(int run_types);
@@ -44,7 +42,6 @@
   static bool g_open_app_immediately_for_testing;
 
   // AppMenuButton:
-  const char* GetClassName() const override;
   bool GetDropFormats(int* formats,
                       std::set<ui::ClipboardFormatType>* format_types) override;
   bool AreDropTypesRequired() override;
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc
index b3d6561..28ab871 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc
@@ -10,9 +10,11 @@
 #include "chrome/browser/flag_descriptions.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/ui/webui/flags/flags_ui.h"
+#include "chrome/common/channel_info.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/google_chrome_strings.h"
 #include "components/flags_ui/pref_service_flags_storage.h"
+#include "components/version_info/channel.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/views/background.h"
@@ -179,7 +181,8 @@
   for (const auto& lab : all_labs) {
     const flags_ui::FeatureEntry* entry =
         flags_state_->FindFeatureEntryByName(lab.internal_name);
-    if (IsFeatureSupportedOnPlatform(entry)) {
+    if (IsFeatureSupportedOnChannel(lab) &&
+        IsFeatureSupportedOnPlatform(entry)) {
       DCHECK_EQ(entry->type, flags_ui::FeatureEntry::FEATURE_VALUE);
       int default_index = GetIndexOfEnabledLabState(entry);
       menu_item_container_->AddChildView(
@@ -235,6 +238,10 @@
   return 0;
 }
 
+bool ChromeLabsBubbleView::IsFeatureSupportedOnChannel(const LabInfo& lab) {
+  return chrome::GetChannel() <= lab.allowed_channel;
+}
+
 // TODO(elainechien): ChromeOS specific logic for owner access only flags.
 bool ChromeLabsBubbleView::IsFeatureSupportedOnPlatform(
     const flags_ui::FeatureEntry* entry) {
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h
index eb37d5b..eca81ab 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h
@@ -43,6 +43,8 @@
 
   int GetIndexOfEnabledLabState(const flags_ui::FeatureEntry* entry);
 
+  bool IsFeatureSupportedOnChannel(const LabInfo& lab);
+
   bool IsFeatureSupportedOnPlatform(const flags_ui::FeatureEntry* entry);
 
   void ShowRelaunchPrompt();
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc
index 163c4d6..b760a002 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc
@@ -6,6 +6,19 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/flag_descriptions.h"
 
+LabInfo::LabInfo(const std::string& internal_name,
+                 const base::string16& visible_name,
+                 const base::string16& visible_description,
+                 version_info::Channel allowed_channel)
+    : internal_name(internal_name),
+      visible_name(visible_name),
+      visible_description(visible_description),
+      allowed_channel(allowed_channel) {}
+
+LabInfo::LabInfo(const LabInfo& other) = default;
+
+LabInfo::~LabInfo() = default;
+
 ChromeLabsBubbleViewModel::ChromeLabsBubbleViewModel() {
   SetUpLabs();
 }
@@ -25,16 +38,18 @@
   // Read Later.
   lab_info_.emplace_back(LabInfo(
       flag_descriptions::kReadLaterFlagId, base::ASCIIToUTF16("Reading List"),
-      base::ASCIIToUTF16(
-          "Right click on a tab or click the star to add tabs to a reading "
-          "list. Access from the Bookmarks bar.")));
+      base::ASCIIToUTF16("Right click on a tab or click the Bookmark icon to "
+                         "add tabs to a reading "
+                         "list. Access from the Bookmarks bar."),
+      version_info::Channel::BETA));
 
   // Tab Search.
   lab_info_.emplace_back(
       LabInfo(flag_descriptions::kEnableTabSearchFlagId,
               base::ASCIIToUTF16("Tab Search"),
               base::ASCIIToUTF16("Enable a popup bubble in Top Chrome UI to "
-                                 "search over currently open tabs.")));
+                                 "search over currently open tabs."),
+              version_info::Channel::BETA));
 }
 
 void ChromeLabsBubbleViewModel::SetLabInfoForTesting(
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h
index 62056cdc..fa5f953 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h
@@ -7,21 +7,26 @@
 
 #include <vector>
 #include "base/strings/string16.h"
+#include "components/version_info/channel.h"
 
 // Currently there are differences in both visible name and visible description
 // between about_flags and what we want for Chrome Labs. We are coordinating to
-// match these. LabInfo struct can be removed after that.
+// match these. Visible name and visible description can be removed from this
+// struct after that.
 struct LabInfo {
   LabInfo(const std::string& internal_name,
           const base::string16& visible_name,
-          const base::string16& visible_description)
-      : internal_name(internal_name),
-        visible_name(visible_name),
-        visible_description(visible_description) {}
-
+          const base::string16& visible_description,
+          version_info::Channel allowed_channel);
+  LabInfo(const LabInfo& other);
+  ~LabInfo();
   std::string internal_name;
   base::string16 visible_name;
   base::string16 visible_description;
+  // Channels that are less stable than allowed_channel will also be
+  // considered allowed. ex) if BETA is specified, this feature will also be
+  // shown on CANARY and DEV.
+  version_info::Channel allowed_channel;
 };
 
 class ChromeLabsBubbleViewModel {
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 99a9c30c..02d8a152 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
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "components/flags_ui/feature_entry_macros.h"
 #include "components/flags_ui/flags_state.h"
+#include "components/version_info/channel.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/views/test/combobox_test_api.h"
 #include "ui/views/test/widget_test.h"
@@ -67,14 +68,17 @@
   }
 
   void CreateTestLabInfo() {
-    test_feature_info_.emplace_back(LabInfo(
-        kFirstTestFeatureId, base::ASCIIToUTF16(""), base::ASCIIToUTF16("")));
+    test_feature_info_.emplace_back(
+        LabInfo(kFirstTestFeatureId, base::ASCIIToUTF16(""),
+                base::ASCIIToUTF16(""), version_info::Channel::UNKNOWN));
 
-    test_feature_info_.emplace_back(LabInfo(
-        kSecondTestFeatureId, base::ASCIIToUTF16(""), base::ASCIIToUTF16("")));
+    test_feature_info_.emplace_back(
+        LabInfo(kSecondTestFeatureId, base::ASCIIToUTF16(""),
+                base::ASCIIToUTF16(""), version_info::Channel::UNKNOWN));
 
-    test_feature_info_.emplace_back(LabInfo(
-        kThirdTestFeatureId, base::ASCIIToUTF16(""), base::ASCIIToUTF16("")));
+    test_feature_info_.emplace_back(
+        LabInfo(kThirdTestFeatureId, base::ASCIIToUTF16(""),
+                base::ASCIIToUTF16(""), version_info::Channel::UNKNOWN));
   }
 
   const std::vector<LabInfo>& GetTestLabInfo() { return test_feature_info_; }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
index 6f07672..a1e9dec 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
@@ -21,14 +21,10 @@
 #include "ui/gfx/color_palette.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 #include "ui/views/view_class_properties.h"
 #include "ui/views/widget/widget.h"
 
-// static
-const char ToolbarAccountIconContainerView::
-    kToolbarAccountIconContainerViewClassName[] =
-        "ToolbarAccountIconContainerView";
-
 ToolbarAccountIconContainerView::ToolbarAccountIconContainerView(
     Browser* browser)
     : ToolbarIconContainerView(
@@ -107,12 +103,11 @@
   UpdateAllIcons();
 }
 
-const char* ToolbarAccountIconContainerView::GetClassName() const {
-  return kToolbarAccountIconContainerViewClassName;
-}
-
 void ToolbarAccountIconContainerView::AddPageActionIcon(views::View* icon) {
   // Add the page action icons to the end of the container, just before the
   // avatar icon.
   AddChildViewAt(icon, GetIndexOf(avatar_));
 }
+
+BEGIN_METADATA(ToolbarAccountIconContainerView, ToolbarIconContainerView)
+END_METADATA
diff --git a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h
index 711c693a..3462707 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h
@@ -9,6 +9,7 @@
 #include "chrome/browser/ui/views/page_action/page_action_icon_container.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h"
+#include "ui/views/metadata/metadata_header_macros.h"
 
 class AvatarToolbarButton;
 class Browser;
@@ -21,6 +22,7 @@
                                         public PageActionIconContainer,
                                         public PageActionIconView::Delegate {
  public:
+  METADATA_HEADER(ToolbarAccountIconContainerView);
   explicit ToolbarAccountIconContainerView(Browser* browser);
   ToolbarAccountIconContainerView(const ToolbarAccountIconContainerView&) =
       delete;
@@ -44,15 +46,12 @@
 
   // views::View:
   void OnThemeChanged() override;
-  const char* GetClassName() const override;
 
   PageActionIconController* page_action_icon_controller() {
     return page_action_icon_controller_.get();
   }
   AvatarToolbarButton* avatar_button() { return avatar_; }
 
-  static const char kToolbarAccountIconContainerViewClassName[];
-
  private:
   // PageActionIconContainer:
   void AddPageActionIcon(views::View* icon) override;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
index f2aa855..2fe7667d 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
@@ -37,6 +37,7 @@
 #include "ui/views/controls/menu/menu_controller.h"
 #include "ui/views/controls/menu/menu_model_adapter.h"
 #include "ui/views/controls/menu/menu_runner.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 #include "ui/views/mouse_constants.h"
 
 using views::LabelButtonBorder;
@@ -51,8 +52,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 // ToolbarActionView
 
-const char ToolbarActionView::kClassName[] = "ToolbarActionView";
-
 ToolbarActionView::ToolbarActionView(
     ToolbarActionViewController* view_controller,
     ToolbarActionView::Delegate* delegate)
@@ -89,10 +88,6 @@
   view_controller_->SetDelegate(nullptr);
 }
 
-const char* ToolbarActionView::GetClassName() const {
-  return kClassName;
-}
-
 gfx::Rect ToolbarActionView::GetAnchorBoundsInScreen() const {
   gfx::Rect bounds = GetBoundsInScreen();
   bounds.Inset(GetToolbarInkDropInsets(this));
@@ -320,3 +315,6 @@
                                                       ui::MENU_SOURCE_NONE);
   }
 }
+
+BEGIN_METADATA(ToolbarActionView, views::MenuButton)
+END_METADATA
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.h b/chrome/browser/ui/views/toolbar/toolbar_action_view.h
index 20430c2..ebfacfa 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.h
@@ -12,6 +12,7 @@
 #include "ui/views/controls/button/menu_button_controller.h"
 #include "ui/views/controls/menu/menu_model_adapter.h"
 #include "ui/views/drag_controller.h"
+#include "ui/views/metadata/metadata_header_macros.h"
 #include "ui/views/view.h"
 
 class ExtensionContextMenuController;
@@ -23,6 +24,8 @@
 class ToolbarActionView : public views::MenuButton,
                           public ToolbarActionViewDelegateViews {
  public:
+  METADATA_HEADER(ToolbarActionView);
+
   // Need DragController here because ToolbarActionView could be
   // dragged/dropped.
   class Delegate : public views::DragController {
@@ -83,11 +86,9 @@
   ExtensionContextMenuController* context_menu_controller_for_testing() const {
     return context_menu_controller_.get();
   }
-  static const char kClassName[];
 
  private:
   // views::MenuButton:
-  const char* GetClassName() const override;
   gfx::Size CalculatePreferredSize() const override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
   void OnMouseReleased(const ui::MouseEvent& event) override;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
index 526f635c..3f4b12c 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
@@ -20,13 +20,10 @@
 #include "ui/views/background.h"
 #include "ui/views/layout/animating_layout_manager.h"
 #include "ui/views/layout/flex_layout.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 #include "ui/views/view_class_properties.h"
 #include "ui/views/view_observer.h"
 
-// static
-const char ToolbarIconContainerView::kToolbarIconContainerViewClassName[] =
-    "ToolbarIconContainerView";
-
 ToolbarIconContainerView::RoundRectBorder::RoundRectBorder(views::View* parent)
     : parent_(parent) {
   layer_.set_delegate(this);
@@ -92,8 +89,10 @@
     const bool is_collapsed = observed_view->bounds().IsEmpty();
     if (is_collapsed != was_collapsed_) {
       was_collapsed_ = is_collapsed;
-      if (!is_collapsed)
-        toolbar_icon_container_view_->animating_layout_manager()->ResetLayout();
+      if (!is_collapsed) {
+        toolbar_icon_container_view_->GetAnimatingLayoutManager()
+            ->ResetLayout();
+      }
     }
   }
 
@@ -159,20 +158,45 @@
   observers_.RemoveObserver(obs);
 }
 
-void ToolbarIconContainerView::OverrideIconColor(SkColor color) {
+void ToolbarIconContainerView::SetIconColor(SkColor color) {
+  if (icon_color_ == color)
+    return;
   icon_color_ = color;
   UpdateAllIcons();
+  OnPropertyChanged(&icon_color_, views::kPropertyEffectsNone);
 }
 
 SkColor ToolbarIconContainerView::GetIconColor() const {
-  if (icon_color_)
-    return icon_color_.value();
-  return GetThemeProvider()->GetColor(
-      ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON);
+  return icon_color_.value_or(
+      GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON));
 }
 
-bool ToolbarIconContainerView::IsHighlighted() {
-  return ShouldDisplayHighlight();
+bool ToolbarIconContainerView::GetHighlighted() const {
+  if (!uses_highlight_)
+    return false;
+
+  if (IsMouseHovered() && (!main_button_ || !main_button_->IsMouseHovered()))
+    return true;
+
+  // Focused, pressed or hovered children should trigger the highlight.
+  for (const views::View* child : children()) {
+    if (child == main_button_)
+      continue;
+    if (child->HasFocus())
+      return true;
+    const views::Button* button = views::Button::AsButton(child);
+    if (!button)
+      continue;
+    if (button->GetState() == views::Button::ButtonState::STATE_PRESSED ||
+        button->GetState() == views::Button::ButtonState::STATE_HOVERED) {
+      return true;
+    }
+    // The container should also be highlighted if a dialog is anchored to.
+    if (base::Contains(highlighted_buttons_, button))
+      return true;
+  }
+
+  return false;
 }
 
 void ToolbarIconContainerView::OnViewFocused(views::View* observed_view) {
@@ -183,6 +207,21 @@
   UpdateHighlight();
 }
 
+views::AnimatingLayoutManager*
+ToolbarIconContainerView::GetAnimatingLayoutManager() {
+  return static_cast<views::AnimatingLayoutManager*>(GetLayoutManager());
+}
+
+const views::AnimatingLayoutManager*
+ToolbarIconContainerView::GetAnimatingLayoutManager() const {
+  return static_cast<const views::AnimatingLayoutManager*>(GetLayoutManager());
+}
+
+views::FlexLayout* ToolbarIconContainerView::GetTargetLayoutManager() {
+  return static_cast<views::FlexLayout*>(
+      GetAnimatingLayoutManager()->target_layout_manager());
+}
+
 void ToolbarIconContainerView::OnBoundsChanged(
     const gfx::Rect& previous_bounds) {
   const gfx::Rect bounds = ConvertRectToWidget(GetLocalBounds());
@@ -198,50 +237,18 @@
   UpdateHighlight();
 }
 
-const char* ToolbarIconContainerView::GetClassName() const {
-  return kToolbarIconContainerViewClassName;
-}
-
 void ToolbarIconContainerView::AddedToWidget() {
   // Add an observer to reset the animation if the browser window is restored,
   // preventing spurious animation. (See crbug.com/1106506)
   restore_observer_ = std::make_unique<WidgetRestoreObserver>(this);
 }
 
-bool ToolbarIconContainerView::ShouldDisplayHighlight() {
-  if (!uses_highlight_)
-    return false;
-
-  if (IsMouseHovered() && (!main_button_ || !main_button_->IsMouseHovered()))
-    return true;
-
-  // Focused, pressed or hovered children should trigger the highlight.
-  for (views::View* child : children()) {
-    if (child == main_button_)
-      continue;
-    if (child->HasFocus())
-      return true;
-    views::Button* button = views::Button::AsButton(child);
-    if (!button)
-      continue;
-    if (button->GetState() == views::Button::ButtonState::STATE_PRESSED ||
-        button->GetState() == views::Button::ButtonState::STATE_HOVERED) {
-      return true;
-    }
-    // The container should also be highlighted if a dialog is anchored to.
-    if (base::Contains(highlighted_buttons_, button))
-      return true;
-  }
-
-  return false;
-}
-
 void ToolbarIconContainerView::UpdateHighlight() {
   bool showing_before = border_.layer()->GetTargetOpacity() == 1;
 
   {
     ui::ScopedLayerAnimationSettings settings(border_.layer()->GetAnimator());
-    border_.layer()->SetOpacity(ShouldDisplayHighlight() ? 1 : 0);
+    border_.layer()->SetOpacity(GetHighlighted() ? 1 : 0);
   }
 
   if (showing_before == (border_.layer()->GetTargetOpacity() == 1))
@@ -259,3 +266,8 @@
 
   UpdateHighlight();
 }
+
+BEGIN_METADATA(ToolbarIconContainerView, views::View)
+ADD_PROPERTY_METADATA(SkColor, IconColor)
+ADD_READONLY_PROPERTY_METADATA(bool, Highlighted)
+END_METADATA
diff --git a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
index 6ef2ccfd..1c7a561 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
@@ -12,12 +12,15 @@
 #include "ui/views/controls/button/button.h"
 #include "ui/views/layout/animating_layout_manager.h"
 #include "ui/views/layout/flex_layout.h"
+#include "ui/views/metadata/metadata_header_macros.h"
 #include "ui/views/view.h"
 
 // A general view container for any type of toolbar icons.
 class ToolbarIconContainerView : public views::View,
                                  public views::ViewObserver {
  public:
+  METADATA_HEADER(ToolbarIconContainerView);
+
   class Observer : public base::CheckedObserver {
    public:
     virtual void OnHighlightChanged() = 0;
@@ -41,34 +44,23 @@
   void AddObserver(Observer* obs);
   void RemoveObserver(const Observer* obs);
 
-  void OverrideIconColor(SkColor icon_color);
+  void SetIconColor(SkColor icon_color);
   SkColor GetIconColor() const;
 
-  bool IsHighlighted();
+  bool GetHighlighted() const;
 
   // views::ViewObserver:
   void OnViewFocused(views::View* observed_view) override;
   void OnViewBlurred(views::View* observed_view) override;
 
-  bool uses_highlight() { return uses_highlight_; }
+  bool uses_highlight() const { return uses_highlight_; }
 
   // Provides access to the animating layout manager for subclasses.
-  views::AnimatingLayoutManager* animating_layout_manager() {
-    return static_cast<views::AnimatingLayoutManager*>(GetLayoutManager());
-  }
-
-  const views::AnimatingLayoutManager* animating_layout_manager() const {
-    return static_cast<const views::AnimatingLayoutManager*>(
-        GetLayoutManager());
-  }
+  views::AnimatingLayoutManager* GetAnimatingLayoutManager();
+  const views::AnimatingLayoutManager* GetAnimatingLayoutManager() const;
 
   // Provides access to the flex layout in the animating layout manager.
-  views::FlexLayout* target_layout_manager() {
-    return static_cast<views::FlexLayout*>(
-        animating_layout_manager()->target_layout_manager());
-  }
-
-  static const char kToolbarIconContainerViewClassName[];
+  views::FlexLayout* GetTargetLayoutManager();
 
  protected:
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
@@ -100,10 +92,8 @@
   // views::View:
   void OnMouseEntered(const ui::MouseEvent& event) override;
   void OnMouseExited(const ui::MouseEvent& event) override;
-  const char* GetClassName() const override;
   void AddedToWidget() override;
 
-  bool ShouldDisplayHighlight();
   void UpdateHighlight();
 
   // Called by |button| when its ink drop highlighted state changes.
@@ -123,7 +113,7 @@
   // Points to the child buttons that we know are currently highlighted.
   // TODO(pbos): Consider observing buttons leaving our hierarchy and removing
   // them from this set.
-  std::set<views::Button*> highlighted_buttons_;
+  std::set<const views::Button*> highlighted_buttons_;
 
   RoundRectBorder border_{this};
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index 14858fc..a26a057 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -83,6 +83,7 @@
 #include "ui/native_theme/native_theme_aura.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/flex_layout.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 #include "ui/views/view_class_properties.h"
 #include "ui/views/widget/tooltip_manager.h"
 #include "ui/views/widget/widget.h"
@@ -143,9 +144,6 @@
 
 }  // namespace
 
-// static
-const char ToolbarView::kViewClassName[] = "ToolbarView";
-
 ////////////////////////////////////////////////////////////////////////////////
 // ToolbarView, public:
 
@@ -413,7 +411,7 @@
     SetPaneFocus(app_menu_button_);
 }
 
-bool ToolbarView::IsAppMenuFocused() {
+bool ToolbarView::GetAppMenuFocused() const {
   return app_menu_button_ && app_menu_button_->HasFocus();
 }
 
@@ -635,10 +633,6 @@
   SchedulePaint();
 }
 
-const char* ToolbarView::GetClassName() const {
-  return kViewClassName;
-}
-
 bool ToolbarView::AcceleratorPressed(const ui::Accelerator& accelerator) {
   const views::View* focused_view = focus_manager()->GetFocusedView();
   if (focused_view && (focused_view->GetID() == VIEW_ID_OMNIBOX))
@@ -704,7 +698,7 @@
   } else if (extensions_container_) {
     const views::FlexSpecification extensions_flex_rule =
         views::FlexSpecification(
-            extensions_container_->animating_layout_manager()
+            extensions_container_->GetAnimatingLayoutManager()
                 ->GetDefaultFlexRule())
             .WithOrder(kExtensionsFlexOrder);
 
@@ -950,3 +944,7 @@
                                  ? views::MenuRunner::SHOULD_SHOW_MNEMONICS
                                  : views::MenuRunner::NO_FLAGS);
 }
+
+BEGIN_METADATA(ToolbarView, views::AccessiblePaneView)
+ADD_READONLY_PROPERTY_METADATA(bool, AppMenuFocused)
+END_METADATA
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.h b/chrome/browser/ui/views/toolbar/toolbar_view.h
index 009d66a..b49d005 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.h
@@ -8,7 +8,6 @@
 #include <memory>
 #include <vector>
 
-#include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/optional.h"
 #include "base/scoped_observation.h"
@@ -31,6 +30,7 @@
 #include "ui/views/accessible_pane_view.h"
 #include "ui/views/animation/animation_delegate_views.h"
 #include "ui/views/controls/button/menu_button.h"
+#include "ui/views/metadata/metadata_header_macros.h"
 #include "ui/views/view.h"
 #include "url/origin.h"
 
@@ -77,6 +77,8 @@
                     public ToolbarButtonProvider,
                     public BrowserRootView::DropTarget {
  public:
+  METADATA_HEADER(ToolbarView);
+
   // Types of display mode this toolbar can have.
   enum class DisplayMode {
     NORMAL,     // Normal toolbar with buttons, etc.
@@ -86,10 +88,9 @@
                 // needs to be displayed.
   };
 
-  // The view class name.
-  static const char kViewClassName[];
-
-  explicit ToolbarView(Browser* browser, BrowserView* browser_view);
+  ToolbarView(Browser* browser, BrowserView* browser_view);
+  ToolbarView(const ToolbarView&) = delete;
+  ToolbarView& operator=(const ToolbarView&) = delete;
   ~ToolbarView() override;
 
   // Create the contents of the Browser Toolbar.
@@ -118,7 +119,7 @@
   void SetPaneFocusAndFocusAppMenu();
 
   // Returns true if the app menu is focused.
-  bool IsAppMenuFocused();
+  bool GetAppMenuFocused() const;
 
   void ShowIntentPickerBubble(
       std::vector<IntentPickerBubbleView::AppInfo> app_info,
@@ -188,7 +189,6 @@
   gfx::Size GetMinimumSize() const override;
   void Layout() override;
   void OnThemeChanged() override;
-  const char* GetClassName() const override;
   bool AcceleratorPressed(const ui::Accelerator& acc) override;
   void ChildPreferredSizeChanged(views::View* child) override;
 
@@ -297,8 +297,6 @@
 
   // Whether this toolbar has been initialized.
   bool initialized_ = false;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(ToolbarView);
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_TOOLBAR_TOOLBAR_VIEW_H_
diff --git a/chrome/browser/ui/views/user_education/feature_promo_bubble_view.cc b/chrome/browser/ui/views/user_education/feature_promo_bubble_view.cc
index e5e2b0d..8c8c41e 100644
--- a/chrome/browser/ui/views/user_education/feature_promo_bubble_view.cc
+++ b/chrome/browser/ui/views/user_education/feature_promo_bubble_view.cc
@@ -316,17 +316,6 @@
     feature_promo_bubble_timeout_->OnMouseExited();
 }
 
-gfx::Rect FeaturePromoBubbleView::GetBubbleBounds() {
-  gfx::Rect bounds = BubbleDialogDelegateView::GetBubbleBounds();
-  if (!focusable_) {
-    if (base::i18n::IsRTL())
-      bounds.Offset(5, 0);
-    else
-      bounds.Offset(-5, 0);
-  }
-  return bounds;
-}
-
 ax::mojom::Role FeaturePromoBubbleView::GetAccessibleWindowRole() {
   // Since we don't have any controls for the user to interact with (we're just
   // an information bubble), override our role to kAlert.
diff --git a/chrome/browser/ui/views/user_education/feature_promo_bubble_view.h b/chrome/browser/ui/views/user_education/feature_promo_bubble_view.h
index d16bc36..2004555 100644
--- a/chrome/browser/ui/views/user_education/feature_promo_bubble_view.h
+++ b/chrome/browser/ui/views/user_education/feature_promo_bubble_view.h
@@ -15,9 +15,6 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 
-namespace gfx {
-class Rect;
-}
 
 namespace ui {
 class MouseEvent;
@@ -89,7 +86,6 @@
   bool OnMousePressed(const ui::MouseEvent& event) override;
   void OnMouseEntered(const ui::MouseEvent& event) override;
   void OnMouseExited(const ui::MouseEvent& event) override;
-  gfx::Rect GetBubbleBounds() override;
   ax::mojom::Role GetAccessibleWindowRole() override;
   base::string16 GetAccessibleWindowTitle() const override;
   void UpdateHighlightedButton(bool highlighted) override {
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
index 8dcbd3c..bd2d2c7 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
@@ -119,7 +119,7 @@
       extensions_container_->SetProperty(
           views::kFlexBehaviorKey,
           views::FlexSpecification(
-              extensions_container_->animating_layout_manager()
+              extensions_container_->GetAnimatingLayoutManager()
                   ->GetDefaultFlexRule())
               .WithOrder(kLowPriorityFlexOrder));
       views::SetHitTestComponent(extensions_container_,
@@ -178,7 +178,7 @@
   if (content_settings_container_)
     content_settings_container_->SetIconColor(foreground_color_);
   if (extensions_container_)
-    extensions_container_->OverrideIconColor(foreground_color_);
+    extensions_container_->SetIconColor(foreground_color_);
   page_action_icon_controller_->SetIconColor(foreground_color_);
   if (web_app_menu_button_)
     web_app_menu_button_->SetColor(foreground_color_);
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 d0f93bd7..68d361a9 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
@@ -24,13 +24,19 @@
 #include "chrome/browser/web_applications/components/app_registry_controller.h"
 #include "chrome/browser/web_applications/components/install_finalizer.h"
 #include "chrome/browser/web_applications/components/os_integration_manager.h"
+#include "chrome/browser/web_applications/components/policy/web_app_policy_constants.h"
+#include "chrome/browser/web_applications/components/policy/web_app_policy_manager.h"
+#include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_id.h"
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
 #include "chrome/browser/web_applications/test/web_app_install_observer.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/prefs/scoped_user_pref_update.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "extensions/browser/extension_dialog_auto_confirm.h"
@@ -214,27 +220,37 @@
         action_strings, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
   }
 
+  // Non-assert actions implemented before assert actions. Implemented in
+  // alphabetical order.
   void ExecuteAction(const std::string& action_string) {
-    if (base::StartsWith(action_string, "navigate_installable")) {
-      NavigateToSite(browser(), GetInstallableAppURL());
-    } else if (action_string == "navigate_browser_in_scope") {
-      NavigateToSite(browser(), GetInScopeURL());
-    } else if (action_string == "navigate_not_installable") {
-      NavigateToSite(browser(), GetOutOfScopeURL());
+    if (action_string == "add_policy_app_internal_tabbed") {
+      AddPolicyAppInternalTabbed();
+    } else if (action_string == "close_pwa") {
+      ClosePWA();
+    } else if (action_string == "install_create_shortcut_tabbed") {
+      InstallCreateShortcutTabbed();
     } else if (action_string == "install_omnibox_or_menu") {
-      ExecutePwaInstallIcon();
+      InstallOmniboxOrMenu();
     } else if (base::StartsWith(action_string, "launch_internal")) {
       LaunchInternal();
+    } else if (action_string == "list_apps_internal") {
+      ListAppsInternal();
+    } else if (action_string == "navigate_browser_in_scope") {
+      NavigateToSite(browser(), GetInScopeURL());
+    } else if (base::StartsWith(action_string, "navigate_installable")) {
+      NavigateToSite(browser(), GetInstallableAppURL());
+    } else if (action_string == "navigate_not_installable") {
+      NavigateToSite(browser(), GetNonInstallableAppURL());
+    } else if (action_string == "remove_policy_app") {
+      RemovePolicyApp();
+    } else if (action_string == "set_open_in_window_internal") {
+      SetOpenInWindowInternal();
     } else if (action_string == "uninstall_from_menu") {
       UninstallFromMenu();
     } else if (action_string == "uninstall_internal") {
       UninstallInternal();
-    } else if (action_string == "install_create_shortcut_tabbed") {
-      InstallCreateShortcutTabbed();
-    } else if (action_string == "set_open_in_window_internal") {
-      SetOpenInWindowInternal();
-    } else if (action_string == "close_pwa") {
-      ClosePWA();
+    } else if (action_string == "assert_app_not_in_list") {
+      AssertAppNotInList();
     } else if (action_string == "assert_installable") {
       AssertInstallable();
     } else if (action_string == "assert_install_icon_shown") {
@@ -245,46 +261,60 @@
       AssertLaunchIconShown();
     } else if (action_string == "assert_launch_icon_not_shown") {
       AssertLaunchIconNotShown();
+    } else if (action_string == "assert_no_crash") {
     } else if (action_string == "assert_window_created") {
       AssertWindowCreated();
-    } else if (action_string == "assert_no_crash") {
     } else {
       FAIL() << "Unimplemented action: " << action_string;
     }
   }
 
   // Automated Testing Actions
-  NavigateToSiteResult NavigateToSite(Browser* browser, const GURL& url) {
-    content::WebContents* web_contents = GetCurrentTab(browser);
-    auto* app_banner_manager =
-        webapps::TestAppBannerManagerDesktop::FromWebContents(web_contents);
-    DCHECK(!app_banner_manager->WaitForInstallableCheck());
-
-    ui_test_utils::NavigateToURL(browser, url);
-    bool installable = app_banner_manager->WaitForInstallableCheck();
-
-    last_navigation_result_ =
-        NavigateToSiteResult{web_contents, app_banner_manager, installable};
-    return last_navigation_result_;
+  void AddPolicyAppInternalTabbed() {
+    GURL url = GetInstallableAppURL();
+    auto* web_app_registrar = WebAppProvider::Get(browser()->profile())
+                                  ->registrar()
+                                  .AsWebAppRegistrar();
+    base::RunLoop run_loop;
+    WebAppInstallObserver observer(browser()->profile());
+    observer.SetWebAppInstalledDelegate(
+        base::BindLambdaForTesting([&](const AppId& app_id) {
+          bool is_installed = web_app_registrar->IsInstalled(app_id);
+          GURL installed_url = web_app_registrar->GetAppStartUrl(app_id);
+          if (is_installed && installed_url.is_valid() &&
+              installed_url.spec() == url.spec()) {
+            app_id_ = app_id;
+            run_loop.Quit();
+          }
+        }));
+    {
+      base::Value item(base::Value::Type::DICTIONARY);
+      item.SetKey(kUrlKey, base::Value(url.spec()));
+      item.SetKey(kDefaultLaunchContainerKey,
+                  base::Value(kDefaultLaunchContainerTabValue));
+      ListPrefUpdate update(browser()->profile()->GetPrefs(),
+                            prefs::kWebAppInstallForceList);
+      update->Append(item.Clone());
+    }
+    run_loop.Run();
   }
 
-  GURL GetInstallableAppURL() {
-    return https_server_.GetURL("/banners/manifest_test_page.html");
+  void ClosePWA() {
+    DCHECK(app_browser_);
+    app_browser_->window()->Close();
+    ui_test_utils::WaitForBrowserToClose(app_browser_);
   }
 
-  GURL GetInScopeURL() {
-    return https_server_.GetURL("/banners/manifest_test_page.html");
+  void InstallCreateShortcutTabbed() {
+    chrome::SetAutoAcceptWebAppDialogForTesting(/*auto_accept=*/true,
+                                                /*auto_open_in_window=*/false);
+    WebAppInstallObserver observer(browser()->profile());
+    CHECK(chrome::ExecuteCommand(browser(), IDC_CREATE_SHORTCUT));
+    app_id_ = observer.AwaitNextInstall();
+    chrome::SetAutoAcceptWebAppDialogForTesting(false, false);
   }
 
-  GURL GetOutOfScopeURL() {
-    return https_server_.GetURL("/out_of_scope/index.html");
-  }
-
-  content::WebContents* GetCurrentTab(Browser* browser) {
-    return browser->tab_strip_model()->GetActiveWebContents();
-  }
-
-  web_app::AppId ExecutePwaInstallIcon() {
+  web_app::AppId InstallOmniboxOrMenu() {
     chrome::SetAutoAcceptPWAInstallConfirmationForTesting(true);
 
     web_app::AppId app_id;
@@ -315,6 +345,55 @@
     return app_browser_;
   }
 
+  void ListAppsInternal() {
+    auto* web_app_registrar = WebAppProvider::Get(browser()->profile())
+                                  ->registrar()
+                                  .AsWebAppRegistrar();
+    app_ids_ = web_app_registrar->GetAppIds();
+  }
+
+  NavigateToSiteResult NavigateToSite(Browser* browser, const GURL& url) {
+    content::WebContents* web_contents = GetCurrentTab(browser);
+    auto* app_banner_manager =
+        webapps::TestAppBannerManagerDesktop::FromWebContents(web_contents);
+    DCHECK(!app_banner_manager->WaitForInstallableCheck());
+
+    ui_test_utils::NavigateToURL(browser, url);
+    bool installable = app_banner_manager->WaitForInstallableCheck();
+
+    last_navigation_result_ =
+        NavigateToSiteResult{web_contents, app_banner_manager, installable};
+    return last_navigation_result_;
+  }
+
+  void RemovePolicyApp() {
+    GURL url = GetInstallableAppURL();
+    base::RunLoop run_loop;
+    WebAppInstallObserver observer(browser()->profile());
+    observer.SetWebAppUninstalledDelegate(
+        base::BindLambdaForTesting([&](const AppId& app_id) {
+          if (app_id_ == app_id) {
+            run_loop.Quit();
+          }
+        }));
+    {
+      ListPrefUpdate update(browser()->profile()->GetPrefs(),
+                            prefs::kWebAppInstallForceList);
+      update->EraseListValueIf([&](const base::Value& item) {
+        const base::Value* url_value = item.FindKey(kUrlKey);
+        return url_value && url_value->GetString() == url.spec();
+      });
+    }
+    run_loop.Run();
+  }
+
+  void SetOpenInWindowInternal() {
+    auto& app_registry_controller =
+        WebAppProvider::Get(browser()->profile())->registry_controller();
+    app_registry_controller.SetAppUserDisplayMode(
+        app_id_, blink::mojom::DisplayMode::kStandalone, true);
+  }
+
   // TODO(https://crbug.com/1159651): Support this action on CrOS.
   void UninstallFromMenu() {
     DCHECK(app_browser_);
@@ -364,42 +443,25 @@
     run_loop.Run();
   }
 
-  void InstallCreateShortcutTabbed() {
-    chrome::SetAutoAcceptWebAppDialogForTesting(/*auto_accept=*/true,
-                                                /*auto_open_in_window=*/false);
-    WebAppInstallObserver observer(browser()->profile());
-    CHECK(chrome::ExecuteCommand(browser(), IDC_CREATE_SHORTCUT));
-    app_id_ = observer.AwaitNextInstall();
-    chrome::SetAutoAcceptWebAppDialogForTesting(false, false);
-  }
-
-  void SetOpenInWindowInternal() {
-    auto& app_registry_controller =
-        WebAppProvider::Get(browser()->profile())->registry_controller();
-    app_registry_controller.SetAppUserDisplayMode(
-        app_id_, blink::mojom::DisplayMode::kStandalone, true);
-  }
-
-  void ClosePWA() {
-    DCHECK(app_browser_);
-    app_browser_->window()->Close();
-    ui_test_utils::WaitForBrowserToClose(app_browser_);
-  }
-
   // Assert Actions
+  void AssertAppNotInList() { EXPECT_FALSE(base::Contains(app_ids_, app_id_)); }
   void AssertInstallable() { EXPECT_TRUE(last_navigation_result_.installable); }
+
   void AssertInstallIconShown() {
     EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, browser()), kEnabled);
     EXPECT_TRUE(pwa_install_view()->GetVisible());
   }
+
   void AssertInstallIconNotShown() {
     EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, browser()), kNotPresent);
     EXPECT_FALSE(pwa_install_view()->GetVisible());
   }
+
   void AssertLaunchIconShown() {
     EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, browser()),
               kEnabled);
   }
+
   void AssertLaunchIconNotShown() {
     EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, browser()),
               kNotPresent);
@@ -407,12 +469,33 @@
 
   void AssertWindowCreated() { EXPECT_TRUE(app_browser_); }
 
+  GURL GetInstallableAppURL() {
+    return https_server_.GetURL("/banners/manifest_test_page.html");
+  }
+
+  GURL GetNonInstallableAppURL() {
+    return https_server_.GetURL("/banners/no_manifest_test_page.html");
+  }
+
+  GURL GetInScopeURL() {
+    return https_server_.GetURL("/banners/manifest_test_page.html");
+  }
+
+  GURL GetOutOfScopeURL() {
+    return https_server_.GetURL("/out_of_scope/index.html");
+  }
+
+  content::WebContents* GetCurrentTab(Browser* browser) {
+    return browser->tab_strip_model()->GetActiveWebContents();
+  }
+
   Browser* app_browser() { return app_browser_; }
   std::vector<std::string>& testing_actions() { return testing_actions_; }
   PageActionIconView* pwa_install_view() { return pwa_install_view_; }
 
  private:
   Browser* app_browser_ = nullptr;
+  std::vector<AppId> app_ids_;
   std::vector<std::string> testing_actions_;
   NavigateToSiteResult last_navigation_result_;
   AppId app_id_;
@@ -435,7 +518,7 @@
   EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, browser()),
             kNotPresent);
 
-  ExecutePwaInstallIcon();
+  InstallOmniboxOrMenu();
 
   chrome::NewTab(browser());
   NavigateToSite(browser(), GetInstallableAppURL());
@@ -450,7 +533,7 @@
   EXPECT_EQ(1U, browser_list->size());
   EXPECT_FALSE(AppBrowserController::IsWebApp(browser_list->GetLastActive()));
   NavigateToSite(browser(), GetInstallableAppURL());
-  ExecutePwaInstallIcon();
+  InstallOmniboxOrMenu();
   EXPECT_EQ(2U, browser_list->size());
   EXPECT_TRUE(AppBrowserController::IsWebApp(browser_list->GetLastActive()));
   ClosePWA();
diff --git a/chrome/browser/ui/web_applications/web_app_engagement_browsertest.cc b/chrome/browser/ui/web_applications/web_app_engagement_browsertest.cc
index 8e97e12..77205c1 100644
--- a/chrome/browser/ui/web_applications/web_app_engagement_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_engagement_browsertest.cc
@@ -12,7 +12,6 @@
 #include "base/metrics/statistics_recorder.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -29,6 +28,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/site_engagement/content/engagement_type.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/ui/web_applications/web_app_launch_manager.cc b/chrome/browser/ui/web_applications/web_app_launch_manager.cc
index 126f17e..56873fc 100644
--- a/chrome/browser/ui/web_applications/web_app_launch_manager.cc
+++ b/chrome/browser/ui/web_applications/web_app_launch_manager.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/banners/app_banner_settings_helper.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -41,6 +40,7 @@
 #include "chrome/browser/web_launch/web_launch_files_helper.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/page_navigator.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/ui/web_applications/web_app_metrics.cc b/chrome/browser/ui/web_applications/web_app_metrics.cc
index ed29cfda..22293d7 100644
--- a/chrome/browser/ui/web_applications/web_app_metrics.cc
+++ b/chrome/browser/ui/web_applications/web_app_metrics.cc
@@ -9,7 +9,6 @@
 #include "base/optional.h"
 #include "base/power_monitor/power_monitor.h"
 #include "base/time/time.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -21,6 +20,7 @@
 #include "chrome/browser/web_applications/daily_metrics_helper.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "components/site_engagement/content/engagement_type.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/webapps/installable/installable_metrics.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
 
diff --git a/chrome/browser/ui/web_applications/web_app_metrics.h b/chrome/browser/ui/web_applications/web_app_metrics.h
index b2d8b25..3359fcb 100644
--- a/chrome/browser/ui/web_applications/web_app_metrics.h
+++ b/chrome/browser/ui/web_applications/web_app_metrics.h
@@ -9,11 +9,11 @@
 #include "base/power_monitor/power_observer.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/banners/app_banner_manager.h"
-#include "chrome/browser/engagement/site_engagement_observer.h"
 #include "chrome/browser/ui/browser_tab_strip_tracker.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/browser/web_applications/components/web_app_id.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/site_engagement/content/site_engagement_observer.h"
 
 class Profile;
 class Browser;
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 09dc9d7..868d9e9 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/buildflags.h"
 #include "chrome/browser/chromeos/login/login_pref_names.h"
 #include "chrome/browser/devtools/devtools_ui_bindings.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/media/history/media_history_keyed_service.h"
 #include "chrome/browser/media/media_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
@@ -95,6 +94,7 @@
 #include "components/security_interstitials/content/known_interception_disclosure_ui.h"
 #include "components/security_interstitials/content/urls.h"
 #include "components/signin/public/base/signin_buildflags.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/common/content_client.h"
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index fcad654a..1280f86 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -236,8 +236,10 @@
 }
 
 user_manager::UserType CalculateUserType(const AccountId& account_id) {
-  if (user_manager::UserManager::Get()->IsSupervisedAccountId(account_id))
-    return user_manager::USER_TYPE_SUPERVISED;
+  if (user_manager::UserManager::Get()->IsDeprecatedSupervisedAccountId(
+          account_id)) {
+    return user_manager::USER_TYPE_SUPERVISED_DEPRECATED;
+  }
 
   if (account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY)
     return user_manager::USER_TYPE_ACTIVE_DIRECTORY;
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 3e786cf..27de93f 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -359,6 +359,7 @@
   builder->Add("removeUserWarningTextCalculating", base::string16());
   builder->Add("removeUserWarningTextSyncNoStats", base::string16());
   builder->Add("removeUserWarningTextSyncCalculating", base::string16());
+  // TODO(crbug/1164090): Remove this.
   builder->AddF("removeLegacySupervisedUserWarningText",
                IDS_LOGIN_POD_LEGACY_SUPERVISED_USER_REMOVE_WARNING,
                base::UTF8ToUTF16(
diff --git a/chrome/browser/ui/webui/discards/discards_ui.cc b/chrome/browser/ui/webui/discards/discards_ui.cc
index dc082820..ca5c5ac 100644
--- a/chrome/browser/ui/webui/discards/discards_ui.cc
+++ b/chrome/browser/ui/webui/discards/discards_ui.cc
@@ -14,7 +14,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
@@ -32,6 +31,7 @@
 #include "chrome/grit/browser_resources.h"
 #include "components/favicon_base/favicon_url_parser.h"
 #include "components/performance_manager/public/performance_manager.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/url_data_source.h"
diff --git a/chrome/browser/ui/webui/engagement/site_engagement_ui.cc b/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
index e4bdcb6f..7781c93a 100644
--- a/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
+++ b/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
@@ -12,10 +12,10 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/macros.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/dev_ui_browser_resources.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "components/site_engagement/core/mojom/site_engagement_details.mojom.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_controller.h"
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
index 833e8ec89..b0196e9 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
@@ -1027,16 +1027,22 @@
       autocomplete_controller_->AddObserver(emitter);
   }
 
-  if (time_of_first_autocomplete_query_.is_null() && !input.empty())
-    time_of_first_autocomplete_query_ = base::TimeTicks::Now();
+  // TODO(tommycli): We use the input being empty as a signal we are requesting
+  // on-focus suggestions. It would be nice if we had a more explicit signal.
+  bool is_on_focus = input.empty();
+
+  // Early exit if a query is already in progress for on focus inputs.
+  if (!autocomplete_controller_->done() && is_on_focus)
+    return;
+
+  if (time_user_first_modified_realbox_.is_null() && !is_on_focus)
+    time_user_first_modified_realbox_ = base::TimeTicks::Now();
 
   AutocompleteInput autocomplete_input(
       input, metrics::OmniboxEventProto::NTP_REALBOX,
       ChromeAutocompleteSchemeClassifier(profile_));
-  // TODO(tommycli): We use the input being empty as a signal we are requesting
-  // on-focus suggestions. It would be nice if we had a more explicit signal.
-  autocomplete_input.set_focus_type(input.empty() ? OmniboxFocusType::ON_FOCUS
-                                                  : OmniboxFocusType::DEFAULT);
+  autocomplete_input.set_focus_type(is_on_focus ? OmniboxFocusType::ON_FOCUS
+                                                : OmniboxFocusType::DEFAULT);
   autocomplete_input.set_prevent_inline_autocomplete(
       prevent_inline_autocomplete);
 
@@ -1055,7 +1061,7 @@
   autocomplete_controller_->Stop(clear_result);
 
   if (clear_result)
-    time_of_first_autocomplete_query_ = base::TimeTicks();
+    time_user_first_modified_realbox_ = base::TimeTicks();
 }
 
 void NewTabPageHandler::OpenAutocompleteMatch(
@@ -1085,7 +1091,7 @@
 
   const auto now = base::TimeTicks::Now();
   base::TimeDelta elapsed_time_since_first_autocomplete_query =
-      now - time_of_first_autocomplete_query_;
+      now - time_user_first_modified_realbox_;
   autocomplete_controller_->UpdateMatchDestinationURLWithQueryFormulationTime(
       elapsed_time_since_first_autocomplete_query, &match);
 
@@ -1136,7 +1142,7 @@
 
   base::TimeDelta default_time_delta = base::TimeDelta::FromMilliseconds(-1);
 
-  if (time_of_first_autocomplete_query_.is_null())
+  if (time_user_first_modified_realbox_.is_null())
     elapsed_time_since_first_autocomplete_query = default_time_delta;
 
   base::TimeDelta elapsed_time_since_last_change_to_default_match =
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
index 28f5a26..f57a2029 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
@@ -238,7 +238,7 @@
   FaviconCache favicon_cache_;
   BitmapFetcherService* bitmap_fetcher_service_;
   std::vector<BitmapFetcherService::RequestId> bitmap_request_ids_;
-  base::TimeTicks time_of_first_autocomplete_query_;
+  base::TimeTicks time_user_first_modified_realbox_;
   content::WebContents* web_contents_;
   base::Time ntp_navigation_start_time_;
   NTPUserDataLogger logger_;
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc
index 6ad4c097..c521456 100644
--- a/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -280,6 +280,10 @@
                                           base::Unretained(this)));
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   web_ui()->RegisterMessageCallback(
+      "openDiagnostics",
+      base::BindRepeating(&AboutHandler::HandleOpenDiagnostics,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
       "openOsHelpPage", base::BindRepeating(&AboutHandler::HandleOpenOsHelpPage,
                                             base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
@@ -420,6 +424,11 @@
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+void AboutHandler::HandleOpenDiagnostics(const base::ListValue* args) {
+  DCHECK(args->empty());
+  chrome::ShowDiagnosticsApp(profile_);
+}
+
 void AboutHandler::HandleCheckInternetConnection(const base::ListValue* args) {
   CHECK_EQ(1U, args->GetSize());
   std::string callback_id;
diff --git a/chrome/browser/ui/webui/settings/about_handler.h b/chrome/browser/ui/webui/settings/about_handler.h
index 68754be..4b5aafa8 100644
--- a/chrome/browser/ui/webui/settings/about_handler.h
+++ b/chrome/browser/ui/webui/settings/about_handler.h
@@ -154,6 +154,8 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+  void HandleOpenDiagnostics(const base::ListValue* args);
+
   void HandleGetRegulatoryInfo(const base::ListValue* args);
 
   // Callback for when the directory with the regulatory label image and alt
diff --git a/chrome/browser/ui/webui/settings/chromeos/about_section.cc b/chrome/browser/ui/webui/settings/chromeos/about_section.cc
index 173b784..556adc7 100644
--- a/chrome/browser/ui/webui/settings/chromeos/about_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/about_section.cc
@@ -97,6 +97,19 @@
   return *tags;
 }
 
+const std::vector<SearchConcept>& GetDiagnosticsAppSearchConcepts() {
+  static const base::NoDestructor<std::vector<SearchConcept>> tags({
+      {IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS,
+       mojom::kAboutChromeOsDetailsSubpagePath,
+       mojom::SearchResultIcon::kChrome,
+       mojom::SearchResultDefaultRank::kMedium,
+       mojom::SearchResultType::kSetting,
+       {.setting = mojom::Setting::kDiagnostics},
+       {IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT1, SearchConcept::kAltTagEnd}},
+  });
+  return *tags;
+}
+
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
 const std::vector<SearchConcept>& GetAboutTermsOfServiceSearchConcepts() {
   static const base::NoDestructor<std::vector<SearchConcept>> tags({
@@ -166,6 +179,10 @@
     : OsSettingsSection(profile, search_tag_registry) {
   SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
   updater.AddSearchTags(GetAboutSearchConcepts());
+
+  if (base::FeatureList::IsEnabled(chromeos::features::kDiagnosticsApp)) {
+    updater.AddSearchTags(GetDiagnosticsAppSearchConcepts());
+  }
 }
 
 AboutSection::~AboutSection() = default;
@@ -177,6 +194,7 @@
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
     {"aboutReportAnIssue", IDS_SETTINGS_ABOUT_PAGE_REPORT_AN_ISSUE},
 #endif
+    {"aboutDiagnostics", IDS_SETTINGS_ABOUT_PAGE_DIAGNOSTICS},
     {"aboutRelaunch", IDS_SETTINGS_ABOUT_PAGE_RELAUNCH},
     {"aboutUpgradeCheckStarted", IDS_SETTINGS_ABOUT_UPGRADE_CHECK_STARTED},
     {"aboutUpgradeRelaunch", IDS_SETTINGS_UPGRADE_SUCCESSFUL_RELAUNCH},
@@ -327,6 +345,10 @@
   std::string safetyInfoLink = GetSafetyInfoLink();
   html_source->AddBoolean("shouldShowSafetyInfo", !safetyInfoLink.empty());
 
+  html_source->AddBoolean(
+      "diagnosticsAppEnabled",
+      base::FeatureList::IsEnabled(chromeos::features::kDiagnosticsApp));
+
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   html_source->AddString("aboutTermsURL", chrome::kChromeUITermsURL);
   html_source->AddLocalizedString("aboutProductTos",
@@ -373,9 +395,9 @@
       mojom::SearchResultIcon::kChrome, mojom::SearchResultDefaultRank::kMedium,
       mojom::kAboutChromeOsDetailsSubpagePath);
   static constexpr mojom::Setting kAboutChromeOsDetailsSettings[] = {
-      mojom::Setting::kCheckForOsUpdate, mojom::Setting::kSeeWhatsNew,
+      mojom::Setting::kCheckForOsUpdate,    mojom::Setting::kSeeWhatsNew,
       mojom::Setting::kGetHelpWithChromeOs, mojom::Setting::kReportAnIssue,
-      mojom::Setting::kTermsOfService};
+      mojom::Setting::kTermsOfService,      mojom::Setting::kDiagnostics};
   RegisterNestedSettingBulk(mojom::Subpage::kAboutChromeOsDetails,
                             kAboutChromeOsDetailsSettings, generator);
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
index b5a1ec99..f28d588 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
@@ -248,6 +248,7 @@
   kGetHelpWithChromeOs = 1704,
   kReportAnIssue = 1705,
   kTermsOfService = 1706,
+  kDiagnostics = 1707,
 
   // Kerberos section.
   kAddKerberosTicketV2 = 1800,
diff --git a/chrome/browser/ui/webui/settings/import_data_handler.cc b/chrome/browser/ui/webui/settings/import_data_handler.cc
index 0e356e6..5eca85a0 100644
--- a/chrome/browser/ui/webui/settings/import_data_handler.cc
+++ b/chrome/browser/ui/webui/settings/import_data_handler.cc
@@ -40,8 +40,7 @@
 const char kImportStatusFailed[] = "failed";
 }  // namespace
 
-ImportDataHandler::ImportDataHandler()
-    : importer_host_(nullptr), import_did_succeed_(false) {
+ImportDataHandler::ImportDataHandler() : importer_host_(nullptr) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
@@ -115,6 +114,12 @@
   const base::DictionaryValue* types = nullptr;
   CHECK(args->GetDictionary(1, &types));
 
+  if (!importer_list_loaded_ || browser_index < 0 ||
+      browser_index >= static_cast<int>(importer_list_->count())) {
+    // Prevent out-of-bounds access.
+    return;
+  }
+
   uint16_t selected_items = importer::NONE;
   if (*types->FindBoolKey(prefs::kImportDialogAutofillFormData))
     selected_items |= importer::AUTOFILL_FORM_DATA;
@@ -180,6 +185,7 @@
 
 void ImportDataHandler::SendBrowserProfileData(const std::string& callback_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  importer_list_loaded_ = true;
 
   base::ListValue browser_profiles;
   for (size_t i = 0; i < importer_list_->count(); ++i) {
diff --git a/chrome/browser/ui/webui/settings/import_data_handler.h b/chrome/browser/ui/webui/settings/import_data_handler.h
index 6ee9d1a..173e710 100644
--- a/chrome/browser/ui/webui/settings/import_data_handler.h
+++ b/chrome/browser/ui/webui/settings/import_data_handler.h
@@ -69,7 +69,8 @@
   // of deleting itself when import is complete.
   ExternalProcessImporterHost* importer_host_;  // weak
 
-  bool import_did_succeed_;
+  bool import_did_succeed_{false};
+  bool importer_list_loaded_{false};
 
   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
 
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index f692ca8..73dd3c4 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/bluetooth/bluetooth_chooser_context.h"
 #include "chrome/browser/bluetooth/bluetooth_chooser_context_factory.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/hid/hid_chooser_context.h"
 #include "chrome/browser/hid/hid_chooser_context_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
@@ -57,6 +56,7 @@
 #include "components/permissions/permission_util.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.cc b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
index d070bef..b33a898 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_handler.cc
+++ b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
@@ -215,6 +215,11 @@
       "setProfileName",
       base::BindRepeating(&ProfilePickerHandler::HandleSetProfileName,
                           base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "recordSignInPromoImpression",
+      base::BindRepeating(
+          &ProfilePickerHandler::HandleRecordSignInPromoImpression,
+          base::Unretained(this)));
 }
 
 void ProfilePickerHandler::OnJavascriptAllowed() {
@@ -454,6 +459,12 @@
       profile, Profile::CREATE_STATUS_INITIALIZED);
 }
 
+void ProfilePickerHandler::HandleRecordSignInPromoImpression(
+    const base::ListValue* /*args*/) {
+  signin_metrics::RecordSigninImpressionUserActionForAccessPoint(
+      signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER);
+}
+
 void ProfilePickerHandler::HandleSetProfileName(const base::ListValue* args) {
   CHECK_EQ(2U, args->GetSize());
   const base::Value& profile_path_value = args->GetList()[0];
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.h b/chrome/browser/ui/webui/signin/profile_picker_handler.h
index b359acf..61e1eb9 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_handler.h
+++ b/chrome/browser/ui/webui/signin/profile_picker_handler.h
@@ -49,6 +49,9 @@
   void HandleGetProfileThemeInfo(const base::ListValue* args);
   void HandleCreateProfile(const base::ListValue* args);
 
+  // |args| is unused.
+  void HandleRecordSignInPromoImpression(const base::ListValue* args);
+
   void OnLoadSigninFinished(bool success);
   void GatherProfileStatistics(Profile* profile);
   void OnProfileStatisticsReceived(base::FilePath profile_path,
diff --git a/chrome/browser/ui/webui/signin/profile_picker_ui.cc b/chrome/browser/ui/webui/signin/profile_picker_ui.cc
index 9618b16..7e790be 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_ui.cc
+++ b/chrome/browser/ui/webui/signin/profile_picker_ui.cc
@@ -80,6 +80,7 @@
       {"browseAsGuestButton", IDS_PROFILE_PICKER_BROWSE_AS_GUEST_BUTTON},
       {"needsSigninPrompt",
        IDS_PROFILE_PICKER_PROFILE_CARD_NEEDS_SIGNIN_PROMPT},
+      {"profileCardButtonLabel", IDS_PROFILE_PICKER_PROFILE_CARD_LABEL},
       {"menu", IDS_MENU},
       {"profileMenuName", IDS_PROFILE_PICKER_PROFILE_MENU_BUTTON_NAME},
       {"profileMenuRemoveText", IDS_PROFILE_PICKER_PROFILE_MENU_REMOVE_TEXT},
diff --git a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
index 3421904..2869c86 100644
--- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
+++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
@@ -71,6 +71,7 @@
 const char kKeyEmailAddress[] = "emailAddress";
 const char kKeyProfilePath[] = "profilePath";
 const char kKeyPublicAccount[] = "publicAccount";
+// TODO(crbug/1164090): Remove this.
 const char kKeyLegacySupervisedUser[] = "legacySupervisedUser";
 const char kKeyChildUser[] = "childUser";
 const char kKeyCanRemove[] = "canRemove";
@@ -660,6 +661,7 @@
   localized_strings->SetString("cancel", l10n_util::GetStringUTF16(IDS_CANCEL));
   localized_strings->SetString(
       "browseAsGuest", l10n_util::GetStringUTF16(IDS_BROWSE_AS_GUEST_BUTTON));
+  // TODO(crbug/1164090): Remove this.
   localized_strings->SetString("addSupervisedUser",
       l10n_util::GetStringUTF16(IDS_CREATE_LEGACY_SUPERVISED_USER_MENU_LABEL));
 
@@ -702,6 +704,7 @@
   localized_strings->SetString(
       "removeUserWarningTextSync",
       l10n_util::GetStringUTF16(IDS_LOGIN_POD_USER_REMOVE_WARNING_SYNC));
+  // TODO(crbug/1164090): Remove this.
   localized_strings->SetString("removeLegacySupervisedUserWarningText",
       l10n_util::GetStringFUTF16(
           IDS_LOGIN_POD_LEGACY_SUPERVISED_USER_REMOVE_WARNING,
@@ -803,6 +806,7 @@
                              profiles::GetAvatarNameForProfile(profile_path));
     profile_value->SetKey(kKeyProfilePath, util::FilePathToValue(profile_path));
     profile_value->SetBoolean(kKeyPublicAccount, false);
+    // TODO(crbug/1164090): Remove this.
     profile_value->SetBoolean(kKeyLegacySupervisedUser,
                               entry->IsLegacySupervised());
     profile_value->SetBoolean(kKeyChildUser, entry->IsChild());
diff --git a/chrome/browser/ui/webui/tab_strip/thumbnail_tracker.cc b/chrome/browser/ui/webui/tab_strip/thumbnail_tracker.cc
index c33dfe94..ab1b9ea 100644
--- a/chrome/browser/ui/webui/tab_strip/thumbnail_tracker.cc
+++ b/chrome/browser/ui/webui/tab_strip/thumbnail_tracker.cc
@@ -15,14 +15,17 @@
 
 // Handles requests for a given tab's thumbnail and watches for thumbnail
 // updates for the lifetime of the tab.
-class ThumbnailTracker::ContentsData : public content::WebContentsObserver,
-                                       public ThumbnailImage::Observer {
+class ThumbnailTracker::ContentsData : public content::WebContentsObserver {
  public:
   ContentsData(ThumbnailTracker* parent, content::WebContents* contents)
       : content::WebContentsObserver(contents), parent_(parent) {
     thumbnail_ = parent_->thumbnail_getter_.Run(contents);
-    if (thumbnail_)
-      observation_.Observe(thumbnail_.get());
+    if (!thumbnail_)
+      return;
+
+    subscription_ = thumbnail_->Subscribe();
+    subscription_->SetCompressedImageCallback(base::BindRepeating(
+        &ContentsData::ThumbnailImageCallback, base::Unretained(this)));
   }
 
   void RequestThumbnail() {
@@ -35,8 +38,7 @@
     // We must un-observe each ThumbnailImage when the WebContents it came from
     // closes.
     if (thumbnail_) {
-      DCHECK(observation_.IsObservingSource(thumbnail_.get()));
-      observation_.Reset();
+      subscription_.reset();
       thumbnail_.reset();
     }
 
@@ -44,17 +46,14 @@
     parent_->ContentsClosed(web_contents());
   }
 
-  // ThumbnailImage::Observer:
-  void OnCompressedThumbnailDataAvailable(
-      CompressedThumbnailData thumbnail_image) override {
-    parent_->ThumbnailUpdated(web_contents(), thumbnail_image);
+ private:
+  void ThumbnailImageCallback(CompressedThumbnailData image) {
+    parent_->ThumbnailUpdated(web_contents(), image);
   }
 
- private:
   ThumbnailTracker* parent_;
   scoped_refptr<ThumbnailImage> thumbnail_;
-  base::ScopedObservation<ThumbnailImage, ThumbnailImage::Observer>
-      observation_{this};
+  std::unique_ptr<ThumbnailImage::Subscription> subscription_;
 
   DISALLOW_COPY_AND_ASSIGN(ContentsData);
 };
diff --git a/chrome/browser/xsurface/BUILD.gn b/chrome/browser/xsurface/BUILD.gn
index ddc8791e..63826464 100644
--- a/chrome/browser/xsurface/BUILD.gn
+++ b/chrome/browser/xsurface/BUILD.gn
@@ -12,6 +12,7 @@
     "android/java/src/org/chromium/chrome/browser/xsurface/ImagePrefetcher.java",
     "android/java/src/org/chromium/chrome/browser/xsurface/ListContentManager.java",
     "android/java/src/org/chromium/chrome/browser/xsurface/ListContentManagerObserver.java",
+    "android/java/src/org/chromium/chrome/browser/xsurface/PersistentKeyValueCache.java",
     "android/java/src/org/chromium/chrome/browser/xsurface/ProcessScope.java",
     "android/java/src/org/chromium/chrome/browser/xsurface/ProcessScopeDependencyProvider.java",
     "android/java/src/org/chromium/chrome/browser/xsurface/SurfaceActionsHandler.java",
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/PersistentKeyValueCache.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/PersistentKeyValueCache.java
new file mode 100644
index 0000000..154d8b6
--- /dev/null
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/PersistentKeyValueCache.java
@@ -0,0 +1,47 @@
+// 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.
+
+package org.chromium.chrome.browser.xsurface;
+
+import androidx.annotation.Nullable;
+
+/** A simple key-value cache that is persisting all data on disk. Automatically evicts old data. */
+public interface PersistentKeyValueCache {
+    /** Consumes the result of PersistentKeyValueCache.lookup(). */
+    public interface ValueConsumer {
+        /**
+         * Called when a lookup is complete.
+         *
+         * @param value The value found. null if no value was present.
+         */
+        void run(@Nullable byte[] value);
+    }
+
+    /**
+     * Retrieves and returns the value associated with the given key if it exists.
+     * This does not affect the key/value's age for automatic eviction.
+     *
+     * @param key The key to look up.
+     * @param consumer The consumer called when the lookup is complete.
+     */
+    default void lookup(byte[] key, ValueConsumer consumer) {}
+
+    /**
+     * Inserts the given entry into the cache, overwriting any entry that might already exist
+     * against the given key.
+     *
+     * @param key The key to insert.
+     * @param value The value to insert.
+     * @param onComplete Called after the key/value was inserted.
+     */
+    default void put(byte[] key, byte[] value, @Nullable Runnable onComplete) {}
+
+    /**
+     * Evicts an entry from the cache by the specified key.
+     *
+     * @param key The key whose entry must be evicted.
+     * @param onComplete Called after the operation completes.
+     */
+    default void evict(byte[] key, @Nullable Runnable onComplete) {}
+}
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ProcessScopeDependencyProvider.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ProcessScopeDependencyProvider.java
index 68e13eb..75fc160 100644
--- a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ProcessScopeDependencyProvider.java
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ProcessScopeDependencyProvider.java
@@ -58,6 +58,11 @@
         return null;
     }
 
+    @Nullable
+    default PersistentKeyValueCache getPersistentKeyValueCache() {
+        return null;
+    }
+
     // Posts task to the UI thread.
     int TASK_TYPE_UI_THREAD = 1;
     // Posts to a background thread. The task may block.
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 386be04a..05e0781 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1610365490-2d34930da692ba60039f1162f9ee976fe2d1462a.profdata
+chrome-linux-master-1610408110-1b25c741a858a17df9d643531610232aedd2e5da.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 2a48652..2f4b4b7 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1610365490-cff9cfe8ee50bd1daf2a4d54676a0415ea568ae4.profdata
+chrome-mac-master-1610408110-5a618b6e0bdb5b4a60796f5aa8c6f6759f78cdd3.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index bf0f840..648021c 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1610376073-6f28b769fbc009549335371d81a980da57e0e846.profdata
+chrome-win32-master-1610398040-59038019b78fe7d9277311f636fda97275a69be4.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 40f58e1..bf52c0a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1610376073-2cb1d075a5ed746f73a3a1ee8fa077d597a4f418.profdata
+chrome-win64-master-1610387594-34ec7be13acbaa9470ba5363f8af27b0c42697d0.profdata
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index f907062..737d5ea5 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -261,8 +261,6 @@
       "extensions/api/omnibox/omnibox_handler.h",
       "extensions/api/speech/tts_engine_manifest_handler.cc",
       "extensions/api/speech/tts_engine_manifest_handler.h",
-      "extensions/api/spellcheck/spellcheck_handler.cc",
-      "extensions/api/spellcheck/spellcheck_handler.h",
       "extensions/api/storage/storage_schema_manifest_handler.cc",
       "extensions/api/storage/storage_schema_manifest_handler.h",
       "extensions/api/system_indicator/system_indicator_handler.cc",
diff --git a/chrome/common/chrome_content_client_unittest.cc b/chrome/common/chrome_content_client_unittest.cc
index e5983cd..ca15501 100644
--- a/chrome/common/chrome_content_client_unittest.cc
+++ b/chrome/common/chrome_content_client_unittest.cc
@@ -14,6 +14,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/origin_util.h"
 #include "content/public/test/test_utils.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/constants.h"
 #include "ppapi/buildflags/buildflags.h"
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
@@ -103,8 +104,28 @@
   EXPECT_EQ("chrome-extension://abcdefghijklmnopqrstuvwxyzabcdef",
             origin.Serialize());
 
-  EXPECT_TRUE(
-      network::IsUrlPotentiallyTrustworthy(GURL("chrome-native://newtab/")));
+  // IsUrlPotentiallyTrustworthy assertions test for https://crbug.com/734581.
+  constexpr const char* kChromeLayerUrlsRegisteredAsSecure[] = {
+    // The schemes below are registered both as secure and no-access.  Product
+    // code needs to treat such URLs as trustworthy, even though no-access
+    // schemes translate into an opaque origin (which is untrustworthy).
+    "chrome-native://newtab/",
+    "chrome-error://foo/",
+    // The schemes below are registered as secure (but not as no-access).
+    "chrome://foo/",
+    "chrome-untrusted://foo/",
+    "chrome-search://foo/",
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+    "chrome-extension://foo/",
+#endif
+    "devtools://foo/",
+  };
+  for (const std::string& str : kChromeLayerUrlsRegisteredAsSecure) {
+    SCOPED_TRACE(str);
+    GURL url(str);
+    EXPECT_TRUE(base::Contains(url::GetSecureSchemes(), url.scheme()));
+    EXPECT_TRUE(network::IsUrlPotentiallyTrustworthy(url));
+  }
 
   GURL chrome_url(content::GetWebUIURL("dummyurl"));
   EXPECT_TRUE(network::IsUrlPotentiallyTrustworthy(chrome_url));
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 982cd07..0f9c497 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -241,7 +241,13 @@
 // Moves the Extensions "puzzle piece" icon from the title bar into the app menu
 // for web app windows.
 const base::Feature kDesktopPWAsElidedExtensionsMenu{
-    "DesktopPWAsElidedExtensionsMenu", base::FEATURE_DISABLED_BY_DEFAULT};
+  "DesktopPWAsElidedExtensionsMenu",
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
 
 // Replaces the origin text flash in web app titlebars with the name of the app.
 const base::Feature kDesktopPWAsFlashAppNameInsteadOfOrigin{
diff --git a/chrome/common/extensions/api/_manifest_features.json b/chrome/common/extensions/api/_manifest_features.json
index 10b8550..832ce41 100644
--- a/chrome/common/extensions/api/_manifest_features.json
+++ b/chrome/common/extensions/api/_manifest_features.json
@@ -213,10 +213,6 @@
     "channel": "stable",
     "extension_types": "all"
   },
-  "spellcheck": {
-    "channel": "dev",
-    "extension_types": ["extension"]
-  },
   "storage": {
     "channel": "stable",
     "extension_types": [
diff --git a/chrome/common/extensions/api/spellcheck/spellcheck_handler.cc b/chrome/common/extensions/api/spellcheck/spellcheck_handler.cc
deleted file mode 100644
index dfcf568..0000000
--- a/chrome/common/extensions/api/spellcheck/spellcheck_handler.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2013 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/common/extensions/api/spellcheck/spellcheck_handler.h"
-
-#include <memory>
-
-#include "base/strings/utf_string_conversions.h"
-#include "extensions/common/manifest_constants.h"
-
-namespace extensions {
-
-namespace keys = manifest_keys;
-namespace errors = manifest_errors;
-
-SpellcheckDictionaryInfo::SpellcheckDictionaryInfo() {
-}
-
-SpellcheckDictionaryInfo::~SpellcheckDictionaryInfo() {
-}
-
-SpellcheckHandler::SpellcheckHandler() {
-}
-
-SpellcheckHandler::~SpellcheckHandler() {
-}
-
-bool SpellcheckHandler::Parse(Extension* extension, base::string16* error) {
-  const base::DictionaryValue* spellcheck_value = NULL;
-  if (!extension->manifest()->GetDictionary(keys::kSpellcheck,
-                                            &spellcheck_value)) {
-    *error = base::ASCIIToUTF16(errors::kInvalidSpellcheck);
-    return false;
-  }
-  std::unique_ptr<SpellcheckDictionaryInfo> spellcheck_info(
-      new SpellcheckDictionaryInfo);
-  if (!spellcheck_value->HasKey(keys::kSpellcheckDictionaryLanguage) ||
-      !spellcheck_value->GetString(keys::kSpellcheckDictionaryLanguage,
-                                  &spellcheck_info->language)) {
-    *error = base::ASCIIToUTF16(errors::kInvalidSpellcheckDictionaryLanguage);
-    return false;
-  }
-  if (!spellcheck_value->HasKey(keys::kSpellcheckDictionaryLocale) ||
-      !spellcheck_value->GetString(keys::kSpellcheckDictionaryLocale,
-                                  &spellcheck_info->locale)) {
-    *error = base::ASCIIToUTF16(errors::kInvalidSpellcheckDictionaryLocale);
-    return false;
-  }
-  if (!spellcheck_value->HasKey(keys::kSpellcheckDictionaryFormat) ||
-      !spellcheck_value->GetString(keys::kSpellcheckDictionaryFormat,
-                                  &spellcheck_info->format)) {
-    *error = base::ASCIIToUTF16(errors::kInvalidSpellcheckDictionaryFormat);
-    return false;
-  }
-  if (!spellcheck_value->HasKey(keys::kSpellcheckDictionaryPath) ||
-      !spellcheck_value->GetString(keys::kSpellcheckDictionaryPath,
-                                  &spellcheck_info->path)) {
-    *error = base::ASCIIToUTF16(errors::kInvalidSpellcheckDictionaryPath);
-    return false;
-  }
-  extension->SetManifestData(keys::kSpellcheck, std::move(spellcheck_info));
-  return true;
-}
-
-base::span<const char* const> SpellcheckHandler::Keys() const {
-  static constexpr const char* kKeys[] = {keys::kSpellcheck};
-  return kKeys;
-}
-
-}  // namespace extensions
diff --git a/chrome/common/extensions/api/spellcheck/spellcheck_handler.h b/chrome/common/extensions/api/spellcheck/spellcheck_handler.h
deleted file mode 100644
index af71305..0000000
--- a/chrome/common/extensions/api/spellcheck/spellcheck_handler.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2013 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_COMMON_EXTENSIONS_API_SPELLCHECK_SPELLCHECK_HANDLER_H_
-#define CHROME_COMMON_EXTENSIONS_API_SPELLCHECK_SPELLCHECK_HANDLER_H_
-
-#include "base/macros.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/manifest_handler.h"
-
-namespace extensions {
-
-// This structure holds the information parsed by the SpellcheckHandler to be
-// used in the SpellcheckAPI functions. It is stored on the extension.
-struct SpellcheckDictionaryInfo : public extensions::Extension::ManifestData {
-  SpellcheckDictionaryInfo();
-  ~SpellcheckDictionaryInfo() override;
-
-  std::string language;
-  std::string locale;
-  std::string path;
-  std::string format;
-};
-
-// Parses the "spellcheck" manifest key.
-class SpellcheckHandler : public ManifestHandler {
- public:
-  SpellcheckHandler();
-  ~SpellcheckHandler() override;
-
-  bool Parse(Extension* extension, base::string16* error) override;
-
- private:
-  base::span<const char* const> Keys() const override;
-
-  DISALLOW_COPY_AND_ASSIGN(SpellcheckHandler);
-};
-
-}  // namespace extensions
-
-#endif  // CHROME_COMMON_EXTENSIONS_API_SPELLCHECK_SPELLCHECK_HANDLER_H_
diff --git a/chrome/common/extensions/chrome_manifest_handlers.cc b/chrome/common/extensions/chrome_manifest_handlers.cc
index 8d2c3b5..57ba6fb 100644
--- a/chrome/common/extensions/chrome_manifest_handlers.cc
+++ b/chrome/common/extensions/chrome_manifest_handlers.cc
@@ -11,7 +11,6 @@
 #include "chrome/common/extensions/api/commands/commands_handler.h"
 #include "chrome/common/extensions/api/omnibox/omnibox_handler.h"
 #include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h"
-#include "chrome/common/extensions/api/spellcheck/spellcheck_handler.h"
 #include "chrome/common/extensions/api/storage/storage_schema_manifest_handler.h"
 #include "chrome/common/extensions/api/system_indicator/system_indicator_handler.h"
 #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h"
@@ -60,7 +59,6 @@
   registry->RegisterHandler(std::make_unique<OmniboxHandler>());
   registry->RegisterHandler(std::make_unique<OptionsPageManifestHandler>());
   registry->RegisterHandler(std::make_unique<SettingsOverridesHandler>());
-  registry->RegisterHandler(std::make_unique<SpellcheckHandler>());
   registry->RegisterHandler(std::make_unique<StorageSchemaManifestHandler>());
   registry->RegisterHandler(std::make_unique<SystemIndicatorHandler>());
   registry->RegisterHandler(std::make_unique<ThemeHandler>());
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 6ad80524..36d50e9 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -174,6 +174,7 @@
 
 // Management URL for Chrome Supervised Users - version without scheme, used
 // for display.
+// TODO(crbug/1164090): Remove this.
 extern const char kLegacySupervisedUserManagementDisplayURL[];
 
 // The URL for the Learn More page about policies and enterprise enrollment.
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 015f492..9062876 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -247,6 +247,7 @@
 const char kChromeUICrostiniUpgraderUrl[] = "chrome://crostini-upgrader";
 const char kChromeUICryptohomeHost[] = "cryptohome";
 const char kChromeUIDeviceEmulatorHost[] = "device-emulator";
+const char kChromeUIDiagnosticsAppURL[] = "chrome://diagnostics";
 const char kChromeUIIntenetConfigDialogURL[] =
     "chrome://internet-config-dialog/";
 const char kChromeUIIntenetDetailDialogURL[] =
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index e5c9666..4495232d 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -240,6 +240,7 @@
 extern const char kChromeUICrostiniUpgraderUrl[];
 extern const char kChromeUICryptohomeHost[];
 extern const char kChromeUIDeviceEmulatorHost[];
+extern const char kChromeUIDiagnosticsAppURL[];
 extern const char kChromeUIEmojiPickerURL[];
 extern const char kChromeUIEmojiPickerHost[];
 extern const char kChromeUIIntenetConfigDialogURL[];
diff --git a/chrome/renderer/v8_unwinder.cc b/chrome/renderer/v8_unwinder.cc
index 813fa47..1f88919 100644
--- a/chrome/renderer/v8_unwinder.cc
+++ b/chrome/renderer/v8_unwinder.cc
@@ -85,7 +85,7 @@
 
 V8Unwinder::~V8Unwinder() = default;
 
-void V8Unwinder::AddInitialModules(base::ModuleCache* module_cache) {
+void V8Unwinder::InitializeModules(base::ModuleCache* module_cache) {
   // This function must be called only once.
   DCHECK(modules_.empty());
 
@@ -109,7 +109,7 @@
 }
 
 // Update the modules based on what was recorded in |code_ranges_|. The singular
-// embedded code range was already added in in AddInitialModules(). It is
+// embedded code range was already added in in InitializeModules(). It is
 // preserved by the algorithm below, which is why kNonEmbedded is
 // unconditionally passed when creating new modules.
 void V8Unwinder::UpdateModules(base::ModuleCache* module_cache) {
diff --git a/chrome/renderer/v8_unwinder.h b/chrome/renderer/v8_unwinder.h
index a394b822..b275a8b 100644
--- a/chrome/renderer/v8_unwinder.h
+++ b/chrome/renderer/v8_unwinder.h
@@ -22,7 +22,7 @@
   V8Unwinder& operator=(const V8Unwinder&) = delete;
 
   // Unwinder:
-  void AddInitialModules(base::ModuleCache* module_cache) override;
+  void InitializeModules(base::ModuleCache* module_cache) override;
   void OnStackCapture() override;
   void UpdateModules(base::ModuleCache* module_cache) override;
   bool CanUnwindFrom(const base::Frame& current_frame) const override;
diff --git a/chrome/renderer/v8_unwinder_unittest.cc b/chrome/renderer/v8_unwinder_unittest.cc
index add0565b..acabe50 100644
--- a/chrome/renderer/v8_unwinder_unittest.cc
+++ b/chrome/renderer/v8_unwinder_unittest.cc
@@ -222,7 +222,7 @@
   V8Unwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
 
   v8::MemoryRange embedded_code_range;
   v8_environment.isolate()->GetEmbeddedCodeRange(
@@ -239,7 +239,7 @@
   UpdateModulesTestUnwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
 
   unwinder.SetCodePages({{reinterpret_cast<void*>(1), 10},
                          GetEmbeddedCodeRange(v8_environment.isolate())});
@@ -264,7 +264,7 @@
   UpdateModulesTestUnwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
 
   const int kDefaultCapacity = v8::Isolate::kMinCodePagesBufferSize;
   std::vector<v8::MemoryRange> code_pages;
@@ -291,7 +291,7 @@
   UpdateModulesTestUnwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
   unwinder.SetCodePages({{reinterpret_cast<void*>(1), 10},
                          GetEmbeddedCodeRange(v8_environment.isolate())});
   unwinder.OnStackCapture();
@@ -312,7 +312,7 @@
   UpdateModulesTestUnwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
 
   unwinder.SetCodePages({{reinterpret_cast<void*>(100), 10},
                          GetEmbeddedCodeRange(v8_environment.isolate())});
@@ -338,7 +338,7 @@
   UpdateModulesTestUnwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
 
   unwinder.SetCodePages({{reinterpret_cast<void*>(1), 10},
                          GetEmbeddedCodeRange(v8_environment.isolate())});
@@ -362,7 +362,7 @@
   UpdateModulesTestUnwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
 
   unwinder.SetCodePages({{reinterpret_cast<void*>(1), 10},
                          GetEmbeddedCodeRange(v8_environment.isolate())});
@@ -387,7 +387,7 @@
   UpdateModulesTestUnwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
 
   unwinder.SetCodePages({{{reinterpret_cast<void*>(1), 10},
                           GetEmbeddedCodeRange(v8_environment.isolate())}});
@@ -408,7 +408,7 @@
   UpdateModulesTestUnwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
 
   unwinder.SetCodePages({{{reinterpret_cast<void*>(1), 10},
                           {reinterpret_cast<void*>(100), 10},
@@ -429,7 +429,7 @@
   UpdateModulesTestUnwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
 
   const int kDefaultCapacity = v8::Isolate::kMinCodePagesBufferSize;
 
@@ -466,7 +466,7 @@
   UpdateModulesTestUnwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
 
   const int kDefaultCapacity = v8::Isolate::kMinCodePagesBufferSize;
   const int kCodePages = kDefaultCapacity * 3;
@@ -500,7 +500,7 @@
   UpdateModulesTestUnwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
 
   unwinder.SetCodePages({{reinterpret_cast<void*>(1), 10},
                          GetEmbeddedCodeRange(v8_environment.isolate())});
@@ -518,7 +518,7 @@
   UpdateModulesTestUnwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
 
   unwinder.SetCodePages({GetEmbeddedCodeRange(v8_environment.isolate())});
   unwinder.OnStackCapture();
@@ -536,7 +536,7 @@
   UpdateModulesTestUnwinder unwinder(v8_environment.isolate());
   base::ModuleCache module_cache;
 
-  unwinder.AddInitialModules(&module_cache);
+  unwinder.InitializeModules(&module_cache);
 
   // Insert a non-native module to potentially exercise the Module comparator.
   unwinder.SetCodePages({{reinterpret_cast<void*>(1), 10},
diff --git a/chrome/services/machine_learning/BUILD.gn b/chrome/services/machine_learning/BUILD.gn
index 644190e..2aefa002 100644
--- a/chrome/services/machine_learning/BUILD.gn
+++ b/chrome/services/machine_learning/BUILD.gn
@@ -14,6 +14,7 @@
     ":metrics",
     "//base",
     "//chrome:strings",
+    "//chrome/common:buildflags",
     "//mojo/public/cpp/bindings",
   ]
 
@@ -69,6 +70,8 @@
     ":metrics",
     "//base",
     "//base/test:test_support",
+    "//chrome/common:buildflags",
+    "//chrome/common:constants",
     "//chrome/services/machine_learning/public/cpp:cpp",
     "//chrome/services/machine_learning/public/cpp:test_support",
     "//testing/gtest",
diff --git a/chrome/services/machine_learning/in_process_tflite_predictor_unittest.cc b/chrome/services/machine_learning/in_process_tflite_predictor_unittest.cc
index ffdee8f..e7e9168 100644
--- a/chrome/services/machine_learning/in_process_tflite_predictor_unittest.cc
+++ b/chrome/services/machine_learning/in_process_tflite_predictor_unittest.cc
@@ -6,10 +6,14 @@
 
 #include <string>
 
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/task_environment.h"
+#include "build/build_config.h"
 #include "chrome/common/chrome_paths.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -35,17 +39,19 @@
   InProcessTFLitePredictorTest() = default;
   ~InProcessTFLitePredictorTest() override = default;
 
-  // Returns TFLite test model path
+  // Returns TFLite test model path.
   std::string GetTFLiteTestPath() {
-    // Location of generated test data (<(PROGRAM_DIR)/test_data).
-    base::FilePath g_gen_test_data_directory;
+    base::FilePath model_file_path;
 
-    base::PathService::Get(chrome::DIR_GEN_TEST_DATA,
-                           &g_gen_test_data_directory);
-    g_gen_test_data_directory =
-        g_gen_test_data_directory.Append("simple_test.tflite");
+    EXPECT_TRUE(
+        base::PathService::Get(base::DIR_SOURCE_ROOT, &model_file_path));
 
-    return g_gen_test_data_directory.value().c_str();
+    model_file_path = model_file_path.Append(FILE_PATH_LITERAL("chrome"))
+                          .Append(FILE_PATH_LITERAL("test"))
+                          .Append(FILE_PATH_LITERAL("data"))
+                          .Append(FILE_PATH_LITERAL("simple_test.tflite"));
+    EXPECT_TRUE(base::PathExists(model_file_path));
+    return model_file_path.value().c_str();
   }
 };
 
diff --git a/chrome/services/sharing/nearby/platform/bluetooth_server_socket.cc b/chrome/services/sharing/nearby/platform/bluetooth_server_socket.cc
index 58c4cd0..2c85c7c 100644
--- a/chrome/services/sharing/nearby/platform/bluetooth_server_socket.cc
+++ b/chrome/services/sharing/nearby/platform/bluetooth_server_socket.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/services/sharing/nearby/platform/bluetooth_server_socket.h"
 
+#include "base/logging.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "chrome/services/sharing/nearby/platform/bluetooth_socket.h"
@@ -19,7 +20,9 @@
           base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})),
       server_socket_(std::move(server_socket), task_runner_) {}
 
-BluetoothServerSocket::~BluetoothServerSocket() = default;
+BluetoothServerSocket::~BluetoothServerSocket() {
+  Close();
+}
 
 std::unique_ptr<api::BluetoothSocket> BluetoothServerSocket::Accept() {
   bluetooth::mojom::AcceptConnectionResultPtr result;
@@ -35,7 +38,14 @@
 }
 
 Exception BluetoothServerSocket::Close() {
-  server_socket_.reset();
+  if (server_socket_) {
+    if (server_socket_->Disconnect()) {
+      VLOG(1) << "Successfully tore down Nearby Bluetooth server socket.";
+    } else {
+      LOG(ERROR) << "Failed to tear down Nearby Bluetooth server socket.";
+    }
+    server_socket_.reset();
+  }
   return {Exception::kSuccess};
 }
 
diff --git a/chrome/services/sharing/nearby/platform/bluetooth_server_socket_unittest.cc b/chrome/services/sharing/nearby/platform/bluetooth_server_socket_unittest.cc
index be332f8..936de61 100644
--- a/chrome/services/sharing/nearby/platform/bluetooth_server_socket_unittest.cc
+++ b/chrome/services/sharing/nearby/platform/bluetooth_server_socket_unittest.cc
@@ -65,6 +65,9 @@
   void Accept(AcceptCallback callback) override {
     std::move(callback).Run(std::move(accept_connection_result_));
   }
+  void Disconnect(DisconnectCallback callback) override {
+    std::move(callback).Run();
+  }
 
   bluetooth::mojom::AcceptConnectionResultPtr accept_connection_result_;
   base::OnceClosure on_destroy_callback_;
diff --git a/chrome/services/sharing/nearby/test_support/fake_adapter.cc b/chrome/services/sharing/nearby/test_support/fake_adapter.cc
index b1efe1b6..72f1e4a 100644
--- a/chrome/services/sharing/nearby/test_support/fake_adapter.cc
+++ b/chrome/services/sharing/nearby/test_support/fake_adapter.cc
@@ -72,6 +72,9 @@
   void Accept(AcceptCallback callback) override {
     std::move(callback).Run(/*result=*/nullptr);
   }
+  void Disconnect(DisconnectCallback callback) override {
+    std::move(callback).Run();
+  }
 };
 
 }  // namespace
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index ac2e26b..d6004b2 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1047,6 +1047,7 @@
       "../browser/extensions/protocol_handler_apitest.cc",
       "../browser/fast_shutdown_browsertest.cc",
       "../browser/favicon/content_favicon_driver_browsertest.cc",
+      "../browser/federated_learning/floc_eligibility_browsertest.cc",
       "../browser/federated_learning/floc_id_provider_browsertest.cc",
       "../browser/federated_learning/floc_origin_trial_browsertest.cc",
       "../browser/first_run/first_run_browsertest.cc",
@@ -2782,6 +2783,7 @@
         "../browser/ui/ash/accelerator_commands_browsertest.cc",
         "../browser/ui/ash/assistant/assistant_context_browsertest.cc",
         "../browser/ui/ash/back_gesture_browsertest.cc",
+        "../browser/ui/ash/capture_mode_browsertest.cc",
         "../browser/ui/ash/chrome_new_window_client_browsertest.cc",
         "../browser/ui/ash/chrome_screenshot_grabber_browsertest.cc",
         "../browser/ui/ash/clipboard_history_browsertest.cc",
@@ -3459,7 +3461,6 @@
     "../browser/component_updater/first_party_sets_component_installer_unittest.cc",
     "../browser/component_updater/floc_component_installer_unittest.cc",
     "../browser/component_updater/games_component_installer_unittest.cc",
-    "../browser/component_updater/optimization_hints_component_installer_unittest.cc",
     "../browser/component_updater/origin_trials_component_installer_unittest.cc",
     "../browser/component_updater/subresource_filter_component_installer_unittest.cc",
     "../browser/component_updater/trust_token_key_commitments_component_installer_unittest.cc",
@@ -3500,6 +3501,7 @@
     "../browser/enterprise/util/affiliation_unittest.cc",
     "../browser/enterprise/util/managed_browser_utils_unittest.cc",
     "../browser/external_protocol/external_protocol_handler_unittest.cc",
+    "../browser/federated_learning/floc_eligibility_unittest.cc",
     "../browser/federated_learning/floc_event_logger_unittest.cc",
     "../browser/federated_learning/floc_id_provider_unittest.cc",
     "../browser/federated_learning/floc_remote_permission_service_unittest.cc",
diff --git a/chrome/test/base/testing_browser_process.cc b/chrome/test/base/testing_browser_process.cc
index 38f6dbd..77d3dfb 100644
--- a/chrome/test/base/testing_browser_process.cc
+++ b/chrome/test/base/testing_browser_process.cc
@@ -30,7 +30,6 @@
 #include "chrome/test/base/testing_browser_process_platform_part.h"
 #include "components/federated_learning/floc_sorting_lsh_clusters_service.h"
 #include "components/network_time/network_time_tracker.h"
-#include "components/optimization_guide/core/optimization_guide_service.h"
 #include "components/permissions/permissions_client.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/prefs/pref_service.h"
@@ -286,11 +285,6 @@
   return floc_sorting_lsh_clusters_service_.get();
 }
 
-optimization_guide::OptimizationGuideService*
-TestingBrowserProcess::optimization_guide_service() {
-  return optimization_guide_service_.get();
-}
-
 BrowserProcessPlatformPart* TestingBrowserProcess::platform_part() {
   return platform_part_.get();
 }
@@ -514,12 +508,6 @@
   floc_sorting_lsh_clusters_service_.swap(service);
 }
 
-void TestingBrowserProcess::SetOptimizationGuideService(
-    std::unique_ptr<optimization_guide::OptimizationGuideService>
-        optimization_guide_service) {
-  optimization_guide_service_.swap(optimization_guide_service);
-}
-
 void TestingBrowserProcess::SetRapporServiceImpl(
     rappor::RapporServiceImpl* rappor_service) {
   rappor_service_ = rappor_service;
diff --git a/chrome/test/base/testing_browser_process.h b/chrome/test/base/testing_browser_process.h
index b17ec0a3..cdd502d1 100644
--- a/chrome/test/base/testing_browser_process.h
+++ b/chrome/test/base/testing_browser_process.h
@@ -103,8 +103,6 @@
       override;
   federated_learning::FlocSortingLshClustersService*
   floc_sorting_lsh_clusters_service() override;
-  optimization_guide::OptimizationGuideService* optimization_guide_service()
-      override;
   BrowserProcessPlatformPart* platform_part() override;
 
   extensions::EventRouterForwarder* extension_event_router_forwarder() override;
@@ -157,9 +155,6 @@
   void SetFlocSortingLshClustersService(
       std::unique_ptr<federated_learning::FlocSortingLshClustersService>
           service);
-  void SetOptimizationGuideService(
-      std::unique_ptr<optimization_guide::OptimizationGuideService>
-          optimization_guide_service);
   void SetSharedURLLoaderFactory(
       scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory);
   void SetNotificationUIManager(
@@ -211,8 +206,6 @@
       subresource_filter_ruleset_service_;
   std::unique_ptr<federated_learning::FlocSortingLshClustersService>
       floc_sorting_lsh_clusters_service_;
-  std::unique_ptr<optimization_guide::OptimizationGuideService>
-      optimization_guide_service_;
 
   std::unique_ptr<network_time::NetworkTimeTracker> network_time_tracker_;
 
diff --git a/chrome/test/chromedriver/commands.cc b/chrome/test/chromedriver/commands.cc
index 8e402b0..277ed1ea 100644
--- a/chrome/test/chromedriver/commands.cc
+++ b/chrome/test/chromedriver/commands.cc
@@ -87,7 +87,7 @@
 namespace {
 
 void OnGetSession(const base::WeakPtr<size_t>& session_remaining_count,
-                  const base::Closure& all_get_session_func,
+                  const base::RepeatingClosure& all_get_session_func,
                   base::ListValue* session_list,
                   const Status& status,
                   std::unique_ptr<base::Value> value,
@@ -144,7 +144,7 @@
 namespace {
 
 void OnSessionQuit(const base::WeakPtr<size_t>& quit_remaining_count,
-                   const base::Closure& all_quit_func,
+                   const base::RepeatingClosure& all_quit_func,
                    const Status& status,
                    std::unique_ptr<base::Value> value,
                    const std::string& session_id,
@@ -205,7 +205,7 @@
     std::unique_ptr<base::DictionaryValue> params,
     scoped_refptr<base::SingleThreadTaskRunner> cmd_task_runner,
     const CommandCallback& callback_on_cmd,
-    const base::Closure& terminate_on_cmd) {
+    const base::RepeatingClosure& terminate_on_cmd) {
   Session* session = GetThreadLocalSession();
 
   if (!session) {
diff --git a/chrome/test/chromedriver/element_commands.h b/chrome/test/chromedriver/element_commands.h
index 01cf33137..d74c7a8 100644
--- a/chrome/test/chromedriver/element_commands.h
+++ b/chrome/test/chromedriver/element_commands.h
@@ -20,12 +20,12 @@
 class Timeout;
 class WebView;
 
-typedef base::Callback<Status(Session* session,
-                              WebView* web_view,
-                              const std::string&,
-                              const base::DictionaryValue&,
-                              std::unique_ptr<base::Value>*)>
-    ElementCommand;
+using ElementCommand =
+    base::RepeatingCallback<Status(Session* session,
+                                   WebView* web_view,
+                                   const std::string&,
+                                   const base::DictionaryValue&,
+                                   std::unique_ptr<base::Value>*)>;
 
 // Execute a command on a specific element.
 Status ExecuteElementCommand(
diff --git a/chrome/test/chromedriver/server/http_handler.cc b/chrome/test/chromedriver/server/http_handler.cc
index 7a16c75..8785448 100644
--- a/chrome/test/chromedriver/server/http_handler.cc
+++ b/chrome/test/chromedriver/server/http_handler.cc
@@ -311,12 +311,14 @@
           kGet, "session/:sessionId/element/:id/enabled",
           WrapToCommand("IsElementEnabled",
                         base::BindRepeating(&ExecuteIsElementEnabled))),
-      CommandMapping(kGet, "session/:sessionId/element/:id/computedlabel",
-                     WrapToCommand("GetComputedLabel",
-                                   base::Bind(&ExecuteGetComputedLabel))),
-      CommandMapping(kGet, "session/:sessionId/element/:id/computedrole",
-                     WrapToCommand("GetComputedRole",
-                                   base::Bind(&ExecuteGetComputedRole))),
+      CommandMapping(
+          kGet, "session/:sessionId/element/:id/computedlabel",
+          WrapToCommand("GetComputedLabel",
+                        base::BindRepeating(&ExecuteGetComputedLabel))),
+      CommandMapping(
+          kGet, "session/:sessionId/element/:id/computedrole",
+          WrapToCommand("GetComputedRole",
+                        base::BindRepeating(&ExecuteGetComputedRole))),
       CommandMapping(kPost, "session/:sessionId/element/:id/click",
                      WrapToCommand("ClickElement",
                                    base::BindRepeating(&ExecuteClickElement))),
@@ -1014,16 +1016,17 @@
 Command HttpHandler::WrapToCommand(const char* name,
                                    const WindowCommand& window_command,
                                    bool w3c_standard_command) {
-  return WrapToCommand(name, base::Bind(&ExecuteWindowCommand, window_command),
-                       w3c_standard_command);
+  return WrapToCommand(
+      name, base::BindRepeating(&ExecuteWindowCommand, window_command),
+      w3c_standard_command);
 }
 
 Command HttpHandler::WrapToCommand(const char* name,
                                    const ElementCommand& element_command,
                                    bool w3c_standard_command) {
-  return WrapToCommand(name,
-                       base::Bind(&ExecuteElementCommand, element_command),
-                       w3c_standard_command);
+  return WrapToCommand(
+      name, base::BindRepeating(&ExecuteElementCommand, element_command),
+      w3c_standard_command);
 }
 
 void HttpHandler::HandleCommand(
diff --git a/chrome/test/chromedriver/window_commands.h b/chrome/test/chromedriver/window_commands.h
index 8299ab75..ae00910 100644
--- a/chrome/test/chromedriver/window_commands.h
+++ b/chrome/test/chromedriver/window_commands.h
@@ -22,12 +22,12 @@
 class Timeout;
 class WebView;
 
-typedef base::Callback<Status(Session* session,
-                              WebView* web_view,
-                              const base::DictionaryValue&,
-                              std::unique_ptr<base::Value>*,
-                              Timeout*)>
-    WindowCommand;
+using WindowCommand =
+    base::RepeatingCallback<Status(Session* session,
+                                   WebView* web_view,
+                                   const base::DictionaryValue&,
+                                   std::unique_ptr<base::Value>*,
+                                   Timeout*)>;
 
 // Execute a Window Command on the target window.
 Status ExecuteWindowCommand(const WindowCommand& command,
diff --git a/chrome/test/data/pdf/navigator_test.js b/chrome/test/data/pdf/navigator_test.js
index aa9ce60..76cda3f 100644
--- a/chrome/test/data/pdf/navigator_test.js
+++ b/chrome/test/data/pdf/navigator_test.js
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {eventToPromise} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/_test_resources/webui/test_util.m.js';
 import {NavigatorDelegate, PdfNavigator, WindowOpenDisposition} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/navigator.js';
 import {OpenPdfParamsParser} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/open_pdf_params_parser.js';
 import {PDFScriptingAPI} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_scripting_api.js';
@@ -56,12 +55,12 @@
  * @param {!MockViewportChangedCallback} viewportChangedCallback
  * @param {!MockNavigatorDelegate} navigatorDelegate
  */
-function doNavigationUrlTest(
+async function doNavigationUrlTest(
     navigator, url, disposition, expectedResultUrl, viewportChangedCallback,
     navigatorDelegate) {
   viewportChangedCallback.reset();
   navigatorDelegate.reset();
-  navigator.navigate(url, disposition);
+  await navigator.navigate(url, disposition);
   chrome.test.assertFalse(viewportChangedCallback.wasCalled);
   chrome.test.assertEq(expectedResultUrl, navigatorDelegate.url);
   if (expectedResultUrl === undefined) {
@@ -89,7 +88,7 @@
  * @param {string} url
  * @param {(string|undefined)} expectedResultUrl
  */
-function doNavigationUrlTests(originalUrl, url, expectedResultUrl) {
+async function doNavigationUrlTests(originalUrl, url, expectedResultUrl) {
   const mockWindow = new MockElement(100, 100, null);
   const mockSizer = new MockSizer();
   const mockViewportChangedCallback = new MockViewportChangedCallback();
@@ -105,13 +104,13 @@
   const navigator =
       new PdfNavigator(originalUrl, viewport, paramsParser, navigatorDelegate);
 
-  doNavigationUrlTest(
+  await doNavigationUrlTest(
       navigator, url, WindowOpenDisposition.CURRENT_TAB, expectedResultUrl,
       mockViewportChangedCallback, navigatorDelegate);
-  doNavigationUrlTest(
+  await doNavigationUrlTest(
       navigator, url, WindowOpenDisposition.NEW_BACKGROUND_TAB,
       expectedResultUrl, mockViewportChangedCallback, navigatorDelegate);
-  doNavigationUrlTest(
+  await doNavigationUrlTest(
       navigator, url, WindowOpenDisposition.NEW_WINDOW, expectedResultUrl,
       mockViewportChangedCallback, navigatorDelegate);
 }
@@ -154,47 +153,36 @@
     viewport.setZoom(1);
 
     mockCallback.reset();
-    let navigatingDone =
-        eventToPromise('navigate-for-testing', navigator.getEventTarget());
     // This should move viewport to page 0.
-    navigator.navigate(url + '#US', WindowOpenDisposition.CURRENT_TAB);
-    await navigatingDone;
+    await navigator.navigate(url + '#US', WindowOpenDisposition.CURRENT_TAB);
     chrome.test.assertTrue(mockCallback.wasCalled);
     chrome.test.assertEq(0, viewport.position.x);
     chrome.test.assertEq(0, viewport.position.y);
 
     mockCallback.reset();
     navigatorDelegate.reset();
-    navigatingDone =
-        eventToPromise('navigate-for-testing', navigator.getEventTarget());
     // This should open "http://xyz.pdf#US" in a new tab. So current tab
     // viewport should not update and viewport position should remain same.
-    navigator.navigate(url + '#US', WindowOpenDisposition.NEW_BACKGROUND_TAB);
-    await navigatingDone;
+    await navigator.navigate(
+        url + '#US', WindowOpenDisposition.NEW_BACKGROUND_TAB);
     chrome.test.assertFalse(mockCallback.wasCalled);
     chrome.test.assertTrue(navigatorDelegate.navigateInNewTabCalled);
     chrome.test.assertEq(0, viewport.position.x);
     chrome.test.assertEq(0, viewport.position.y);
 
     mockCallback.reset();
-    navigatingDone =
-        eventToPromise('navigate-for-testing', navigator.getEventTarget());
     // This should move viewport to page 2.
-    navigator.navigate(url + '#UY', WindowOpenDisposition.CURRENT_TAB);
-    await navigatingDone;
+    await navigator.navigate(url + '#UY', WindowOpenDisposition.CURRENT_TAB);
     chrome.test.assertTrue(mockCallback.wasCalled);
     chrome.test.assertEq(0, viewport.position.x);
     chrome.test.assertEq(300, viewport.position.y);
 
     mockCallback.reset();
     navigatorDelegate.reset();
-    navigatingDone =
-        eventToPromise('navigate-for-testing', navigator.getEventTarget());
     // #ABC is not a named destination in the page so viewport should not
     // update, and the viewport position should remain same as testNavigate3's
     // navigating results, as this link will open in the same tab.
-    navigator.navigate(url + '#ABC', WindowOpenDisposition.CURRENT_TAB);
-    await navigatingDone;
+    await navigator.navigate(url + '#ABC', WindowOpenDisposition.CURRENT_TAB);
     chrome.test.assertFalse(mockCallback.wasCalled);
     chrome.test.assertTrue(navigatorDelegate.navigateInCurrentTabCalled);
     chrome.test.assertEq(0, viewport.position.x);
@@ -207,44 +195,45 @@
    * a valid scheme, so the navigator must determine the url by following
    * similar heuristics as Adobe Acrobat Reader.
    */
-  function testNavigateForLinksWithoutScheme() {
+  async function testNavigateForLinksWithoutScheme() {
     const url = 'http://www.example.com/subdir/xyz.pdf';
 
     // Sanity check.
-    doNavigationUrlTests(
+    await doNavigationUrlTests(
         url, 'https://www.foo.com/bar.pdf', 'https://www.foo.com/bar.pdf');
 
     // Open relative links.
-    doNavigationUrlTests(
+    await doNavigationUrlTests(
         url, 'foo/bar.pdf', 'http://www.example.com/subdir/foo/bar.pdf');
-    doNavigationUrlTests(
+    await doNavigationUrlTests(
         url, 'foo.com/bar.pdf',
         'http://www.example.com/subdir/foo.com/bar.pdf');
-    doNavigationUrlTests(
+    await doNavigationUrlTests(
         url, '../www.foo.com/bar.pdf',
         'http://www.example.com/www.foo.com/bar.pdf');
 
     // Open an absolute link.
-    doNavigationUrlTests(
+    await doNavigationUrlTests(
         url, '/foodotcom/bar.pdf', 'http://www.example.com/foodotcom/bar.pdf');
 
     // Open a http url without a scheme.
-    doNavigationUrlTests(
+    await doNavigationUrlTests(
         url, 'www.foo.com/bar.pdf', 'http://www.foo.com/bar.pdf');
 
     // Test three dots.
-    doNavigationUrlTests(
+    await doNavigationUrlTests(
         url, '.../bar.pdf', 'http://www.example.com/subdir/.../bar.pdf');
 
     // Test forward slashes.
-    doNavigationUrlTests(url, '..\\bar.pdf', 'http://www.example.com/bar.pdf');
-    doNavigationUrlTests(
+    await doNavigationUrlTests(
+        url, '..\\bar.pdf', 'http://www.example.com/bar.pdf');
+    await doNavigationUrlTests(
         url, '.\\bar.pdf', 'http://www.example.com/subdir/bar.pdf');
-    doNavigationUrlTests(
+    await doNavigationUrlTests(
         url, '\\bar.pdf', 'http://www.example.com/subdir//bar.pdf');
 
     // Regression test for https://crbug.com/569040
-    doNavigationUrlTests(
+    await doNavigationUrlTests(
         url, 'http://something.else/foo#page=5',
         'http://something.else/foo#page=5');
 
@@ -254,31 +243,31 @@
    * Test opening a url in the same tab, in a new tab, and in a new window with
    * a file:/// url as the current location.
    */
-  function testNavigateFromLocalFile() {
+  async function testNavigateFromLocalFile() {
     const url = 'file:///some/path/to/myfile.pdf';
 
     // Open an absolute link.
-    doNavigationUrlTests(
+    await doNavigationUrlTests(
         url, '/foodotcom/bar.pdf', 'file:///foodotcom/bar.pdf');
 
     chrome.test.succeed();
   },
 
-  function testNavigateInvalidUrls() {
+  async function testNavigateInvalidUrls() {
     const url = 'https://example.com/some-web-document.pdf';
 
     // From non-file: to file:
-    doNavigationUrlTests(url, 'file:///bar.pdf', undefined);
+    await doNavigationUrlTests(url, 'file:///bar.pdf', undefined);
 
-    doNavigationUrlTests(url, 'chrome://version', undefined);
+    await doNavigationUrlTests(url, 'chrome://version', undefined);
 
-    doNavigationUrlTests(
+    await doNavigationUrlTests(
         url, 'javascript:// this is not a document.pdf', undefined);
 
-    doNavigationUrlTests(
+    await doNavigationUrlTests(
         url, 'this-is-not-a-valid-scheme://path.pdf', undefined);
 
-    doNavigationUrlTests(url, '', undefined);
+    await doNavigationUrlTests(url, '', undefined);
 
     chrome.test.succeed();
   }
diff --git a/chrome/test/data/pdf/params_parser_test.js b/chrome/test/data/pdf/params_parser_test.js
index 3728853..bc2f17f 100644
--- a/chrome/test/data/pdf/params_parser_test.js
+++ b/chrome/test/data/pdf/params_parser_test.js
@@ -11,7 +11,7 @@
   /**
    * Test named destinations.
    */
-  function testParamsParser() {
+  async function testParamsParser() {
     const paramsParser = new OpenPdfParamsParser(function(destination) {
       // Set the dummy viewport dimensions for calculating the zoom level for
       // view destination with 'FitR' type.
@@ -20,259 +20,248 @@
       if (destination === 'RU') {
         return Promise.resolve(
             {messageId: 'getNamedDestination_1', pageNumber: 26});
-      } else if (destination === 'US') {
+      }
+      if (destination === 'US') {
         return Promise.resolve(
             {messageId: 'getNamedDestination_2', pageNumber: 0});
-      } else if (destination === 'UY') {
+      }
+      if (destination === 'UY') {
         return Promise.resolve(
             {messageId: 'getNamedDestination_3', pageNumber: 22});
-      } else if (destination === 'DestWithXYZ') {
+      }
+      if (destination === 'DestWithXYZ') {
         return Promise.resolve({
           messageId: 'getNamedDestination_4',
           namedDestinationView: 'XYZ,111,222,1.7',
           pageNumber: 10
         });
-      } else if (destination === 'DestWithXYZAtZoom0') {
+      }
+      if (destination === 'DestWithXYZAtZoom0') {
         return Promise.resolve({
           messageId: 'getNamedDestination_5',
           namedDestinationView: 'XYZ,111,222,0',
           pageNumber: 10
         });
-      } else if (destination === 'DestWithXYZWithNullParameter') {
+      }
+      if (destination === 'DestWithXYZWithNullParameter') {
         return Promise.resolve({
           messageId: 'getNamedDestination_6',
           namedDestinationView: 'XYZ,111,null,1.7',
           pageNumber: 13
         });
-      } else if (destination === 'DestWithFitR') {
+      }
+      if (destination === 'DestWithFitR') {
         return Promise.resolve({
           messageId: 'getNamedDestination_7',
           namedDestinationView: 'FitR,20,100,120,300',
           pageNumber: 0
         });
-      } else if (destination === 'DestWithFitRReversedCoordinates') {
+      }
+      if (destination === 'DestWithFitRReversedCoordinates') {
         return Promise.resolve({
           messageId: 'getNamedDestination_8',
           namedDestinationView: 'FitR,120,300,20,100',
           pageNumber: 0
         });
-      } else if (destination === 'DestWithFitRWithNull') {
+      }
+      if (destination === 'DestWithFitRWithNull') {
         return Promise.resolve({
           messageId: 'getNamedDestination_9',
           namedDestinationView: 'FitR,null,100,100,300',
           pageNumber: 0
         });
-      } else {
-        return Promise.resolve(
-            {messageId: 'getNamedDestination_10', pageNumber: -1});
       }
+      return Promise.resolve(
+          {messageId: 'getNamedDestination_10', pageNumber: -1});
     });
 
     const url = 'http://xyz.pdf';
 
     // Checking #nameddest.
-    paramsParser.getViewportFromUrlParams(`${url}#RU`, function(params) {
-      chrome.test.assertEq(26, params.page);
-    });
+    let params = await paramsParser.getViewportFromUrlParams(`${url}#RU`);
+    chrome.test.assertEq(26, params.page);
 
     // Checking #nameddest=name.
-    paramsParser.getViewportFromUrlParams(
-        `${url}#nameddest=US`, function(params) {
-          chrome.test.assertEq(0, params.page);
-        });
+    params = await paramsParser.getViewportFromUrlParams(`${url}#nameddest=US`);
+    chrome.test.assertEq(0, params.page);
 
     // Checking #page=pagenum nameddest.The document first page has a pagenum
     // value of 1.
-    paramsParser.getViewportFromUrlParams(`${url}#page=6`, function(params) {
-      chrome.test.assertEq(5, params.page);
-    });
+    params = await paramsParser.getViewportFromUrlParams(`${url}#page=6`);
+    chrome.test.assertEq(5, params.page);
 
     // Checking #zoom=scale.
-    paramsParser.getViewportFromUrlParams(`${url}#zoom=200`, function(params) {
-      chrome.test.assertEq(2, params.zoom);
-    });
+    params = await paramsParser.getViewportFromUrlParams(`${url}#zoom=200`);
+    chrome.test.assertEq(2, params.zoom);
 
     // Checking #zoom=scale,left,top.
-    paramsParser.getViewportFromUrlParams(
-        `${url}#zoom=200,100,200`, function(params) {
-          chrome.test.assertEq(2, params.zoom);
-          chrome.test.assertEq(100, params.position.x);
-          chrome.test.assertEq(200, params.position.y);
-        });
+    params =
+        await paramsParser.getViewportFromUrlParams(`${url}#zoom=200,100,200`);
+    chrome.test.assertEq(2, params.zoom);
+    chrome.test.assertEq(100, params.position.x);
+    chrome.test.assertEq(200, params.position.y);
 
     // Checking #nameddest=name and zoom=scale.
-    paramsParser.getViewportFromUrlParams(
-        `${url}#nameddest=UY&zoom=150`, function(params) {
-          chrome.test.assertEq(22, params.page);
-          chrome.test.assertEq(1.5, params.zoom);
-        });
+    params = await paramsParser.getViewportFromUrlParams(
+        `${url}#nameddest=UY&zoom=150`);
+    chrome.test.assertEq(22, params.page);
+    chrome.test.assertEq(1.5, params.zoom);
 
     // Checking #page=pagenum and zoom=scale.
-    paramsParser.getViewportFromUrlParams(
-        `${url}#page=2&zoom=250`, function(params) {
-          chrome.test.assertEq(1, params.page);
-          chrome.test.assertEq(2.5, params.zoom);
-        });
+    params =
+        await paramsParser.getViewportFromUrlParams(`${url}#page=2&zoom=250`);
+    chrome.test.assertEq(1, params.page);
+    chrome.test.assertEq(2.5, params.zoom);
 
     // Checking #nameddest=name and zoom=scale,left,top.
-    paramsParser.getViewportFromUrlParams(
-        `${url}#nameddest=UY&zoom=150,100,200`, function(params) {
-          chrome.test.assertEq(22, params.page);
-          chrome.test.assertEq(1.5, params.zoom);
-          chrome.test.assertEq(100, params.position.x);
-          chrome.test.assertEq(200, params.position.y);
-        });
+    params = await paramsParser.getViewportFromUrlParams(
+        `${url}#nameddest=UY&zoom=150,100,200`);
+    chrome.test.assertEq(22, params.page);
+    chrome.test.assertEq(1.5, params.zoom);
+    chrome.test.assertEq(100, params.position.x);
+    chrome.test.assertEq(200, params.position.y);
 
     // Checking #page=pagenum and zoom=scale,left,top.
-    paramsParser.getViewportFromUrlParams(
-        `${url}#page=2&zoom=250,100,200`, function(params) {
-          chrome.test.assertEq(1, params.page);
-          chrome.test.assertEq(2.5, params.zoom);
-          chrome.test.assertEq(100, params.position.x);
-          chrome.test.assertEq(200, params.position.y);
-        });
+    params = await paramsParser.getViewportFromUrlParams(
+        `${url}#page=2&zoom=250,100,200`);
+    chrome.test.assertEq(1, params.page);
+    chrome.test.assertEq(2.5, params.zoom);
+    chrome.test.assertEq(100, params.position.x);
+    chrome.test.assertEq(200, params.position.y);
 
     // Checking #nameddest=name with a nameddest that specifies the view fit
     // type is "XYZ" with multiple valid parameters.
-    paramsParser.getViewportFromUrlParams(
-        `${url}#nameddest=DestWithXYZ`, function(params) {
-          chrome.test.assertEq(10, params.page);
-          chrome.test.assertEq(1.7, params.zoom);
-          chrome.test.assertEq(111, params.position.x);
-          chrome.test.assertEq(222, params.position.y);
-          chrome.test.assertEq(undefined, params.viewPosition);
-        });
+    params = await paramsParser.getViewportFromUrlParams(
+        `${url}#nameddest=DestWithXYZ`);
+    chrome.test.assertEq(10, params.page);
+    chrome.test.assertEq(1.7, params.zoom);
+    chrome.test.assertEq(111, params.position.x);
+    chrome.test.assertEq(222, params.position.y);
+    chrome.test.assertEq(undefined, params.viewPosition);
 
     // Checking #nameddest=name with a nameddest that specifies the view fit
     // type is "XYZ" with a zoom parameter of 0.
-    paramsParser.getViewportFromUrlParams(
-        `${url}#nameddest=DestWithXYZAtZoom0`, function(params) {
-          chrome.test.assertEq(10, params.page);
-          chrome.test.assertEq(undefined, params.zoom);
-          chrome.test.assertEq(111, params.position.x);
-          chrome.test.assertEq(222, params.position.y);
-          chrome.test.assertEq(undefined, params.viewPosition);
-        });
+    params = await paramsParser.getViewportFromUrlParams(
+        `${url}#nameddest=DestWithXYZAtZoom0`);
+    chrome.test.assertEq(10, params.page);
+    chrome.test.assertEq(undefined, params.zoom);
+    chrome.test.assertEq(111, params.position.x);
+    chrome.test.assertEq(222, params.position.y);
+    chrome.test.assertEq(undefined, params.viewPosition);
 
     // Checking #nameddest=name with a nameddest that specifies the view fit
     // type is "XYZ" and one of its parameters is null.
-    paramsParser.getViewportFromUrlParams(
-        `${url}#nameddest=DestWithXYZWithNullParameter`, function(params) {
-          chrome.test.assertEq(13, params.page);
-          chrome.test.assertEq(undefined, params.zoom);
-          chrome.test.assertEq(undefined, params.position);
-          chrome.test.assertEq(undefined, params.viewPosition);
-        });
+    params = await paramsParser.getViewportFromUrlParams(
+        `${url}#nameddest=DestWithXYZWithNullParameter`);
+    chrome.test.assertEq(13, params.page);
+    chrome.test.assertEq(undefined, params.zoom);
+    chrome.test.assertEq(undefined, params.position);
+    chrome.test.assertEq(undefined, params.viewPosition);
 
     // Checking #nameddest=name with a nameddest that specifies the view fit
     // type is "FitR" with multiple valid parameters.
-    paramsParser.getViewportFromUrlParams(
-        `${url}#nameddest=DestWithFitR`, function(params) {
-          chrome.test.assertEq(0, params.page);
-          chrome.test.assertEq(2.5, params.zoom);
-          chrome.test.assertEq(20, params.position.x);
-          chrome.test.assertEq(100, params.position.y);
-          chrome.test.assertEq(undefined, params.viewPosition);
-        });
+    params = await paramsParser.getViewportFromUrlParams(
+        `${url}#nameddest=DestWithFitR`);
+    chrome.test.assertEq(0, params.page);
+    chrome.test.assertEq(2.5, params.zoom);
+    chrome.test.assertEq(20, params.position.x);
+    chrome.test.assertEq(100, params.position.y);
+    chrome.test.assertEq(undefined, params.viewPosition);
 
     // Checking #nameddest=name with a nameddest that specifies the view fit
     // type is "FitR" with multiple valid parameters.
-    paramsParser.getViewportFromUrlParams(
-        `${url}#nameddest=DestWithFitRReversedCoordinates`, function(params) {
-          chrome.test.assertEq(0, params.page);
-          chrome.test.assertEq(2.5, params.zoom);
-          chrome.test.assertEq(20, params.position.x);
-          chrome.test.assertEq(100, params.position.y);
-          chrome.test.assertEq(undefined, params.viewPosition);
-        });
+    params = await paramsParser.getViewportFromUrlParams(
+        `${url}#nameddest=DestWithFitRReversedCoordinates`);
+    chrome.test.assertEq(0, params.page);
+    chrome.test.assertEq(2.5, params.zoom);
+    chrome.test.assertEq(20, params.position.x);
+    chrome.test.assertEq(100, params.position.y);
+    chrome.test.assertEq(undefined, params.viewPosition);
 
     // Checking #nameddest=name with a nameddest that specifies the view fit
     // type is "FitR" with one NULL parameters.
-    paramsParser.getViewportFromUrlParams(
-        `${url}#nameddest=DestWithFitRWithNull`, function(params) {
-          chrome.test.assertEq(0, params.page);
-          chrome.test.assertEq(undefined, params.zoom);
-          chrome.test.assertEq(undefined, params.position);
-          chrome.test.assertEq(undefined, params.viewPosition);
-        });
+    params = await paramsParser.getViewportFromUrlParams(
+        `${url}#nameddest=DestWithFitRWithNull`);
+    chrome.test.assertEq(0, params.page);
+    chrome.test.assertEq(undefined, params.zoom);
+    chrome.test.assertEq(undefined, params.position);
+    chrome.test.assertEq(undefined, params.viewPosition);
 
     // Checking #view=Fit.
-    paramsParser.getViewportFromUrlParams(`${url}#view=Fit`, function(params) {
-      chrome.test.assertEq(FittingType.FIT_TO_PAGE, params.view);
-      chrome.test.assertEq(undefined, params.viewPosition);
-    });
-    // Checking #view=FitH.
-    paramsParser.getViewportFromUrlParams(`${url}#view=FitH`, function(params) {
-      chrome.test.assertEq(FittingType.FIT_TO_WIDTH, params.view);
-      chrome.test.assertEq(undefined, params.viewPosition);
-    });
-    // Checking #view=FitH,[int position].
-    paramsParser.getViewportFromUrlParams(
-        `${url}#view=FitH,789`, function(params) {
-          chrome.test.assertEq(FittingType.FIT_TO_WIDTH, params.view);
-          chrome.test.assertEq(789, params.viewPosition);
-        });
-    // Checking #view=FitH,[float position].
-    paramsParser.getViewportFromUrlParams(
-        `${url}#view=FitH,7.89`, function(params) {
-          chrome.test.assertEq(FittingType.FIT_TO_WIDTH, params.view);
-          chrome.test.assertEq(7.89, params.viewPosition);
-        });
-    // Checking #view=FitV.
-    paramsParser.getViewportFromUrlParams(`${url}#view=FitV`, function(params) {
-      chrome.test.assertEq(FittingType.FIT_TO_HEIGHT, params.view);
-      chrome.test.assertEq(undefined, params.viewPosition);
-    });
-    // Checking #view=FitV,[int position].
-    paramsParser.getViewportFromUrlParams(
-        `${url}#view=FitV,123`, function(params) {
-          chrome.test.assertEq(FittingType.FIT_TO_HEIGHT, params.view);
-          chrome.test.assertEq(123, params.viewPosition);
-        });
-    // Checking #view=FitV,[float position].
-    paramsParser.getViewportFromUrlParams(
-        `${url}#view=FitV,1.23`, function(params) {
-          chrome.test.assertEq(FittingType.FIT_TO_HEIGHT, params.view);
-          chrome.test.assertEq(1.23, params.viewPosition);
-        });
-    // Checking #view=[wrong parameter].
-    paramsParser.getViewportFromUrlParams(`${url}#view=FitW`, function(params) {
-      chrome.test.assertEq(undefined, params.view);
-      chrome.test.assertEq(undefined, params.viewPosition);
-    });
-    // Checking #view=[wrong parameter],[position].
-    paramsParser.getViewportFromUrlParams(
-        `${url}#view=FitW,555`, function(params) {
-          chrome.test.assertEq(undefined, params.view);
-          chrome.test.assertEq(undefined, params.viewPosition);
-        });
-    // Checking #view=[wrong parameter].
-    paramsParser.getViewportFromUrlParams(`${url}#view=XYZ`, function(params) {
-      chrome.test.assertEq(undefined, params.view);
-      chrome.test.assertEq(undefined, params.viewPosition);
-    });
-    // Checking #view=[wrong parameter],[position].
-    paramsParser.getViewportFromUrlParams(
-        `${url}#view=XYZ,111,222,1.7`, function(params) {
-          chrome.test.assertEq(undefined, params.zoom);
-          chrome.test.assertEq(undefined, params.position);
-          chrome.test.assertEq(undefined, params.view);
-          chrome.test.assertEq(undefined, params.viewPosition);
-        });
-    // Checking #view=[wrong parameter].
-    paramsParser.getViewportFromUrlParams(`${url}#view=FitR`, function(params) {
-      chrome.test.assertEq(undefined, params.view);
-      chrome.test.assertEq(undefined, params.viewPosition);
-    });
-    // Checking #view=[wrong parameter],[position].
-    paramsParser.getViewportFromUrlParams(
-        `${url}#view=FitR,20,100,120,300`, function(params) {
-          chrome.test.assertEq(undefined, params.zoom);
-          chrome.test.assertEq(undefined, params.position);
-          chrome.test.assertEq(undefined, params.view);
-          chrome.test.assertEq(undefined, params.viewPosition);
-        });
+    params = await paramsParser.getViewportFromUrlParams(`${url}#view=Fit`);
+    chrome.test.assertEq(FittingType.FIT_TO_PAGE, params.view);
+    chrome.test.assertEq(undefined, params.viewPosition);
 
+    // Checking #view=FitH.
+    params = await paramsParser.getViewportFromUrlParams(`${url}#view=FitH`);
+    chrome.test.assertEq(FittingType.FIT_TO_WIDTH, params.view);
+    chrome.test.assertEq(undefined, params.viewPosition);
+
+    // Checking #view=FitH,[int position].
+    params =
+        await paramsParser.getViewportFromUrlParams(`${url}#view=FitH,789`);
+    chrome.test.assertEq(FittingType.FIT_TO_WIDTH, params.view);
+    chrome.test.assertEq(789, params.viewPosition);
+
+    // Checking #view=FitH,[float position].
+    params =
+        await paramsParser.getViewportFromUrlParams(`${url}#view=FitH,7.89`);
+    chrome.test.assertEq(FittingType.FIT_TO_WIDTH, params.view);
+    chrome.test.assertEq(7.89, params.viewPosition);
+
+    // Checking #view=FitV.
+    params = await paramsParser.getViewportFromUrlParams(`${url}#view=FitV`);
+    chrome.test.assertEq(FittingType.FIT_TO_HEIGHT, params.view);
+    chrome.test.assertEq(undefined, params.viewPosition);
+
+    // Checking #view=FitV,[int position].
+    params =
+        await paramsParser.getViewportFromUrlParams(`${url}#view=FitV,123`);
+    chrome.test.assertEq(FittingType.FIT_TO_HEIGHT, params.view);
+    chrome.test.assertEq(123, params.viewPosition);
+
+    // Checking #view=FitV,[float position].
+    params =
+        await paramsParser.getViewportFromUrlParams(`${url}#view=FitV,1.23`);
+    chrome.test.assertEq(FittingType.FIT_TO_HEIGHT, params.view);
+    chrome.test.assertEq(1.23, params.viewPosition);
+
+    // Checking #view=[wrong parameter].
+    params = await paramsParser.getViewportFromUrlParams(`${url}#view=FitW`);
+    chrome.test.assertEq(undefined, params.view);
+    chrome.test.assertEq(undefined, params.viewPosition);
+
+    // Checking #view=[wrong parameter],[position].
+    params =
+        await paramsParser.getViewportFromUrlParams(`${url}#view=FitW,555`);
+    chrome.test.assertEq(undefined, params.view);
+    chrome.test.assertEq(undefined, params.viewPosition);
+
+    // Checking #view=[wrong parameter].
+    params = await paramsParser.getViewportFromUrlParams(`${url}#view=XYZ`);
+    chrome.test.assertEq(undefined, params.view);
+    chrome.test.assertEq(undefined, params.viewPosition);
+
+    // Checking #view=[wrong parameter],[position].
+    params = await paramsParser.getViewportFromUrlParams(
+        `${url}#view=XYZ,111,222,1.7`);
+    chrome.test.assertEq(undefined, params.zoom);
+    chrome.test.assertEq(undefined, params.position);
+    chrome.test.assertEq(undefined, params.view);
+    chrome.test.assertEq(undefined, params.viewPosition);
+
+    // Checking #view=[wrong parameter].
+    params = await paramsParser.getViewportFromUrlParams(`${url}#view=FitR`);
+    chrome.test.assertEq(undefined, params.view);
+    chrome.test.assertEq(undefined, params.viewPosition);
+
+    // Checking #view=[wrong parameter],[position].
+    params = await paramsParser.getViewportFromUrlParams(
+        `${url}#view=FitR,20,100,120,300`);
+    chrome.test.assertEq(undefined, params.zoom);
+    chrome.test.assertEq(undefined, params.position);
+    chrome.test.assertEq(undefined, params.view);
+    chrome.test.assertEq(undefined, params.viewPosition);
 
     // Checking #toolbar=0 to disable the toolbar.
     chrome.test.assertFalse(paramsParser.shouldShowToolbar(`${url}#toolbar=0`));
diff --git a/chrome/test/data/pdf/viewer_properties_dialog_test.js b/chrome/test/data/pdf/viewer_properties_dialog_test.js
index 4cd2adc..b175a8a 100644
--- a/chrome/test/data/pdf/viewer_properties_dialog_test.js
+++ b/chrome/test/data/pdf/viewer_properties_dialog_test.js
@@ -60,7 +60,7 @@
      ['modified', '-'],
      ['application', 'Your Preferred Text Editor'],
      ['pdf-producer', 'fixup_pdf_template.py'],
-     ['pdf-version', '-'],
+     ['pdf-version', '1.7'],
      ['page-count', '-'],
      ['page-size', '-'],
      ['fast-web-view', '-'],
diff --git a/chrome/test/data/web_apps/web_app_integration_browsertest_cases.csv b/chrome/test/data/web_apps/web_app_integration_browsertest_cases.csv
index 7893e73..162e049 100644
--- a/chrome/test/data/web_apps/web_app_integration_browsertest_cases.csv
+++ b/chrome/test/data/web_apps/web_app_integration_browsertest_cases.csv
@@ -2,6 +2,7 @@
 navigate_not_installable,assert_install_icon_not_shown,
 navigate_installable,assert_installable,install_omnibox_or_menu, navigate_browser_in_scope,assert_launch_icon_shown, assert_install_icon_not_shown,
 navigate_installable, install_create_shortcut_tabbed, set_open_in_window_internal, launch_internal, assert_window_created,
-navigate_installable_site_a, assert_install_icon_shown, install_omnibox_or_menu, assert_window_created, launch_internal_site_a, close_pwa, assert_no_crash,
-navigate_installable,install_omnibox_or_menu,launch_internal, uninstall_from_menu,navigate_browser_in_scope, assert_install_icon_shown,assert_launch_icon_not_shown,
+navigate_installable_site_a, assert_install_icon_shown, install_omnibox_or_menu, assert_window_created, launch_internal, close_pwa, assert_no_crash,
+add_policy_app_internal_tabbed, remove_policy_app, list_apps_internal, assert_app_not_in_list,
+Linux, Mac, Win | navigate_installable,install_omnibox_or_menu,launch_internal, uninstall_from_menu,navigate_browser_in_scope, assert_install_icon_shown,assert_launch_icon_not_shown,
 ChromeOS | navigate_installable,install_omnibox_or_menu,launch_internal, uninstall_internal,navigate_browser_in_scope, assert_install_icon_shown,assert_launch_icon_not_shown,
diff --git a/chrome/test/data/webui/chromeos/diagnostics/BUILD.gn b/chrome/test/data/webui/chromeos/diagnostics/BUILD.gn
index 5fe7cee..792a3c8 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/diagnostics/BUILD.gn
@@ -18,6 +18,7 @@
     ":diagnostics_app_test",
     ":diagnostics_app_unified_test",
     ":diagnostics_test_utils",
+    ":diagnostics_utils_test",
     ":fake_method_provider_test",
     ":fake_observables_test",
     ":fake_system_data_provider_test",
@@ -105,6 +106,13 @@
   ]
 }
 
+js_library("diagnostics_utils_test") {
+  deps = [
+    "../..:chai_assert",
+    "//chromeos/components/diagnostics_ui/resources:diagnostics_utils",
+  ]
+}
+
 js_library("fake_method_provider_test") {
   deps = [
     "../..:chai_assert",
@@ -141,6 +149,7 @@
     "../..:chai_assert",
     "../..:test_util.m",
     "//chromeos/components/diagnostics_ui/resources:diagnostics_types",
+    "//chromeos/components/diagnostics_ui/resources:diagnostics_utils",
     "//chromeos/components/diagnostics_ui/resources:fake_data",
     "//chromeos/components/diagnostics_ui/resources:fake_system_data_provider",
     "//chromeos/components/diagnostics_ui/resources:memory_card",
diff --git a/chrome/test/data/webui/chromeos/diagnostics/diagnostics_app_unified_test.js b/chrome/test/data/webui/chromeos/diagnostics/diagnostics_app_unified_test.js
index ba335ad46..be6c0b93 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/diagnostics_app_unified_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/diagnostics_app_unified_test.js
@@ -9,6 +9,7 @@
 import {cpuCardTestSuite} from './cpu_card_test.js';
 import {dataPointTestSuite} from './data_point_test.js';
 import {appTestSuite} from './diagnostics_app_test.js';
+import {diagnosticsUtilsTestSuite} from './diagnostics_utils_test.js';
 import {fakeMethodResolverTestSuite} from './fake_method_provider_test.js';
 import {fakeObservablesTestSuite} from './fake_observables_test.js';
 import {fakeSystemDataProviderTestSuite} from './fake_system_data_provider_test.js';
@@ -35,6 +36,7 @@
 runSuite('BatteryStatusCard', batteryStatusCardTestSuite);
 runSuite('CpuCard', cpuCardTestSuite);
 runSuite('DataPoint', dataPointTestSuite);
+runSuite('DiagnosticsUtils', diagnosticsUtilsTestSuite);
 runSuite('FakeMethodProvider', fakeMethodResolverTestSuite);
 runSuite('FakeMojoInterface', fakeMojoProviderTestSuite);
 runSuite('FakeObservables', fakeObservablesTestSuite);
diff --git a/chrome/test/data/webui/chromeos/diagnostics/diagnostics_browsertest.js b/chrome/test/data/webui/chromeos/diagnostics/diagnostics_browsertest.js
index 37b948e..082e5832 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/diagnostics_browsertest.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/diagnostics_browsertest.js
@@ -44,11 +44,11 @@
 // You must register all suites in unified test here as well for consistency,
 // although technically is not necessary.
 const debug_suites_list = [
-  'App', 'BatteryStatusCard', 'CpuCard', 'DataPoint', 'FakeMethodProvider',
-  'FakeMojoInterface', 'FakeObservables', 'FakeSystemDataProvider',
-  'FakeSystemRoutineContoller', 'MemoryCard', 'OverviewCard', 'PercentBarChart',
-  'RealtimeCpuChart', 'RoutineListExecutor', 'RoutineResultEntry',
-  'RoutineResultList', 'RoutineSection', 'TextBadge'
+  'App', 'BatteryStatusCard', 'CpuCard', 'DataPoint', 'DiagnosticsUtils',
+  'FakeMethodProvider', 'FakeMojoInterface', 'FakeObservables',
+  'FakeSystemDataProvider', 'FakeSystemRoutineContoller', 'MemoryCard',
+  'OverviewCard', 'PercentBarChart', 'RealtimeCpuChart', 'RoutineListExecutor',
+  'RoutineResultEntry', 'RoutineResultList', 'RoutineSection', 'TextBadge'
 ];
 
 TEST_F('DiagnosticsApp', 'BrowserTest', function() {
diff --git a/chrome/test/data/webui/chromeos/diagnostics/diagnostics_utils_test.js b/chrome/test/data/webui/chromeos/diagnostics/diagnostics_utils_test.js
new file mode 100644
index 0000000..1fe8f3d
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/diagnostics/diagnostics_utils_test.js
@@ -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.
+
+import {convertKibToGibDecimalString} from 'chrome://diagnostics/diagnostics_utils.js';
+import {assertEquals} from '../../chai_assert.js';
+
+export function diagnosticsUtilsTestSuite() {
+  test('ProperlyConvertsKibToGib', () => {
+    assertEquals('0', convertKibToGibDecimalString(0, 0));
+    assertEquals('0.00', convertKibToGibDecimalString(0, 2));
+    assertEquals('0.000000', convertKibToGibDecimalString(0, 6));
+    assertEquals('0', convertKibToGibDecimalString(1, 0));
+    assertEquals('5.72', convertKibToGibDecimalString(6000000, 2));
+    assertEquals('5.722046', convertKibToGibDecimalString(6000000, 6));
+    assertEquals('1.00', convertKibToGibDecimalString(2 ** 20, 2));
+    assertEquals('1.00', convertKibToGibDecimalString(2 ** 20 + 1, 2));
+    assertEquals('1.00', convertKibToGibDecimalString(2 ** 20 - 1, 2));
+    assertEquals('0.999999', convertKibToGibDecimalString(2 ** 20 - 1, 6));
+  });
+}
\ No newline at end of file
diff --git a/chrome/test/data/webui/chromeos/diagnostics/memory_card_test.js b/chrome/test/data/webui/chromeos/diagnostics/memory_card_test.js
index b055f92f..dea8153 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/memory_card_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/memory_card_test.js
@@ -5,9 +5,11 @@
 import 'chrome://diagnostics/memory_card.js';
 
 import {MemoryUsage} from 'chrome://diagnostics/diagnostics_types.js';
+import {convertKibToGibDecimalString} from 'chrome://diagnostics/diagnostics_utils.js';
 import {fakeMemoryUsage} from 'chrome://diagnostics/fake_data.js';
 import {FakeSystemDataProvider} from 'chrome://diagnostics/fake_system_data_provider.js';
 import {setSystemDataProviderForTesting} from 'chrome://diagnostics/mojo_interface_provider.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 import {flushTasks, isChildVisible} from '../../test_util.m.js';
@@ -91,8 +93,14 @@
       const barChart = dx_utils.getPercentBarChartElement(memoryElement);
       const memInUse = fakeMemoryUsage[0].totalMemoryKib -
           fakeMemoryUsage[0].availableMemoryKib;
+      const expectedChartHeader = loadTimeData.getStringF(
+          'memoryAvailable',
+          convertKibToGibDecimalString(
+              fakeMemoryUsage[0].availableMemoryKib, 2),
+          convertKibToGibDecimalString(fakeMemoryUsage[0].totalMemoryKib, 2));
       assertEquals(fakeMemoryUsage[0].totalMemoryKib, barChart.max);
       assertEquals(memInUse, barChart.value);
+      dx_utils.assertTextContains(barChart.header, expectedChartHeader);
 
       // Verify the routine section is in the card.
       assertTrue(!!getRoutineSection());
diff --git a/chrome/test/data/webui/chromeos/diagnostics/percent_bar_chart_test.js b/chrome/test/data/webui/chromeos/diagnostics/percent_bar_chart_test.js
index 9f2fb35..1fc2971 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/percent_bar_chart_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/percent_bar_chart_test.js
@@ -58,10 +58,6 @@
 
       assertEquals(
           header, percentBarChartElement.$$('#chartName').textContent.trim());
-      dx_utils.assertElementContainsText(
-          /** @type {!HTMLElement} */ (
-              percentBarChartElement.$$('#percentageLabel')),
-          `${percent}`);
 
       assertFalse(!!percentBarChartElement.$$('#headerIcon'));
     });
diff --git a/chrome/test/data/webui/engagement/site_engagement_browsertest.js b/chrome/test/data/webui/engagement/site_engagement_browsertest.js
index cee7d37..0b1fe30f 100644
--- a/chrome/test/data/webui/engagement/site_engagement_browsertest.js
+++ b/chrome/test/data/webui/engagement/site_engagement_browsertest.js
@@ -8,7 +8,7 @@
 var EXAMPLE_URL_1 = 'http://example.com/';
 var EXAMPLE_URL_2 = 'http://shmlexample.com/';
 
-GEN('#include "chrome/browser/engagement/site_engagement_service.h"');
+GEN('#include "components/site_engagement/content/site_engagement_service.h"');
 GEN('#include "chrome/browser/engagement/site_engagement_service_factory.h"');
 GEN('#include "chrome/browser/profiles/profile.h"');
 GEN('#include "chrome/browser/ui/browser.h"');
diff --git a/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.js b/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.js
index b939632..99708c5 100644
--- a/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.js
+++ b/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import 'chrome://new-tab-page/lazy_load.js';
-import {BrowserProxy, PromoBrowserCommandProxy} from 'chrome://new-tab-page/new_tab_page.js';
+import {$$, BrowserProxy, PromoBrowserCommandProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {createTestProxy} from 'chrome://test/new_tab_page/test_support.js';
 import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
 import {eventToPromise, flushTasks} from 'chrome://test/test_util.m.js';
@@ -19,7 +19,11 @@
         promoBrowserCommand.mojom.CommandHandlerRemote);
   });
 
-  async function createMiddleSlotPromo(canShowPromo = true) {
+  /**
+   * @param {boolean} canShowPromo
+   * @return {!Element}
+   */
+  async function createMiddleSlotPromo(canShowPromo) {
     const testProxy = BrowserProxy.getInstance();
     testProxy.handler.setResultFor('getPromo', Promise.resolve({
       promo: {
@@ -79,43 +83,57 @@
     return middleSlotPromo;
   }
 
-  [true, false].forEach(canShowPromo => {
-    test(`render ${canShowPromo}`, async () => {
-      const middleSlotPromo = await createMiddleSlotPromo(canShowPromo);
-      assertEquals(!canShowPromo, middleSlotPromo.hasAttribute('hidden'));
-      const parts = middleSlotPromo.$.container.children;
-      assertEquals(6, parts.length);
-      const [image, imageWithLink, imageWithCommand, text, link, command] =
-          parts;
+  /**
+   * @param {boolean} hasContent
+   * @param {!Element} middleSlotPromo
+   * @private
+   */
+  function assertHasContent(hasContent, middleSlotPromo) {
+    assertEquals(hasContent, !!$$(middleSlotPromo, '#container'));
+  }
 
-      assertEquals('https://image', image.autoSrc);
+  test(`render canShowPromo=true`, async () => {
+    const canShowPromo = true;
+    const middleSlotPromo = await createMiddleSlotPromo(canShowPromo);
+    assertHasContent(canShowPromo, middleSlotPromo);
+    const parts = $$(middleSlotPromo, '#container').children;
+    assertEquals(6, parts.length);
+    const [image, imageWithLink, imageWithCommand, text, link, command] = parts;
 
-      assertEquals('https://link/', imageWithLink.href);
-      assertEquals('https://image', imageWithLink.children[0].autoSrc);
+    assertEquals('https://image', image.autoSrc);
 
-      assertEquals('', imageWithCommand.href);
-      assertEquals('https://image', imageWithCommand.children[0].autoSrc);
+    assertEquals('https://link/', imageWithLink.href);
+    assertEquals('https://image', imageWithLink.children[0].autoSrc);
 
-      assertEquals('text', text.innerText);
-      assertEquals('red', text.style.color);
+    assertEquals('', imageWithCommand.href);
+    assertEquals('https://image', imageWithCommand.children[0].autoSrc);
 
-      assertEquals('https://link/', link.href);
-      assertEquals('link', link.innerText);
-      assertEquals('green', link.style.color);
+    assertEquals('text', text.innerText);
+    assertEquals('red', text.style.color);
 
-      assertEquals('', command.href);
-      assertEquals('command', command.text);
-      assertEquals('blue', command.style.color);
-    });
+    assertEquals('https://link/', link.href);
+    assertEquals('link', link.innerText);
+    assertEquals('green', link.style.color);
+
+    assertEquals('', command.href);
+    assertEquals('command', command.text);
+    assertEquals('blue', command.style.color);
+  });
+
+  test(`render canShowPromo=false`, async () => {
+    const canShowPromo = false;
+    const middleSlotPromo = await createMiddleSlotPromo(canShowPromo);
+    assertHasContent(canShowPromo, middleSlotPromo);
   });
 
   test('clicking on command', async () => {
-    const middleSlotPromo = await createMiddleSlotPromo();
-    assertFalse(middleSlotPromo.hasAttribute('hidden'));
+    const canShowPromo = true;
+    const middleSlotPromo = await createMiddleSlotPromo(canShowPromo);
+    assertHasContent(canShowPromo, middleSlotPromo);
     const testProxy = PromoBrowserCommandProxy.getInstance();
     testProxy.handler.setResultFor('executeCommand', Promise.resolve());
-    const imageWithCommand = middleSlotPromo.$.container.children[2];
-    const command = middleSlotPromo.$.container.children[5];
+    const imageWithCommand = $$(middleSlotPromo, '#container').children[2];
+    const command = $$(middleSlotPromo, '#container').children[5];
     await Promise.all([imageWithCommand, command].map(async el => {
       testProxy.handler.reset();
       el.click();
@@ -138,20 +156,24 @@
     }));
   });
 
-  test('promo remains hidden if there is no data', async () => {
-    const promoBrowserCommandTestProxy = PromoBrowserCommandProxy.getInstance();
-    const testProxy = BrowserProxy.getInstance();
-    testProxy.handler.setResultFor('getPromo', Promise.resolve({promo: null}));
-    const middleSlotPromo = document.createElement('ntp-middle-slot-promo');
-    document.body.appendChild(middleSlotPromo);
-    const loaded =
-        eventToPromise('ntp-middle-slot-promo-loaded', document.body);
-    assertEquals(
-        0,
-        promoBrowserCommandTestProxy.handler.getCallCount(
-            'canShowPromoWithCommand'));
-    assertEquals(0, testProxy.handler.getCallCount('onPromoRendered'));
-    assertTrue(middleSlotPromo.hasAttribute('hidden'));
-    await loaded;
+  [null,
+   {middleSlotParts: []},
+   {middleSlotParts: [{break: {}}]},
+  ].forEach((promo, i) => {
+    test(`promo remains hidden if there is no data ${i}`, async () => {
+      const promoBrowserCommandTestProxy =
+          PromoBrowserCommandProxy.getInstance();
+      const testProxy = BrowserProxy.getInstance();
+      testProxy.handler.setResultFor('getPromo', Promise.resolve({promo}));
+      const middleSlotPromo = document.createElement('ntp-middle-slot-promo');
+      document.body.appendChild(middleSlotPromo);
+      await flushTasks();
+      assertEquals(
+          0,
+          promoBrowserCommandTestProxy.handler.getCallCount(
+              'canShowPromoWithCommand'));
+      assertEquals(0, testProxy.handler.getCallCount('onPromoRendered'));
+      assertHasContent(false, middleSlotPromo);
+    });
   });
 });
diff --git a/chrome/test/data/webui/resources/list_property_update_behavior_tests.js b/chrome/test/data/webui/resources/list_property_update_behavior_tests.js
index 8b571cd..a11be43 100644
--- a/chrome/test/data/webui/resources/list_property_update_behavior_tests.js
+++ b/chrome/test/data/webui/resources/list_property_update_behavior_tests.js
@@ -4,7 +4,7 @@
 
 /** @fileoverview Suite of tests for the ListPropertyUpdateBehavior.  */
 
-// #import {ListPropertyUpdateBehavior} from 'chrome://resources/js/list_property_update_behavior.m.js';
+// #import {ListPropertyUpdateBehavior, updateListProperty} from 'chrome://resources/js/list_property_update_behavior.m.js';
 // #import {Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 suite('ListPropertyUpdateBehavior', function() {
@@ -76,7 +76,7 @@
       updateComplexArray(newArray) {
         if (this.updateList(
                 'complexArray', x => x.letter, newArray,
-                true /* uidBasedUpdate */)) {
+                true /* identityBasedUpdate */)) {
           return {topArrayChanged: true, wordsArrayChanged: false};
         }
 
@@ -305,4 +305,48 @@
     assertTrue(testElement.updateList('complexArray', x => x.letter, newArray));
     assertDeepEquals(['apricot'], testElement.complexArray[0].words);
   });
+
+  test('updateListProperty() function triggers notifySplices()', () => {
+    // Ensure that the array is updated when an element is removed from the
+    // end.
+    let elementRemoved = testElement.complexArray.slice(0, 2);
+    updateListProperty(
+        testElement, 'complexArray', obj => obj, elementRemoved, true);
+    assertComplexArrayEquals(testElement.complexArray, elementRemoved);
+
+    // Ensure that the array is updated when an element is removed from the
+    // beginning.
+    testElement.resetComplexArray();
+    elementRemoved = testElement.complexArray.slice(1);
+    updateListProperty(
+        testElement, 'complexArray', obj => obj, elementRemoved, true);
+    assertComplexArrayEquals(testElement.complexArray, elementRemoved);
+
+    // Ensure that the array is updated when an element is added to the end.
+    testElement.resetComplexArray();
+    let elementAdded = testElement.complexArray.slice();
+    elementAdded.push({letter: 'd', words: ['door', 'dice']});
+    updateListProperty(
+        testElement, 'complexArray', obj => obj, elementAdded, true);
+    assertComplexArrayEquals(testElement.complexArray, elementAdded);
+
+    // Ensure that the array is updated when an element is added to the
+    // beginning.
+    testElement.resetComplexArray();
+    elementAdded = [{letter: 'A', words: ['Alphabet']}];
+    elementAdded.push(...testElement.complexArray);
+    updateListProperty(
+        testElement, 'complexArray', obj => obj, elementAdded, true);
+    assertComplexArrayEquals(testElement.complexArray, elementAdded);
+
+    // Ensure that the array is updated when the entire array is different.
+    testElement.resetComplexArray();
+    const newArray = [
+      {letter: 'w', words: ['water', 'woods']},
+      {letter: 'x', words: ['xylophone']}, {letter: 'y', words: ['yo-yo']},
+      {letter: 'z', words: ['zebra', 'zephyr']}
+    ];
+    updateListProperty(testElement, 'complexArray', obj => obj, newArray, true);
+    assertComplexArrayEquals(testElement.complexArray, newArray);
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js b/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
index ee2bcb0..a11485d 100644
--- a/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
@@ -492,6 +492,43 @@
       page.$.help.click();
       return aboutBrowserProxy.whenCalled('openOsHelpPage');
     });
+
+    test('LaunchDiagnostics', async function() {
+      loadTimeData.overrideValues({
+        isDeepLinkingEnabled: true,
+        diagnosticsAppEnabled: true,
+      });
+
+      await initNewPage();
+      Polymer.dom.flush();
+
+      assertTrue(!!page.$.diagnostics);
+      page.$.diagnostics.click();
+      await aboutBrowserProxy.whenCalled('openDiagnostics');
+    });
+
+    test('Deep link to diagnostics', async () => {
+      loadTimeData.overrideValues({
+        isDeepLinkingEnabled: true,
+        diagnosticsAppEnabled: true,
+      });
+
+      await initNewPage();
+      Polymer.dom.flush();
+
+      const params = new URLSearchParams;
+      params.append('settingId', '1707');  // Setting::kDiagnostics
+      settings.Router.getInstance().navigateTo(
+          settings.routes.ABOUT_ABOUT, params);
+
+      Polymer.dom.flush();
+
+      const deepLinkElement = page.$$('#diagnostics').$$('cr-icon-button');
+      await test_util.waitAfterNextRender(deepLinkElement);
+      assertEquals(
+          deepLinkElement, getDeepActiveElement(),
+          'Diagnostics should be focused for settingId=1707.');
+    });
   });
 
   suite('DetailedBuildInfoTest', function() {
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index d7cfe5d..fff7095 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -845,8 +845,7 @@
   }
 };
 
-// TODO(https://crbug.com/1121139): Re-enable flaky test.
-TEST_F('OSSettingsFingerprintListTest', 'DISABLED_AllJsTests', () => {
+TEST_F('OSSettingsFingerprintListTest', 'AllJsTests', () => {
   mocha.run();
 });
 
diff --git a/chrome/test/data/webui/settings/chromeos/test_about_page_browser_proxy_chromeos.js b/chrome/test/data/webui/settings/chromeos/test_about_page_browser_proxy_chromeos.js
index 23481067..3cac13f 100644
--- a/chrome/test/data/webui/settings/chromeos/test_about_page_browser_proxy_chromeos.js
+++ b/chrome/test/data/webui/settings/chromeos/test_about_page_browser_proxy_chromeos.js
@@ -23,6 +23,7 @@
       'getEndOfLifeInfo',
       'launchReleaseNotes',
       'openOsHelpPage',
+      'openDiagnostics',
       'refreshTPMFirmwareUpdateStatus',
       'setChannel',
     ]);
@@ -191,6 +192,11 @@
   }
 
   /** @override */
+  openDiagnostics() {
+    this.methodCalled('openDiagnostics');
+  }
+
+  /** @override */
   launchReleaseNotes() {
     this.methodCalled('launchReleaseNotes');
   }
diff --git a/chrome/test/data/webui/settings/test_about_page_browser_proxy.js b/chrome/test/data/webui/settings/test_about_page_browser_proxy.js
index d5c6bc9..98bf474 100644
--- a/chrome/test/data/webui/settings/test_about_page_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_about_page_browser_proxy.js
@@ -71,6 +71,9 @@
   openOsHelpPage() {}
 
   /** @override */
+  openDiagnostics() {}
+
+  /** @override */
   requestUpdate() {}
 
   /** @override */
diff --git a/chrome/test/data/webui/signin/profile_type_choice_test.js b/chrome/test/data/webui/signin/profile_type_choice_test.js
index b07f1a7..4ac3b62 100644
--- a/chrome/test/data/webui/signin/profile_type_choice_test.js
+++ b/chrome/test/data/webui/signin/profile_type_choice_test.js
@@ -4,14 +4,23 @@
 
 import 'chrome://profile-picker/lazy_load.js';
 
+import {ManageProfilesBrowserProxyImpl} from 'chrome://profile-picker/profile_picker.js';
+
 import {assertTrue} from '../chai_assert.js';
 import {isChildVisible} from '../test_util.m.js';
 
+import {TestManageProfilesBrowserProxy} from './test_manage_profiles_browser_proxy.js';
+
 suite('ProfileTypeChoiceTest', function() {
   /** @type {!ProfileTypeChoiceElement} */
   let choice;
 
+  /** @type {!TestManageProfilesBrowserProxy} */
+  let browserProxy;
+
   setup(function() {
+    browserProxy = new TestManageProfilesBrowserProxy();
+    ManageProfilesBrowserProxyImpl.instance_ = browserProxy;
     document.body.innerHTML = '';
     choice = /** @type {!ProfileTypeChoiceElement} */ (
         document.createElement('profile-type-choice'));
@@ -29,4 +38,8 @@
   test('NotNowButton', function() {
     assertTrue(isChildVisible(choice, '#notNowButton'));
   });
+
+  test('VerifySignInPromoImpressionRecorded', function() {
+    return browserProxy.whenCalled('recordSignInPromoImpression');
+  });
 });
diff --git a/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.js b/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.js
index 2296e8da..95e4e6f6 100644
--- a/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.js
+++ b/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.js
@@ -22,6 +22,7 @@
       'loadSignInProfileCreationFlow',
       'createProfile',
       'setProfileName',
+      'recordSignInPromoImpression',
     ]);
 
     /** @type {!AutogeneratedThemeColorInfo} */
@@ -116,4 +117,9 @@
   setProfileName(profilePath, profileName) {
     this.methodCalled('setProfileName', [profilePath, profileName]);
   }
+
+  /** @override */
+  recordSignInPromoImpression() {
+    this.methodCalled('recordSignInPromoImpression');
+  }
 }
diff --git a/chrome/test/data/webui/tab_search/infinite_list_test.js b/chrome/test/data/webui/tab_search/infinite_list_test.js
index f924aed..e704262 100644
--- a/chrome/test/data/webui/tab_search/infinite_list_test.js
+++ b/chrome/test/data/webui/tab_search/infinite_list_test.js
@@ -87,6 +87,7 @@
   test('ScrollHeight', async () => {
     const tabItems = sampleTabItems(sampleSiteNames());
     await setupTest(tabItems);
+    await waitAfterNextRender(infiniteList);
 
     assertEquals(0, infiniteList.scrollTop);
 
diff --git a/chrome/updater/app/app_install.cc b/chrome/updater/app/app_install.cc
index 4938e63..f777e564 100644
--- a/chrome/updater/app/app_install.cc
+++ b/chrome/updater/app/app_install.cc
@@ -143,14 +143,18 @@
 }
 
 void AppInstall::RegisterUpdater() {
-  // TODO(crbug.com/1128060): We should update the updater's registration with
-  // the new version, brand code, etc. For now, fake it.
-  RegistrationResponse result;
-  result.status_code = 0;
-  RegisterUpdaterDone(result);
+  RegistrationRequest registration_request;
+  registration_request.app_id = kUpdaterAppId;
+  registration_request.version = base::Version(UPDATER_VERSION_STRING);
+
+  scoped_refptr<UpdateService> update_service = CreateUpdateService();
+  update_service->RegisterApp(
+      registration_request,
+      base::BindOnce(&AppInstall::RegisterUpdaterDone, this, update_service));
 }
 
-void AppInstall::RegisterUpdaterDone(const RegistrationResponse& response) {
+void AppInstall::RegisterUpdaterDone(scoped_refptr<UpdateService>,
+                                     const RegistrationResponse& response) {
   VLOG(1) << "Updater registration complete, code = " << response.status_code;
   MaybeInstallApp();
 }
diff --git a/chrome/updater/app/app_install.h b/chrome/updater/app/app_install.h
index b10fe7f2..9fbe1129 100644
--- a/chrome/updater/app/app_install.h
+++ b/chrome/updater/app/app_install.h
@@ -68,7 +68,8 @@
 
   void RegisterUpdater();
 
-  void RegisterUpdaterDone(const RegistrationResponse& response);
+  void RegisterUpdaterDone(scoped_refptr<UpdateService>,
+                           const RegistrationResponse& response);
 
   // Handles the --tag and --app-id command line arguments, and triggers
   // installing of the corresponding application if either argument is present.
diff --git a/chrome/updater/win/update_service_proxy.cc b/chrome/updater/win/update_service_proxy.cc
index daaf9042..efaad0f7 100644
--- a/chrome/updater/win/update_service_proxy.cc
+++ b/chrome/updater/win/update_service_proxy.cc
@@ -25,6 +25,7 @@
 #include "base/version.h"
 #include "base/win/scoped_bstr.h"
 #include "chrome/updater/app/server/win/updater_idl.h"
+#include "chrome/updater/registration_data.h"
 #include "chrome/updater/util.h"
 
 namespace updater {
@@ -302,7 +303,9 @@
     const RegistrationRequest& request,
     base::OnceCallback<void(const RegistrationResponse&)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  NOTREACHED();
+  // TODO(crbug.com/1163524): Implement registration API.
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), RegistrationResponse(0)));
 }
 
 void UpdateServiceProxy::UpdateAll(StateChangeCallback state_update,
diff --git a/chromecast/browser/webview/client/webview.cc b/chromecast/browser/webview/client/webview.cc
index 3141d3b..1ee134bb 100644
--- a/chromecast/browser/webview/client/webview.cc
+++ b/chromecast/browser/webview/client/webview.cc
@@ -39,6 +39,7 @@
 constexpr char kPositionCommand[] = "position";
 constexpr char kKeyCommand[] = "key";
 constexpr char kFillCommand[] = "fill";
+constexpr char kSetInsetsCommand[] = "set_insets";
 
 void FrameCallback(void* data, wl_callback* callback, uint32_t time) {
   WebviewClient* webview_client = static_cast<WebviewClient*>(data);
@@ -356,6 +357,8 @@
     SendKeyRequest(tokens);
   else if (tokens[1] == kFillCommand)
     HandleFillSurfaceColor(tokens);
+  else if (tokens[1] == kSetInsetsCommand)
+    HandleSetInsets(tokens);
 
   std::cout << "Enter command: ";
   std::cout.flush();
@@ -499,6 +502,40 @@
   surfaces_[id]->buffer->sk_surface->getCanvas()->clear(color);
 }
 
+void WebviewClient::HandleSetInsets(const std::vector<std::string>& tokens) {
+  int id, top, left, bottom, right;
+  if (tokens.size() != 6 || !base::StringToInt(tokens[0], &id) ||
+      !base::StringToInt(tokens[2], &top) ||
+      !base::StringToInt(tokens[3], &left) ||
+      !base::StringToInt(tokens[4], &bottom) ||
+      !base::StringToInt(tokens[5], &right)) {
+    LOG(ERROR) << "Usage: [ID] " << kSetInsetsCommand
+               << " [top] [left] [bottom] [right]";
+    return;
+  }
+
+  if (surfaces_.find(id) == surfaces_.end()) {
+    LOG(ERROR) << "Failed to find surface " << id;
+    return;
+  }
+
+  Webview* webview = Webview::FromSurface(surfaces_[id].get());
+  if (!webview) {
+    LOG(ERROR) << "Failed to find webview " << id;
+    return;
+  }
+
+  WebviewRequest request;
+  request.mutable_set_insets()->set_top(top);
+  request.mutable_set_insets()->set_left(left);
+  request.mutable_set_insets()->set_bottom(bottom);
+  request.mutable_set_insets()->set_right(right);
+  if (!webview->client->Write(request)) {
+    LOG(ERROR) << "SetInsets failed";
+    return;
+  }
+}
+
 void WebviewClient::SendResizeRequest(Webview* webview, int width, int height) {
   WebviewRequest resize_request;
   resize_request.mutable_resize()->set_width(width);
diff --git a/chromecast/browser/webview/client/webview.h b/chromecast/browser/webview/client/webview.h
index e2f1733f..229e723 100644
--- a/chromecast/browser/webview/client/webview.h
+++ b/chromecast/browser/webview/client/webview.h
@@ -99,6 +99,7 @@
   void HandleResizeRequest(const std::vector<std::string>& tokens);
   void HandleFillSurfaceColor(const std::vector<std::string>& tokens);
   void SendKeyRequest(const std::vector<std::string>& tokens);
+  void HandleSetInsets(const std::vector<std::string>& tokens);
 
   void SendTouchInput(const Webview* webview,
                       int x,
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 1d93b180..27ec272 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -662,9 +662,6 @@
       <message name="IDS_DIAGNOSTICS_SESSION_LOG_LABEL" desc="The label for the button that opens the session log for the diagnostics app.">
         Save Session log
       </message>
-      <message name="IDS_DIAGNOSTICS_USED_MEMORY_LABEL" desc="The label for the progress bar that displays the amount of used memory on a users' device." translateable="false">
-        Used memory
-      </message>
       <message name="IDS_DIAGNOSTICS_BATTERY_CHIP_TEXT" desc="The text for a battery's full charge capacity represented in milliamp hours.">
         <ph name="CHARGE_VALUE">$1<ex>6000</ex></ph>mAh Battery
       </message>
@@ -782,8 +779,8 @@
       <message name="IDS_DIAGNOSTICS_CPU_SPEED_TEXT" desc="The text that displays the CPU speed. GHz is an SI unit for gigahertz.">
         <ph name="CURRENT">$1<ex>1.6</ex></ph>GHz / <ph name="MAX">$2<ex>1.9</ex></ph>GHz
       </message>
-      <message name="IDS_DIAGNOSTICS_TOTAL_MEMORY_LABEL" desc="The label for the total amount of memory on a device." translateable="false">
-        Total Memory
+      <message name="IDS_DIAGNOSTICS_MEMORY_AVAILABLE_TEXT" desc="The text that displays the amount of memory available on a users' device.">
+        <ph name="AVAILABLE_MEMORY">$1<ex>13.12</ex></ph> GB of <ph name="TOTAL_MEMORY">$2<ex>16</ex></ph> GB available.
       </message>
 
       <!-- Quick Answers -->
@@ -1022,6 +1019,9 @@
       <message name="IDS_NETWORK_DIAGNOSTICS_CAPTIVE_PORTAL" desc="Label for Network diagnostics `Captive Portal` test. This test ensures that the device's internet connection is not gated by a captive portal">
         Captive Portal
       </message>
+      <message name="IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING" desc="Label for Network diagnostics `Video Conferencing` test. This test ensures that the device can communicate with Google's video conferencing servers">
+        Video Conferencing
+      </message>
       <message name="IDS_NETWORK_DIAGNOSTICS_PASSED" desc="Label shown when a Network diagnostics routine passed">
         Passed
       </message>
@@ -1139,6 +1139,15 @@
       <message name="IDS_NETWORK_DIAGNOSTICS_CAPTIVE_PORTAL_PROBLEM_NO_INTERNET" desc="Error message shown when no network portal is detected, but there is no internet connection">
         No internet
       </message>
+      <message name="IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_UPD_FAILURE" desc="Error message shown when there are failed UDP requests to the video conferencing servers">
+        UDP request failures
+      </message>
+      <message name="IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_TCP_FAILURE" desc="Error message shown when there are failed TCP requests to the video conferencing servers">
+        TCP request failures
+      </message>
+      <message name="IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_MEDIA_FAILURE" desc="Error message shown when a connection cannot be established to the video conferencing media servers">
+        Unable to connect to media servers
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chromeos/chromeos_strings_grd/IDS_DIAGNOSTICS_MEMORY_AVAILABLE_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_DIAGNOSTICS_MEMORY_AVAILABLE_TEXT.png.sha1
new file mode 100644
index 0000000..17842aa1
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_DIAGNOSTICS_MEMORY_AVAILABLE_TEXT.png.sha1
@@ -0,0 +1 @@
+d9ac09bb04b99206d904d1aa223aff0cdda9e55e
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING.png.sha1 b/chromeos/chromeos_strings_grd/IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING.png.sha1
new file mode 100644
index 0000000..2ee1633
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING.png.sha1
@@ -0,0 +1 @@
+0fb960b6498b3a45e33a8d7940e5e99b41fbfdb5
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_MEDIA_FAILURE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_MEDIA_FAILURE.png.sha1
new file mode 100644
index 0000000..f8bcca3
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_MEDIA_FAILURE.png.sha1
@@ -0,0 +1 @@
+82498a3f69a603a3414828a24ba45cb542aa7f36
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_TCP_FAILURE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_TCP_FAILURE.png.sha1
new file mode 100644
index 0000000..1ea61c5
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_TCP_FAILURE.png.sha1
@@ -0,0 +1 @@
+180bb8dce3224095afc380d498e84a7c1585b5da
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_UPD_FAILURE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_UPD_FAILURE.png.sha1
new file mode 100644
index 0000000..59d335a
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_UPD_FAILURE.png.sha1
@@ -0,0 +1 @@
+d3d4d620e9bf27e0ff76251d812bd293efbe4ab4
\ No newline at end of file
diff --git a/chromeos/components/camera_app_ui/camera_app_ui.cc b/chromeos/components/camera_app_ui/camera_app_ui.cc
index 3de983eb..10efcde 100644
--- a/chromeos/components/camera_app_ui/camera_app_ui.cc
+++ b/chromeos/components/camera_app_ui/camera_app_ui.cc
@@ -223,6 +223,8 @@
 
   delegate_->SetLaunchDirectory();
 
+  window()->SetProperty(ash::kMinimizeOnBackKey, false);
+
   // Set up the data source.
   content::WebUIDataSource::Add(browser_context,
                                 CreateCameraAppUIHTMLSource(delegate_.get()));
diff --git a/chromeos/components/camera_app_ui/resources/js/main.js b/chromeos/components/camera_app_ui/resources/js/main.js
index f371da3..97b6f89 100644
--- a/chromeos/components/camera_app_ui/resources/js/main.js
+++ b/chromeos/components/camera_app_ui/resources/js/main.js
@@ -363,6 +363,8 @@
     bgOps = createFakeBackgroundOps();
   }
 
+  state.set(state.State.INTENT, bgOps.getIntent() !== null);
+
   browserProxy.setupUnloadListener(() => {
     // For SWA, we don't cancel the unhandled intent here since there is no
     // guarantee that asynchronous calls in unload listener can be executed
diff --git a/chromeos/components/camera_app_ui/resources/js/nav.js b/chromeos/components/camera_app_ui/resources/js/nav.js
index 0de44a48..858a8d1 100644
--- a/chromeos/components/camera_app_ui/resources/js/nav.js
+++ b/chromeos/components/camera_app_ui/resources/js/nav.js
@@ -179,7 +179,16 @@
   const key = util.getShortcutIdentifier(event);
   switch (key) {
     case 'BrowserBack':
-      windowController.minimize();
+      // Only works for non-intent instance.
+      if (!state.get(state.State.INTENT)) {
+        windowController.minimize();
+      }
+      break;
+    case 'Alt--':
+      // Prevent intent window from minimizing.
+      if (state.get(state.State.INTENT)) {
+        event.preventDefault();
+      }
       break;
     case 'Ctrl-=':
     case 'Ctrl--':
diff --git a/chromeos/components/camera_app_ui/resources/js/state.js b/chromeos/components/camera_app_ui/resources/js/state.js
index ed8792f..18dd48d0 100644
--- a/chromeos/components/camera_app_ui/resources/js/state.js
+++ b/chromeos/components/camera_app_ui/resources/js/state.js
@@ -26,6 +26,7 @@
   HAS_BACK_CAMERA: 'has-back-camera',
   HAS_EXTERNAL_SCREEN: 'has-external-screen',
   HAS_FRONT_CAMERA: 'has-front-camera',
+  INTENT: 'intent',
   MAX_WND: 'max-wnd',
   MIC: 'mic',
   MIRROR: 'mirror',
diff --git a/chromeos/components/diagnostics_ui/diagnostics_ui.cc b/chromeos/components/diagnostics_ui/diagnostics_ui.cc
index 0f7e3ffb..164dfe5 100644
--- a/chromeos/components/diagnostics_ui/diagnostics_ui.cc
+++ b/chromeos/components/diagnostics_ui/diagnostics_ui.cc
@@ -70,6 +70,7 @@
       {"hideReportText", IDS_DIAGNOSTICS_HIDE_REPORT_TEXT},
       {"learnMore", IDS_DIANOSTICS_LEARN_MORE_LABEL},
       {"learnMoreShort", IDS_DIAGNOSTICS_LEARN_MORE_LABEL_SHORT},
+      {"memoryAvailable", IDS_DIAGNOSTICS_MEMORY_AVAILABLE_TEXT},
       {"memoryRoutineText", IDS_DIAGNOSTICS_MEMORY_ROUTINE_TEXT},
       {"memoryTitle", IDS_DIAGNOSTICS_MEMORY_TITLE},
       {"percentageLabel", IDS_DIAGNOSTICS_PERCENTAGE_LABEL},
@@ -92,8 +93,6 @@
       {"testRunningBadgeText", IDS_DIAGNOSTICS_TEST_RUNNING_BADGE_TEXT},
       {"testSuccess", IDS_DIAGNOSTICS_TEST_SUCCESS_TEXT},
       {"testSucceededBadgeText", IDS_DIAGNOSTICS_TEST_SUCCESS_BADGE_TEXT},
-      {"totalMemory", IDS_DIAGNOSTICS_TOTAL_MEMORY_LABEL},
-      {"usedMemory", IDS_DIAGNOSTICS_USED_MEMORY_LABEL},
   };
   for (const auto& str : kLocalizedStrings) {
     html_source->AddLocalizedString(str.name, str.id);
diff --git a/chromeos/components/diagnostics_ui/resources/BUILD.gn b/chromeos/components/diagnostics_ui/resources/BUILD.gn
index 7d3626f..357c7e6 100644
--- a/chromeos/components/diagnostics_ui/resources/BUILD.gn
+++ b/chromeos/components/diagnostics_ui/resources/BUILD.gn
@@ -16,6 +16,7 @@
     ":diagnostics_app",
     ":diagnostics_card",
     ":diagnostics_types",
+    ":diagnostics_utils",
     ":fake_data",
     ":fake_method_resolver",
     ":fake_observables",
@@ -89,6 +90,9 @@
 js_library("diagnostics_types") {
 }
 
+js_library("diagnostics_utils") {
+}
+
 js_library("fake_data") {
   deps = [ ":diagnostics_types" ]
 }
@@ -112,9 +116,11 @@
 js_library("memory_card") {
   deps = [
     ":data_point",
+    ":diagnostics_utils",
     ":mojo_interface_provider",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/js:i18n_behavior.m",
+    "//ui/webui/resources/js:load_time_data.m",
   ]
 }
 
diff --git a/chromeos/components/diagnostics_ui/resources/battery_status_card.html b/chromeos/components/diagnostics_ui/resources/battery_status_card.html
index a5e5ced..8a95de24 100644
--- a/chromeos/components/diagnostics_ui/resources/battery_status_card.html
+++ b/chromeos/components/diagnostics_ui/resources/battery_status_card.html
@@ -2,12 +2,12 @@
 
 <diagnostics-card>
   <div id="cardTitle" slot="title">[[i18n('batteryTitle')]]</div>
-  <div id="batteryStatusChipInfo" slot="chip">
+  <div id="batteryStatusChipInfo" slot="chip" class="diagnostics-chip">
     [[getDesignedFullCharge_(batteryHealth_.chargeFullDesignMilliampHours)]]
   </div>
   <!-- TODO(michaelcheco): Replace placeholder icon. -->
   <iron-icon slot="icon" icon="cr:add"></iron-icon>
-  <percent-bar-chart slot="left-panel" header="[[i18n('remainingCharge')]]"
+  <percent-bar-chart slot="left-panel" header="[[powerTimeString_]]"
     value="[[batteryChargeStatus_.chargeNowMilliampHours]]"
     max="[[batteryHealth_.chargeFullNowMilliampHours]]">
   </percent-bar-chart>
diff --git a/chromeos/components/diagnostics_ui/resources/cpu_card.html b/chromeos/components/diagnostics_ui/resources/cpu_card.html
index 7ef34b24..90045aa 100644
--- a/chromeos/components/diagnostics_ui/resources/cpu_card.html
+++ b/chromeos/components/diagnostics_ui/resources/cpu_card.html
@@ -2,7 +2,9 @@
 
 <diagnostics-card>
   <div id="cardTitle" slot="title">[[i18n('cpuTitle')]]</div>
-  <div id="cpuChipInfo" slot="chip">[[cpuChipInfo_]]</div>
+  <div id="cpuChipInfo" slot="chip" class="diagnostics-chip">
+    [[cpuChipInfo_]]
+  </div>
   <!-- TODO(michaelcheco): Replace placeholder icon. -->
   <iron-icon slot="icon" icon="cr:add"></iron-icon>
   <!-- TODO(michaelcheco): Add i18n string for percent number format -->
diff --git a/chromeos/components/diagnostics_ui/resources/diagnostics_app_resources.grd b/chromeos/components/diagnostics_ui/resources/diagnostics_app_resources.grd
index 6399743..1d3bc0b 100644
--- a/chromeos/components/diagnostics_ui/resources/diagnostics_app_resources.grd
+++ b/chromeos/components/diagnostics_ui/resources/diagnostics_app_resources.grd
@@ -41,6 +41,7 @@
       <include name="IDR_DIAGNOSTICS_SYSTEM_ROUTINE_CONTROLLER_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/mojom/system_routine_controller.mojom-lite.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_DIAGNOSTICS_TEXT_BADGE_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/text_badge.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_TYPES_JS" file="diagnostics_types.js" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_UTILS_JS" file="diagnostics_utils.js" type="BINDATA"/>
       <include name="IDR_D3_SRC_D3_MIN_JS" file="../../../../third_party/d3/src/d3.min.js" type="BINDATA" />
     </includes>
   </release>
diff --git a/chromeos/components/diagnostics_ui/resources/diagnostics_card.html b/chromeos/components/diagnostics_ui/resources/diagnostics_card.html
index 22939d5..560b7f2 100644
--- a/chromeos/components/diagnostics_ui/resources/diagnostics_card.html
+++ b/chromeos/components/diagnostics_ui/resources/diagnostics_card.html
@@ -1,21 +1,20 @@
 <style include="diagnostics-shared diagnostics-fonts">
-  ::slotted([slot=chip]) {
-    background-color: var(--google-grey-100);
-    border-radius: 16px;
-    color: var(--diagnostics-overview-text-color);
-    padding: 4px 8px;
-  }
   ::slotted([slot=icon]) {
-    background-color: rgba(var(--google-blue-refresh-500-rgb), .12);
+    background-color: var(--google-blue-50);
     border-radius: 50%;
     color: var(--google-blue-600);
+    height: 26px;
     left: 5px;
+    margin-left: 20px;
+    margin-right: 28px;
     padding: 2px;
     position: relative;
+    width: 20px;
   }
 
   ::slotted([slot=left-panel]) {
     flex: 1;
+    margin-bottom: 16px;
   }
 
   .card-container {
@@ -25,7 +24,6 @@
   }
 
   .card-header {
-    @apply --diagnostics-default-font;
     display: flex;
     justify-content: space-between;
     min-height: 25px;
@@ -44,10 +42,6 @@
     padding-inline: var(--data-point-container-padding);
   }
 
-  .icon {
-    width: 10%;
-  }
-
   .routine-container {
     @apply --diagnostics-default-font;
   }
@@ -61,11 +55,7 @@
   .top-section {
     border-bottom: 1px solid var(--diagnostics-border-color);
     display: flex;
-    margin-bottom: 6px;
-    margin-top: 12px;
-    padding-bottom: 12px;
-    padding-inline-end: 15px;
-    padding-inline-start: 15px;
+    margin-top: 16px;
   }
 </style>
 <div class="card-container">
@@ -75,9 +65,7 @@
   </div>
   <div class="card-wrapper">
     <div class="top-section">
-      <div class="icon">
-        <slot name="icon"></slot>
-      </div>
+      <slot name="icon"></slot>
       <slot name="left-panel"></slot>
     </div>
     <div id="body" class="data-points" hidden$="[[hideDataPoints]]">
diff --git a/chromeos/components/diagnostics_ui/resources/diagnostics_fonts_css.html b/chromeos/components/diagnostics_ui/resources/diagnostics_fonts_css.html
index 739a375..9614c6f 100644
--- a/chromeos/components/diagnostics_ui/resources/diagnostics_fonts_css.html
+++ b/chromeos/components/diagnostics_ui/resources/diagnostics_fonts_css.html
@@ -4,6 +4,7 @@
       --diagnostics-default-font-family: "Google Sans", Roboto, sans-serif;
       --diagnostics-header-font-family: Roboto, sans-serif;
       --diagnostics-data-point-font-family: Roboto, sans-serif;
+      --diagnostics-overview-font-family: Roboto, sans-serif;
 
       --diagnostics-default-font-size: 14px;
       --diagnostics-header-font-size: 22px;
@@ -18,11 +19,11 @@
       --diagnostics-header-font-weight: 600;
       --diagnostics-data-point-title-font-weight: 400;
       --diagnostics-data-point-subtitle-font-weight: 450;
-      --diagnostics-overview-font-weight: 500;
+      --diagnostics-overview-font-weight: 400;
 
       --diagnostics-default-text-color: var(--google-grey-900);
       --diagnostics-header-text-color: var(--google-grey-900);
-      --diagnostics-overview-text-color: var(--google-grey-600);
+      --diagnostics-overview-text-color: var(--google-grey-900);
       --diagnostics-data-point-title-color: var(--google-grey-900);
       --diagnostics-data-point-subtitle-color: var(--google-grey-700);
 
@@ -48,7 +49,7 @@
           font-weight: var(--diagnostics-data-point-subtitle-font-weight);
       };
       --diagnostics-overview-font: {
-          font-family: var(--diagnostics-default-font-family);
+          font-family: var(--diagnostics-overview-font-family);
           font-size: var(--diagnostics-overview-font-size);
           font-weight: var(--diagnostics-overview-font-weight);
       };
diff --git a/chromeos/components/diagnostics_ui/resources/diagnostics_shared_css.html b/chromeos/components/diagnostics_ui/resources/diagnostics_shared_css.html
index 7db368eb..18354ee 100644
--- a/chromeos/components/diagnostics_ui/resources/diagnostics_shared_css.html
+++ b/chromeos/components/diagnostics_ui/resources/diagnostics_shared_css.html
@@ -21,6 +21,15 @@
       margin-left: 20px;
     }
 
+    .diagnostics-chip {
+      @apply --diagnostics-overview-font;
+      background-color: var(--google-grey-200);
+      border-radius: 16px;
+      color: var(--diagnostics-overview-text-color);
+      height: 20px;
+      padding: 4px 8px 0;
+    }
+
     .divider {
       border-left: 1px solid var(--google-grey-200);
       height: 32px;
diff --git a/chromeos/components/diagnostics_ui/resources/diagnostics_utils.js b/chromeos/components/diagnostics_ui/resources/diagnostics_utils.js
new file mode 100644
index 0000000..f299951
--- /dev/null
+++ b/chromeos/components/diagnostics_ui/resources/diagnostics_utils.js
@@ -0,0 +1,14 @@
+// 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.
+
+/**
+ * Converts a KiB storage value to GiB and returns a fixed-point string
+ * to the desired number of decimal places.
+ * @param {number} value
+ * @param {number} numDecimalPlaces
+ * @return {string}
+ */
+export function convertKibToGibDecimalString(value, numDecimalPlaces) {
+  return (value / 2 ** 20).toFixed(numDecimalPlaces);
+}
diff --git a/chromeos/components/diagnostics_ui/resources/memory_card.html b/chromeos/components/diagnostics_ui/resources/memory_card.html
index 7e5cb38..0dbd498 100644
--- a/chromeos/components/diagnostics_ui/resources/memory_card.html
+++ b/chromeos/components/diagnostics_ui/resources/memory_card.html
@@ -4,7 +4,8 @@
 <diagnostics-card hide-data-points="true">
   <div id="cardTitle" slot="title">[[i18n('memoryTitle')]]</div>
   <iron-icon slot="icon" icon="cr:add"></iron-icon>
-  <percent-bar-chart slot="left-panel" header="[[i18n('usedMemory')]]"
+  <percent-bar-chart slot="left-panel"
+    header="[[getAvailableMemory_(memoryUsage_)]]"
     value="[[getTotalUsedMemory_(memoryUsage_)]]"
     max="[[memoryUsage_.totalMemoryKib]]">
   </percent-bar-chart>
diff --git a/chromeos/components/diagnostics_ui/resources/memory_card.js b/chromeos/components/diagnostics_ui/resources/memory_card.js
index 5dd2d70..b74353e 100644
--- a/chromeos/components/diagnostics_ui/resources/memory_card.js
+++ b/chromeos/components/diagnostics_ui/resources/memory_card.js
@@ -12,9 +12,11 @@
 import './strings.m.js';
 
 import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {MemoryUsage, RoutineType, SystemDataProviderInterface} from './diagnostics_types.js'
+import {convertKibToGibDecimalString} from './diagnostics_utils.js';
 import {getSystemDataProvider} from './mojo_interface_provider.js';
 
 /**
@@ -103,5 +105,19 @@
    */
   getTotalUsedMemory_(memoryUsage) {
     return memoryUsage.totalMemoryKib - memoryUsage.availableMemoryKib;
-  }
+  },
+
+  /**
+   * Calculates total available memory from MemoryUsage object.
+   * @return {string}
+   * @protected
+   */
+  getAvailableMemory_() {
+    // Note: The storage value is converted to GiB but we still display "GB" to
+    // the user since this is the convention memory manufacturers use.
+    return loadTimeData.getStringF(
+        'memoryAvailable',
+        convertKibToGibDecimalString(this.memoryUsage_.availableMemoryKib, 2),
+        convertKibToGibDecimalString(this.memoryUsage_.totalMemoryKib, 2));
+  },
 });
diff --git a/chromeos/components/diagnostics_ui/resources/overview_card.html b/chromeos/components/diagnostics_ui/resources/overview_card.html
index 8e510740..08ae8b0 100644
--- a/chromeos/components/diagnostics_ui/resources/overview_card.html
+++ b/chromeos/components/diagnostics_ui/resources/overview_card.html
@@ -1,25 +1,17 @@
 <style include="diagnostics-shared diagnostics-fonts">
   #marketingName {
-    font-weight: var(--diagnostics-overview-font-weight);
-    margin-right: 5px;
+    font-weight: 500;
+    margin-right: 4px;
   }
 
   #overviewCardContainer {
     display: flex;
+    height: 48px;
     justify-content: center;
   }
-
-  .overview-chip {
-    @apply --diagnostics-overview-font;
-    background-color: var(--google-grey-100);
-    border-radius: 16px;
-    color: var(--diagnostics-overview-text-color);
-    margin-right: 15px;
-    padding: 10px;
-  }
 </style>
 <div id="overviewCardContainer">
-  <div class="overview-chip">
+  <div class="diagnostics-chip">
     <span id="marketingName">[[systemInfo_.marketingName]]</span>
     <span id="deviceInfo">[[deviceInfo_]]</span>
   </div>
diff --git a/chromeos/components/diagnostics_ui/resources/percent_bar_chart.html b/chromeos/components/diagnostics_ui/resources/percent_bar_chart.html
index 1d78d87..dbc6f6fb 100644
--- a/chromeos/components/diagnostics_ui/resources/percent_bar_chart.html
+++ b/chromeos/components/diagnostics_ui/resources/percent_bar_chart.html
@@ -1,7 +1,11 @@
 <style include="diagnostics-shared diagnostics-fonts">
-  #barChartContainer > label {
+  #chartName {
     @apply --diagnostics-chart-title-font;
-    color: var(--diagnostics-overview-text-color);
+    color: var(--google-grey-700);
+    font-family: Roboto;
+    font-size: 13px;
+    line-height: 20px;
+    margin-top: 8px;
   }
 
   #headerIcon {
@@ -18,28 +22,20 @@
     width: 85%;
     --paper-progress-active-color: var(--google-blue-500);
     --paper-progress-container-color: var(--google-blue-100);
-    --paper-progress-height: 8px;
+    --paper-progress-height: 4px;
   }
-
-  #percentageLabel {
-    @apply --diagnostics-chart-label-font;
-    display: inline;
-    padding-left: 5px;
-  }
-
 </style>
 
 <div id="barChartContainer">
+  <div class="body">
+    <paper-progress id="barChart"
+      value="[[getAdjustedValue_(value, max)]]" max="[[max]]">
+    </paper-progress>
+  </div>
   <label id="chartName">
     <template is="dom-if" if="[[headerIcon.length]]">
       <iron-icon id="headerIcon" icon="[[headerIcon]]"></iron-icon>
     </template>
     [[header]]
   </label>
-  <div class="body">
-    <paper-progress id="barChart"
-      value="[[getAdjustedValue_(value, max)]]" max="[[max]]">
-    </paper-progress>
-    <label id="percentageLabel">[[computePercentage_(value, max)]]</label>
-  </div>
 </div>
diff --git a/chromeos/components/diagnostics_ui/resources/percent_bar_chart.js b/chromeos/components/diagnostics_ui/resources/percent_bar_chart.js
index c3a7ea8..26d5137 100644
--- a/chromeos/components/diagnostics_ui/resources/percent_bar_chart.js
+++ b/chromeos/components/diagnostics_ui/resources/percent_bar_chart.js
@@ -48,19 +48,6 @@
   },
 
   /**
-   * Returns the percentage of the current bar chart, rounded to the nearest
-   * whole number.
-   * @param {number} currentValue
-   * @param {number} maxValue
-   * @return {string} i18n string for the percentage value.
-   * @private
-   */
-  computePercentage_(currentValue, maxValue) {
-    return loadTimeData.getStringF(
-        'percentageLabel', Math.round(100 * currentValue / maxValue));
-  },
-
-  /**
    * Get adjusted value clamped to max value. paper-progress breaks for a while
    * when value is set higher than max in certain cases (e.g. due to fetching of
    * max being resolved later).
diff --git a/chromeos/components/network_ui/network_diagnostics_resource_provider.cc b/chromeos/components/network_ui/network_diagnostics_resource_provider.cc
index 9ed87316..9e93b7d 100644
--- a/chromeos/components/network_ui/network_diagnostics_resource_provider.cc
+++ b/chromeos/components/network_ui/network_diagnostics_resource_provider.cc
@@ -32,6 +32,8 @@
     {"NetworkDiagnosticsHttpsFirewall", IDS_NETWORK_DIAGNOSTICS_HTTPS_FIREWALL},
     {"NetworkDiagnosticsHttpsLatency", IDS_NETWORK_DIAGNOSTICS_HTTPS_LATENCY},
     {"NetworkDiagnosticsCaptivePortal", IDS_NETWORK_DIAGNOSTICS_CAPTIVE_PORTAL},
+    {"NetworkDiagnosticsVideoConferencing",
+     IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING},
     {"NetworkDiagnosticsPassed", IDS_NETWORK_DIAGNOSTICS_PASSED},
     {"NetworkDiagnosticsFailed", IDS_NETWORK_DIAGNOSTICS_FAILED},
     {"NetworkDiagnosticsNotRun", IDS_NETWORK_DIAGNOSTICS_NOT_RUN},
@@ -99,6 +101,12 @@
      IDS_NETWORK_DIAGNOSTICS_CAPTIVE_PORTAL_PROBLEM_PROXY_AUTH_REQUIRED},
     {"CaptivePortalProblem_NoInternet",
      IDS_NETWORK_DIAGNOSTICS_CAPTIVE_PORTAL_PROBLEM_NO_INTERNET},
+    {"VideoConferencingProblem_UdpFailure",
+     IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_UPD_FAILURE},
+    {"VideoConferencingProblem_TcpFailure",
+     IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_TCP_FAILURE},
+    {"VideoConferencingProblem_MediaFailure",
+     IDS_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_PROBLEM_MEDIA_FAILURE},
 };
 
 struct WebUiResource {
@@ -131,6 +139,7 @@
     {"NetworkDiagnosticsGatewayGroup", "Gateway"},
     {"NetworkDiagnosticsFirewallGroup", "Firewall"},
     {"NetworkDiagnosticsDnsGroup", "DNS"},
+    {"NetworkDiagnosticsGoogleServicesGroup", "Google Services"},
 };
 
 }  // namespace
diff --git a/chromeos/components/scanning/resources/scan_preview.html b/chromeos/components/scanning/resources/scan_preview.html
index 23ccf2f..bee743e 100644
--- a/chromeos/components/scanning/resources/scan_preview.html
+++ b/chromeos/components/scanning/resources/scan_preview.html
@@ -52,7 +52,7 @@
   }
 
   .preview-item {
-    border: 1px solid var(--google-grey-200);
+    border: 1px solid var(--google-grey-300);
     border-radius: 4px;
     /* Subtract 2px for the border. */
     width: calc(100% - 2px);
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 3ff4352..0d47d25 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -88,8 +88,8 @@
     "ArcManagedAdbSideloadingSupport", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Controls whether to enable support for View.onKeyPreIme() of ARC apps.
-const base::Feature kArcPreImeKeyEventSupport{
-    "ArcPreImeKeyEventSupport", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kArcPreImeKeyEventSupport{"ArcPreImeKeyEventSupport",
+                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables or disables auto screen-brightness adjustment when ambient light
 // changes.
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index 0aa4211..d4ee905 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -97,14 +97,20 @@
 // 'en-US,en' as preferred languages.
 const char kArcDisableLocaleSync[] = "arc-disable-locale-sync";
 
-// Used for development of Android app that are included into ARC++ as system
-// default apps in order to be able to install them via adb.
-const char kArcDisableSystemDefaultApps[] = "arc-disable-system-default-apps";
+// Used to disable GMS scheduling of media store periodic indexing and corpora
+// maintenance tasks. Used in performance tests to prevent running during
+// testing which can cause unstable results or CPU not idle pre-test failures.
+const char kArcDisableMediaStoreMaintenance[] =
+    "arc-disable-media-store-maintenance";
 
 // Flag that disables ARC Play Auto Install flow that installs set of predefined
 // apps silently. Used in autotests to resolve racy conditions.
 const char kArcDisablePlayAutoInstall[] = "arc-disable-play-auto-install";
 
+// Used for development of Android app that are included into ARC as system
+// default apps in order to be able to install them via adb.
+const char kArcDisableSystemDefaultApps[] = "arc-disable-system-default-apps";
+
 // Flag that forces ARC to cache icons for apps.
 const char kArcForceCacheAppIcons[] = "arc-force-cache-app-icons";
 
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index 109c851..2bef31a 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -41,6 +41,8 @@
 extern const char kArcDisableGmsCoreCache[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcDisableLocaleSync[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kArcDisableMediaStoreMaintenance[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kArcDisableSystemDefaultApps[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kArcDisablePlayAutoInstall[];
diff --git a/chromeos/login/auth/authenticator.h b/chromeos/login/auth/authenticator.h
index 08f5a75c..2e7d31c 100644
--- a/chromeos/login/auth/authenticator.h
+++ b/chromeos/login/auth/authenticator.h
@@ -44,10 +44,6 @@
   virtual void AuthenticateToLogin(content::BrowserContext* browser_context,
                                    const UserContext& user_context) = 0;
 
-  // Initiates supervised user login.
-  // TODO(crbug.com/866790): Remove this as a part of Supervised users cleanup.
-  virtual void LoginAsSupervisedUser(const UserContext& user_context) = 0;
-
   // Initiates incognito ("browse without signing in") login.
   virtual void LoginOffTheRecord() = 0;
 
diff --git a/chromeos/login/auth/cryptohome_authenticator.cc b/chromeos/login/auth/cryptohome_authenticator.cc
index d86d41c..89bb6895 100644
--- a/chromeos/login/auth/cryptohome_authenticator.cc
+++ b/chromeos/login/auth/cryptohome_authenticator.cc
@@ -113,8 +113,8 @@
       return "GUEST_LOGIN";
     case CryptohomeAuthenticator::PUBLIC_ACCOUNT_LOGIN:
       return "PUBLIC_ACCOUNT_LOGIN";
-    case CryptohomeAuthenticator::SUPERVISED_USER_LOGIN:
-      return "SUPERVISED_USER_LOGIN";
+    case CryptohomeAuthenticator::SUPERVISED_USER_LOGIN_DEPRECATED:
+      return "SUPERVISED_USER_LOGIN_DEPRECATED";
     case CryptohomeAuthenticator::LOGIN_FAILED:
       return "LOGIN_FAILED";
     case CryptohomeAuthenticator::OWNER_REQUIRED:
@@ -628,22 +628,6 @@
                      this));
 }
 
-void CryptohomeAuthenticator::LoginAsSupervisedUser(
-    const UserContext& user_context) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  DCHECK_EQ(user_manager::USER_TYPE_SUPERVISED, user_context.GetUserType());
-
-  // TODO(nkostylev): Pass proper value for |user_is_new| or remove (not used).
-  current_state_.reset(new AuthAttemptState(user_context,
-                                            false,    // unlock
-                                            false,    // online_complete
-                                            false));  // user_is_new
-  remove_user_data_on_failure_ = false;
-  StartMount(current_state_->AsWeakPtr(),
-             scoped_refptr<CryptohomeAuthenticator>(this),
-             false /* ephemeral */, false /* create_if_nonexistent */);
-}
-
 void CryptohomeAuthenticator::LoginOffTheRecord() {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   current_state_.reset(
@@ -962,7 +946,7 @@
           FROM_HERE,
           base::BindOnce(&CryptohomeAuthenticator::OnAuthSuccess, this));
       break;
-    case SUPERVISED_USER_LOGIN:
+    case SUPERVISED_USER_LOGIN_DEPRECATED:
       current_state_->user_context.SetIsUsingOAuth(false);
       task_runner_->PostTask(
           FROM_HERE,
@@ -1141,8 +1125,9 @@
     return PUBLIC_ACCOUNT_LOGIN;
   if (user_type == user_manager::USER_TYPE_KIOSK_APP)
     return KIOSK_ACCOUNT_LOGIN;
-  if (user_type == user_manager::USER_TYPE_SUPERVISED)
-    return SUPERVISED_USER_LOGIN;
+  // TODO(crbug/1155729): If this check is never true, remove the enum field.
+  if (user_type == user_manager::USER_TYPE_SUPERVISED_DEPRECATED)
+    return SUPERVISED_USER_LOGIN_DEPRECATED;
 
   if (!VerifyOwner())
     return CONTINUE;
diff --git a/chromeos/login/auth/cryptohome_authenticator.h b/chromeos/login/auth/cryptohome_authenticator.h
index d32fbde..2fa01e0 100644
--- a/chromeos/login/auth/cryptohome_authenticator.h
+++ b/chromeos/login/auth/cryptohome_authenticator.h
@@ -84,10 +84,12 @@
     ONLINE_FAILED = 15,      // Obsolete (ClientLogin): Online login disallowed,
                              // but offline succeeded.
     GUEST_LOGIN = 16,        // Logged in guest mode.
-    PUBLIC_ACCOUNT_LOGIN = 17,        // Logged into a public account.
-    SUPERVISED_USER_LOGIN = 18,       // Logged in as a supervised user.
-    LOGIN_FAILED = 19,                // Obsolete: Login denied.
-    OWNER_REQUIRED = 20,              // Login is restricted to the owner only.
+    PUBLIC_ACCOUNT_LOGIN = 17,  // Logged into a public account.
+    // TODO(crbug/1155729): Remove this enum.
+    SUPERVISED_USER_LOGIN_DEPRECATED =
+        18,               // Logged in as deprecated legacy supervised user.
+    LOGIN_FAILED = 19,    // Obsolete: Login denied.
+    OWNER_REQUIRED = 20,  // Login is restricted to the owner only.
     FAILED_USERNAME_HASH = 21,        // Failed GetSanitizedUsername request.
     KIOSK_ACCOUNT_LOGIN = 22,         // Logged into a kiosk account.
     REMOVED_DATA_AFTER_FAILURE = 23,  // Successfully removed the user's
@@ -121,11 +123,6 @@
   void AuthenticateToLogin(content::BrowserContext* context,
                            const UserContext& user_context) override;
 
-  // Initiates supervised user login.
-  // Creates cryptohome if missing or mounts existing one and
-  // notifies consumer on the success/failure.
-  void LoginAsSupervisedUser(const UserContext& user_context) override;
-
   // Initiates incognito ("browse without signing in") login.
   // Mounts tmpfs and notifies consumer on the success/failure.
   void LoginOffTheRecord() override;
diff --git a/chromeos/login/auth/stub_authenticator.cc b/chromeos/login/auth/stub_authenticator.cc
index 6218b3c..8ddc575f 100644
--- a/chromeos/login/auth/stub_authenticator.cc
+++ b/chromeos/login/auth/stub_authenticator.cc
@@ -80,13 +80,6 @@
                                 AuthFailure::FromNetworkAuthFailure(error)));
 }
 
-void StubAuthenticator::LoginAsSupervisedUser(const UserContext& user_context) {
-  UserContext new_user_context = user_context;
-  new_user_context.SetUserIDHash(user_context.GetAccountId().GetUserEmail() +
-                                 kUserIdHashSuffix);
-  consumer_->OnAuthSuccess(new_user_context);
-}
-
 void StubAuthenticator::LoginOffTheRecord() {
   consumer_->OnOffTheRecordAuthSuccess();
 }
diff --git a/chromeos/login/auth/stub_authenticator.h b/chromeos/login/auth/stub_authenticator.h
index 7e9b717..3189efb 100644
--- a/chromeos/login/auth/stub_authenticator.h
+++ b/chromeos/login/auth/stub_authenticator.h
@@ -46,7 +46,6 @@
                      const UserContext& user_context) override;
   void AuthenticateToLogin(content::BrowserContext* context,
                            const UserContext& user_context) override;
-  void LoginAsSupervisedUser(const UserContext& user_context) override;
   void LoginOffTheRecord() override;
   void LoginAsPublicSession(const UserContext& user_context) override;
   void LoginAsKioskAccount(const AccountId& app_account_id,
diff --git a/chromeos/login/login_state/login_state.cc b/chromeos/login/login_state/login_state.cc
index c0d8dbf1..bb05cbf 100644
--- a/chromeos/login/login_state/login_state.cc
+++ b/chromeos/login/login_state/login_state.cc
@@ -123,7 +123,7 @@
 bool LoginState::IsUserAuthenticated() const {
   return logged_in_user_type_ == LOGGED_IN_USER_REGULAR ||
          logged_in_user_type_ == LOGGED_IN_USER_OWNER ||
-         logged_in_user_type_ == LOGGED_IN_USER_SUPERVISED ||
+         logged_in_user_type_ == LOGGED_IN_USER_SUPERVISED_DEPRECATED ||
          logged_in_user_type_ == LOGGED_IN_USER_CHILD;
 }
 
diff --git a/chromeos/login/login_state/login_state.h b/chromeos/login/login_state/login_state.h
index 86151437..2523b93 100644
--- a/chromeos/login/login_state/login_state.h
+++ b/chromeos/login/login_state/login_state.h
@@ -27,7 +27,9 @@
     LOGGED_IN_USER_GUEST,           // A guest is logged in (i.e. incognito)
     LOGGED_IN_USER_PUBLIC_ACCOUNT,  // A user is logged in to a public session.
     LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED,  // Public session v2.
-    LOGGED_IN_USER_SUPERVISED,              // A supervised user is logged in
+    // TODO(crbug/1155729): Remove this enum field.
+    LOGGED_IN_USER_SUPERVISED_DEPRECATED,  // A deprecated legacy supervised
+                                           // user is logged in.
     LOGGED_IN_USER_KIOSK_APP,  // Is in one of the kiosk modes -- Chrome App,
                                // Arc or Web App
     LOGGED_IN_USER_CHILD       // A child is logged in
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index a9a4247..a5f2ee1 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-89-4367.0-1609757097-benchmark-89.0.4384.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-89-4380.0-1610362181-benchmark-89.0.4385.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index 97ec8d9..4d7151f 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-89-4367.0-1609764762-benchmark-89.0.4384.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-89-4374.0-1610361443-benchmark-89.0.4385.0-r1-redacted.afdo.xz
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 946e900..357e038 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -35,6 +35,7 @@
 #include "chromeos/services/assistant/libassistant_service_host_impl.h"
 #include "chromeos/services/assistant/media_session/assistant_media_session.h"
 #include "chromeos/services/assistant/platform_api_impl.h"
+#include "chromeos/services/assistant/proxy/conversation_controller_proxy.h"
 #include "chromeos/services/assistant/proxy/service_controller_proxy.h"
 #include "chromeos/services/assistant/public/cpp/assistant_client.h"
 #include "chromeos/services/assistant/public/cpp/device_actions.h"
@@ -506,22 +507,10 @@
     AssistantQuerySource source,
     bool allow_tts) {
   DVLOG(1) << __func__;
-  assistant_client::VoicelessOptions options;
-  options.is_user_initiated = true;
 
-  if (!allow_tts) {
-    options.modality =
-        assistant_client::VoicelessOptions::Modality::TYPING_MODALITY;
-  }
-
-  // Cache metadata about this interaction that can be resolved when the
-  // associated conversation turn starts in LibAssistant.
-  options.conversation_turn_id =
-      NewPendingInteraction(AssistantInteractionType::kText, source, query);
-
-  std::string interaction = CreateTextQueryInteraction(query);
-  assistant_manager_internal()->SendVoicelessInteraction(
-      interaction, /*description=*/"text_query", options, [](auto) {});
+  conversation_controller_proxy().SendTextQuery(
+      query, allow_tts,
+      NewPendingInteraction(AssistantInteractionType::kText, source, query));
 }
 
 void AssistantManagerServiceImpl::AddAssistantInteractionSubscriber(
@@ -1403,6 +1392,11 @@
   audio_input_host_->SetMicState(mic_open);
 }
 
+ConversationControllerProxy&
+AssistantManagerServiceImpl::conversation_controller_proxy() {
+  return assistant_proxy_->conversation_controller_proxy();
+}
+
 ServiceControllerProxy& AssistantManagerServiceImpl::service_controller() {
   return assistant_proxy_->service_controller();
 }
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h
index cff59765..e4f358cd 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -24,6 +24,7 @@
 #include "chromeos/services/assistant/assistant_settings_impl.h"
 #include "chromeos/services/assistant/chromium_api_delegate.h"
 #include "chromeos/services/assistant/proxy/assistant_proxy.h"
+#include "chromeos/services/assistant/proxy/conversation_controller_proxy.h"
 #include "chromeos/services/assistant/proxy/libassistant_service_host.h"
 #include "chromeos/services/assistant/public/cpp/assistant_notification.h"
 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
@@ -299,6 +300,7 @@
   DeviceActions* device_actions();
   scoped_refptr<base::SequencedTaskRunner> main_task_runner();
 
+  ConversationControllerProxy& conversation_controller_proxy();
   CrosDisplayConnection* display_connection();
   ServiceControllerProxy& service_controller();
   const ServiceControllerProxy& service_controller() const;
diff --git a/chromeos/services/assistant/proxy/BUILD.gn b/chromeos/services/assistant/proxy/BUILD.gn
index dc5ee5ae..56808eaf 100644
--- a/chromeos/services/assistant/proxy/BUILD.gn
+++ b/chromeos/services/assistant/proxy/BUILD.gn
@@ -11,6 +11,8 @@
   sources = [
     "assistant_proxy.cc",
     "assistant_proxy.h",
+    "conversation_controller_proxy.cc",
+    "conversation_controller_proxy.h",
     "libassistant_service_host.h",
     "service_controller_proxy.cc",
     "service_controller_proxy.h",
diff --git a/chromeos/services/assistant/proxy/assistant_proxy.cc b/chromeos/services/assistant/proxy/assistant_proxy.cc
index 6cee87f..cfdf6fba 100644
--- a/chromeos/services/assistant/proxy/assistant_proxy.cc
+++ b/chromeos/services/assistant/proxy/assistant_proxy.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/check.h"
+#include "chromeos/services/assistant/proxy/conversation_controller_proxy.h"
 #include "chromeos/services/assistant/proxy/libassistant_service_host.h"
 #include "chromeos/services/assistant/proxy/service_controller_proxy.h"
 #include "chromeos/services/libassistant/libassistant_service.h"
@@ -30,6 +31,9 @@
 
   service_controller_proxy_ =
       std::make_unique<ServiceControllerProxy>(host, BindServiceController());
+  conversation_controller_proxy_ =
+      std::make_unique<ConversationControllerProxy>(
+          BindConversationController());
 }
 
 void AssistantProxy::LaunchLibassistantService() {
@@ -78,6 +82,14 @@
   return pending_remote;
 }
 
+mojo::PendingRemote<AssistantProxy::ConversationControllerMojom>
+AssistantProxy::BindConversationController() {
+  mojo::PendingRemote<ConversationControllerMojom> pending_remote;
+  libassistant_service_remote_->BindConversationController(
+      pending_remote.InitWithNewPipeAndPassReceiver());
+  return pending_remote;
+}
+
 scoped_refptr<base::SingleThreadTaskRunner>
 AssistantProxy::background_task_runner() {
   return background_thread_.task_runner();
@@ -88,5 +100,10 @@
   return *service_controller_proxy_;
 }
 
+ConversationControllerProxy& AssistantProxy::conversation_controller_proxy() {
+  DCHECK(conversation_controller_proxy_);
+  return *conversation_controller_proxy_;
+}
+
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/proxy/assistant_proxy.h b/chromeos/services/assistant/proxy/assistant_proxy.h
index bece8e34..4cadf62 100644
--- a/chromeos/services/assistant/proxy/assistant_proxy.h
+++ b/chromeos/services/assistant/proxy/assistant_proxy.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/threading/thread.h"
+#include "chromeos/services/libassistant/public/mojom/conversation_controller.mojom.h"
 #include "chromeos/services/libassistant/public/mojom/service.mojom.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
@@ -20,6 +21,7 @@
 namespace chromeos {
 namespace assistant {
 
+class ConversationControllerProxy;
 class LibassistantServiceHost;
 class ServiceControllerProxy;
 
@@ -38,6 +40,9 @@
   // service.
   ServiceControllerProxy& service_controller();
 
+  // Returns the controller that manages conversations with Libassistant.
+  ConversationControllerProxy& conversation_controller_proxy();
+
   // The background thread is temporary exposed until the entire Libassistant
   // API is hidden behind this proxy API.
   base::Thread& background_thread() { return background_thread_; }
@@ -47,6 +52,8 @@
       chromeos::libassistant::mojom::LibassistantService;
   using ServiceControllerMojom =
       chromeos::libassistant::mojom::ServiceController;
+  using ConversationControllerMojom =
+      chromeos::libassistant::mojom::ConversationController;
 
   scoped_refptr<base::SingleThreadTaskRunner> background_task_runner();
 
@@ -57,12 +64,14 @@
   void StopLibassistantServiceOnBackgroundThread();
 
   mojo::PendingRemote<ServiceControllerMojom> BindServiceController();
+  mojo::PendingRemote<ConversationControllerMojom> BindConversationController();
 
   // Owned by |AssistantManagerServiceImpl|.
   LibassistantServiceHost* libassistant_service_host_ = nullptr;
   mojo::Remote<LibassistantServiceMojom> libassistant_service_remote_;
 
   std::unique_ptr<ServiceControllerProxy> service_controller_proxy_;
+  std::unique_ptr<ConversationControllerProxy> conversation_controller_proxy_;
 
   // The thread on which the Libassistant service runs.
   // Warning: must be the last object, so it is destroyed (and flushed) first.
diff --git a/chromeos/services/assistant/proxy/conversation_controller_proxy.cc b/chromeos/services/assistant/proxy/conversation_controller_proxy.cc
new file mode 100644
index 0000000..8a1a159
--- /dev/null
+++ b/chromeos/services/assistant/proxy/conversation_controller_proxy.cc
@@ -0,0 +1,28 @@
+// 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 "chromeos/services/assistant/proxy/conversation_controller_proxy.h"
+
+#include "chromeos/assistant/internal/internal_util.h"
+
+namespace chromeos {
+namespace assistant {
+
+ConversationControllerProxy::ConversationControllerProxy(
+    mojo::PendingRemote<ConversationController> conversation_controller_remote)
+    : conversation_controller_remote_(
+          std::move(conversation_controller_remote)) {}
+
+ConversationControllerProxy::~ConversationControllerProxy() = default;
+
+void ConversationControllerProxy::SendTextQuery(
+    const std::string& query,
+    bool allow_tts,
+    const std::string& conversation_id) {
+  conversation_controller_remote_->SendTextQuery(query, allow_tts,
+                                                 conversation_id);
+}
+
+}  // namespace assistant
+}  // namespace chromeos
diff --git a/chromeos/services/assistant/proxy/conversation_controller_proxy.h b/chromeos/services/assistant/proxy/conversation_controller_proxy.h
new file mode 100644
index 0000000..a39835d
--- /dev/null
+++ b/chromeos/services/assistant/proxy/conversation_controller_proxy.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef CHROMEOS_SERVICES_ASSISTANT_PROXY_CONVERSATION_CONTROLLER_PROXY_H_
+#define CHROMEOS_SERVICES_ASSISTANT_PROXY_CONVERSATION_CONTROLLER_PROXY_H_
+
+#include <string>
+
+#include "chromeos/services/libassistant/public/mojom/conversation_controller.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace chromeos {
+namespace assistant {
+
+using chromeos::libassistant::mojom::ConversationController;
+
+class ConversationControllerProxy {
+ public:
+  explicit ConversationControllerProxy(
+      mojo::PendingRemote<ConversationController>
+          conversation_controller_remote);
+  ConversationControllerProxy(const ConversationControllerProxy&) = delete;
+  ConversationControllerProxy& operator=(const ConversationControllerProxy&) =
+      delete;
+  ~ConversationControllerProxy();
+
+  void SendTextQuery(const std::string& query,
+                     bool allow_tts,
+                     const std::string& conversation_id);
+
+ private:
+  mojo::Remote<ConversationController> conversation_controller_remote_;
+};
+
+}  // namespace assistant
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_ASSISTANT_PROXY_CONVERSATION_CONTROLLER_PROXY_H_
diff --git a/chromeos/services/assistant/test_support/fake_libassistant_service.h b/chromeos/services/assistant/test_support/fake_libassistant_service.h
index 9265ec6..413b842f 100644
--- a/chromeos/services/assistant/test_support/fake_libassistant_service.h
+++ b/chromeos/services/assistant/test_support/fake_libassistant_service.h
@@ -33,9 +33,11 @@
   void BindServiceController(
       mojo::PendingReceiver<libassistant::mojom::ServiceController> receiver)
       override;
+  void BindConversationController(
+      mojo::PendingReceiver<libassistant::mojom::ConversationController>
+          receiver) override {}
   void BindAudioInputController() override {}
   void BindAudioOutputController() override {}
-  void BindInteractionController() override {}
 
  private:
   mojo::Receiver<libassistant::mojom::LibassistantService> receiver_;
diff --git a/chromeos/services/libassistant/BUILD.gn b/chromeos/services/libassistant/BUILD.gn
index 674865d..10babbf 100644
--- a/chromeos/services/libassistant/BUILD.gn
+++ b/chromeos/services/libassistant/BUILD.gn
@@ -33,6 +33,8 @@
 
   sources = [
     "assistant_manager_observer.h",
+    "conversation_controller.cc",
+    "conversation_controller.h",
     "platform_api.cc",
     "platform_api.h",
     "service_controller.cc",
@@ -40,9 +42,12 @@
   ]
 
   deps = [
+    "//chromeos/assistant/internal",
+    "//chromeos/assistant/internal/proto/google3",
     "//chromeos/services/assistant/public/cpp",
     "//chromeos/services/assistant/public/cpp/migration",
     "//chromeos/services/libassistant/public/mojom",
+    "//libassistant/shared/internal_api:assistant_manager_internal",
     "//libassistant/shared/public",
   ]
 
diff --git a/chromeos/services/libassistant/conversation_controller.cc b/chromeos/services/libassistant/conversation_controller.cc
new file mode 100644
index 0000000..dd7bdc1
--- /dev/null
+++ b/chromeos/services/libassistant/conversation_controller.cc
@@ -0,0 +1,68 @@
+// 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 "chromeos/services/libassistant/conversation_controller.h"
+
+#include "chromeos/assistant/internal/internal_util.h"
+#include "chromeos/services/libassistant/public/mojom/conversation_controller.mojom.h"
+#include "chromeos/services/libassistant/service_controller.h"
+#include "libassistant/shared/internal_api/assistant_manager_internal.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+
+namespace chromeos {
+namespace libassistant {
+
+ConversationController::ConversationController(
+    ServiceController* service_controller)
+    : receiver_(this), service_controller_(service_controller) {
+  DCHECK(service_controller_);
+}
+
+ConversationController::~ConversationController() = default;
+
+void ConversationController::Bind(
+    mojo::PendingReceiver<mojom::ConversationController> receiver) {
+  // Cannot bind the receiver twice.
+  DCHECK(!receiver_.is_bound());
+  receiver_.Bind(std::move(receiver));
+}
+
+void ConversationController::SendTextQuery(
+    const std::string& query,
+    bool allow_tts,
+    const base::Optional<std::string>& conversation_id) {
+  // DCHECKs if this function gets invoked after the service has been fully
+  // started.
+  // TODO(meilinw): only check for the |ServiceState::kRunning| state instead
+  // after it has been wired up.
+  DCHECK(service_controller_->IsStarted())
+      << "Libassistant service is not ready to handle queries.";
+  DCHECK(assistant_manager_internal());
+
+  // Configs |VoicelessOptions|.
+  assistant_client::VoicelessOptions options;
+  options.is_user_initiated = true;
+  if (!allow_tts) {
+    options.modality =
+        assistant_client::VoicelessOptions::Modality::TYPING_MODALITY;
+  }
+  // Ensure LibAssistant uses the requested conversation id.
+  if (conversation_id.has_value())
+    options.conversation_turn_id = conversation_id.value();
+
+  // Builds text interaction.
+  std::string interaction =
+      chromeos::assistant::CreateTextQueryInteraction(query);
+
+  assistant_manager_internal()->SendVoicelessInteraction(
+      interaction, /*description=*/"text_query", options, [](auto) {});
+}
+
+assistant_client::AssistantManagerInternal*
+ConversationController::assistant_manager_internal() {
+  return service_controller_->assistant_manager_internal();
+}
+
+}  // namespace libassistant
+}  // namespace chromeos
diff --git a/chromeos/services/libassistant/conversation_controller.h b/chromeos/services/libassistant/conversation_controller.h
new file mode 100644
index 0000000..52ba96dc
--- /dev/null
+++ b/chromeos/services/libassistant/conversation_controller.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef CHROMEOS_SERVICES_LIBASSISTANT_CONVERSATION_CONTROLLER_H_
+#define CHROMEOS_SERVICES_LIBASSISTANT_CONVERSATION_CONTROLLER_H_
+
+#include "base/component_export.h"
+#include "base/optional.h"
+#include "chromeos/services/libassistant/public/mojom/conversation_controller.mojom.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+namespace assistant_client {
+class AssistantManagerInternal;
+}  // namespace assistant_client
+
+namespace chromeos {
+namespace libassistant {
+
+class ServiceController;
+
+class COMPONENT_EXPORT(LIBASSISTANT_SERVICE) ConversationController
+    : public mojom::ConversationController {
+ public:
+  explicit ConversationController(ServiceController* service_controller);
+  ConversationController(const ConversationController&) = delete;
+  ConversationController& operator=(const ConversationController&) = delete;
+  ~ConversationController() override;
+
+  void Bind(mojo::PendingReceiver<mojom::ConversationController> receiver);
+
+  // mojom::ConversationController implementation:
+  void SendTextQuery(
+      const std::string& query,
+      bool allow_tts,
+      const base::Optional<std::string>& conversation_id) override;
+
+ private:
+  assistant_client::AssistantManagerInternal* assistant_manager_internal();
+
+  mojo::Receiver<mojom::ConversationController> receiver_;
+
+  // Owned by |LibassistantService|.
+  ServiceController* const service_controller_;
+};
+
+}  // namespace libassistant
+}  // namespace chromeos
+
+#endif  //  CHROMEOS_SERVICES_LIBASSISTANT_CONVERSATION_CONTROLLER_H_
diff --git a/chromeos/services/libassistant/libassistant_service.cc b/chromeos/services/libassistant/libassistant_service.cc
index d3bbd7a..82bc4048 100644
--- a/chromeos/services/libassistant/libassistant_service.cc
+++ b/chromeos/services/libassistant/libassistant_service.cc
@@ -10,6 +10,7 @@
 #include "base/check.h"
 #include "base/logging.h"
 #include "chromeos/services/assistant/public/cpp/migration/cros_platform_api.h"
+#include "chromeos/services/libassistant/conversation_controller.h"
 #include "chromeos/services/libassistant/platform_api.h"
 #include "chromeos/services/libassistant/service_controller.h"
 
@@ -23,7 +24,9 @@
     : receiver_(this, std::move(receiver)),
       platform_api_(std::make_unique<PlatformApi>()),
       service_controller_(
-          std::make_unique<ServiceController>(delegate, platform_api_.get())) {
+          std::make_unique<ServiceController>(delegate, platform_api_.get())),
+      conversation_controller_(
+          std::make_unique<ConversationController>(service_controller_.get())) {
   platform_api_->SetAudioInputProvider(&platform_api->GetAudioInputProvider())
       .SetAudioOutputProvider(&platform_api->GetAudioOutputProvider())
       .SetAuthProvider(&platform_api->GetAuthProvider())
@@ -43,5 +46,10 @@
   service_controller().SetInitializeCallback(std::move(callback));
 }
 
+void LibassistantService::BindConversationController(
+    mojo::PendingReceiver<mojom::ConversationController> receiver) {
+  conversation_controller_->Bind(std::move(receiver));
+}
+
 }  // namespace libassistant
 }  // namespace chromeos
diff --git a/chromeos/services/libassistant/libassistant_service.h b/chromeos/services/libassistant/libassistant_service.h
index c485d11..c15c17a 100644
--- a/chromeos/services/libassistant/libassistant_service.h
+++ b/chromeos/services/libassistant/libassistant_service.h
@@ -8,7 +8,9 @@
 #include <memory>
 
 #include "base/component_export.h"
+#include "chromeos/services/libassistant/public/mojom/conversation_controller.mojom.h"
 #include "chromeos/services/libassistant/public/mojom/service.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
 namespace assistant_client {
@@ -26,6 +28,7 @@
 namespace chromeos {
 namespace libassistant {
 
+class ConversationController;
 class PlatformApi;
 class ServiceController;
 
@@ -52,13 +55,15 @@
   // mojom::LibassistantService implementation:
   void BindServiceController(
       mojo::PendingReceiver<mojom::ServiceController> receiver) override;
+  void BindConversationController(
+      mojo::PendingReceiver<mojom::ConversationController> receiver) override;
   void BindAudioInputController() override {}
   void BindAudioOutputController() override {}
-  void BindInteractionController() override {}
 
   mojo::Receiver<mojom::LibassistantService> receiver_;
   std::unique_ptr<PlatformApi> platform_api_;
   std::unique_ptr<ServiceController> service_controller_;
+  std::unique_ptr<ConversationController> conversation_controller_;
 };
 
 }  // namespace libassistant
diff --git a/chromeos/services/libassistant/public/mojom/BUILD.gn b/chromeos/services/libassistant/public/mojom/BUILD.gn
index 96ead4f..2a52ce7b 100644
--- a/chromeos/services/libassistant/public/mojom/BUILD.gn
+++ b/chromeos/services/libassistant/public/mojom/BUILD.gn
@@ -6,6 +6,7 @@
 
 mojom("mojom") {
   sources = [
+    "conversation_controller.mojom",
     "service.mojom",
     "service_controller.mojom",
   ]
diff --git a/chromeos/services/libassistant/public/mojom/conversation_controller.mojom b/chromeos/services/libassistant/public/mojom/conversation_controller.mojom
new file mode 100644
index 0000000..3672263
--- /dev/null
+++ b/chromeos/services/libassistant/public/mojom/conversation_controller.mojom
@@ -0,0 +1,14 @@
+// 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.
+
+module chromeos.libassistant.mojom;
+
+// Interface for controller in charge of Assistant conversations.
+interface ConversationController {
+  // Sends the specific text query to Libassistant. |conversation_id| is an
+  // identifier that will be propagated through the speech processor event
+  // pipeline to uniquely identify a conversation turn. If omitted, a unique
+  // identifier will be automatically generated.
+  SendTextQuery(string query, bool allow_tts, string? conversation_id);
+};
diff --git a/chromeos/services/libassistant/public/mojom/service.mojom b/chromeos/services/libassistant/public/mojom/service.mojom
index c79d1fb..252b8470 100644
--- a/chromeos/services/libassistant/public/mojom/service.mojom
+++ b/chromeos/services/libassistant/public/mojom/service.mojom
@@ -5,6 +5,7 @@
 module chromeos.libassistant.mojom;
 
 import "chromeos/services/libassistant/public/mojom/service_controller.mojom";
+import "chromeos/services/libassistant/public/mojom/conversation_controller.mojom";
 
 // The main interface to the Libassistant service on ChromeOS.
 // Libassistant provides access to the Google Assistant.
@@ -17,9 +18,12 @@
   // of the Libassistant process (start/stop).
   BindServiceController(pending_receiver<ServiceController> receiver);
 
+  // Bind the conversation controller, which in charge of handling
+  // conversations with Libassistant.
+  BindConversationController(pending_receiver<ConversationController> receiver);
+
   // This service will further expose methods to bind all other helper
   // controllers.
   BindAudioInputController(/* pending_receiver */);
   BindAudioOutputController(/* pending_receiver */);
-  BindInteractionController(/* pending_receiver */);
 };
diff --git a/chromeos/services/libassistant/service_controller.cc b/chromeos/services/libassistant/service_controller.cc
index 3f7a799..c5e014e 100644
--- a/chromeos/services/libassistant/service_controller.cc
+++ b/chromeos/services/libassistant/service_controller.cc
@@ -124,7 +124,7 @@
 }
 
 bool ServiceController::IsStarted() const {
-  return state_ != mojom::ServiceState::kStopped;
+  return state_ != ServiceState::kStopped;
 }
 
 bool ServiceController::IsInitialized() const {
diff --git a/chromeos/services/machine_learning/public/cpp/fake_service_connection.cc b/chromeos/services/machine_learning/public/cpp/fake_service_connection.cc
index d16e68e..23d02dd 100644
--- a/chromeos/services/machine_learning/public/cpp/fake_service_connection.cc
+++ b/chromeos/services/machine_learning/public/cpp/fake_service_connection.cc
@@ -71,7 +71,7 @@
     mojo::PendingReceiver<mojom::HandwritingRecognizer> receiver,
     mojom::MachineLearningService::LoadHandwritingModelCallback callback) {
   ScheduleCall(base::BindOnce(
-      &FakeServiceConnectionImpl::HandleLoadHandwritingModel,
+      &FakeServiceConnectionImpl::HandleLoadHandwritingModelCall,
       base::Unretained(this), std::move(receiver), std::move(callback)));
 }
 
@@ -81,7 +81,7 @@
     mojom::MachineLearningService::LoadHandwritingModelWithSpecCallback
         callback) {
   ScheduleCall(base::BindOnce(
-      &FakeServiceConnectionImpl::HandleLoadHandwritingModelWithSpec,
+      &FakeServiceConnectionImpl::HandleLoadHandwritingModelWithSpecCall,
       base::Unretained(this), std::move(receiver), std::move(callback)));
 }
 
@@ -89,7 +89,7 @@
     mojo::PendingReceiver<mojom::GrammarChecker> receiver,
     mojom::MachineLearningService::LoadGrammarCheckerCallback callback) {
   ScheduleCall(base::BindOnce(
-      &FakeServiceConnectionImpl::HandleLoadGrammarChecker,
+      &FakeServiceConnectionImpl::HandleLoadGrammarCheckerCall,
       base::Unretained(this), std::move(receiver), std::move(callback)));
 }
 void FakeServiceConnectionImpl::LoadSpeechRecognizer(
@@ -98,7 +98,7 @@
     mojo::PendingReceiver<mojom::SodaRecognizer> soda_recognizer,
     mojom::MachineLearningService::LoadSpeechRecognizerCallback callback) {
   ScheduleCall(
-      base::BindOnce(&FakeServiceConnectionImpl::HandleLoadSpeechRecognizer,
+      base::BindOnce(&FakeServiceConnectionImpl::HandleLoadSpeechRecognizerCall,
                      base::Unretained(this), std::move(soda_client),
                      std::move(soda_recognizer), std::move(callback)));
 }
@@ -303,7 +303,7 @@
 void FakeServiceConnectionImpl::Recognize(
     mojom::HandwritingRecognitionQueryPtr query,
     mojom::HandwritingRecognizer::RecognizeCallback callback) {
-  ScheduleCall(base::BindOnce(&FakeServiceConnectionImpl::HandleRecognize,
+  ScheduleCall(base::BindOnce(&FakeServiceConnectionImpl::HandleRecognizeCall,
                               base::Unretained(this), std::move(query),
                               std::move(callback)));
 }
@@ -312,36 +312,36 @@
     mojom::GrammarCheckerQueryPtr query,
     mojom::GrammarChecker::CheckCallback callback) {
   ScheduleCall(base::BindOnce(
-      &FakeServiceConnectionImpl::HandleGrammarCheckerQuery,
+      &FakeServiceConnectionImpl::HandleGrammarCheckerQueryCall,
       base::Unretained(this), std::move(query), std::move(callback)));
 }
-void FakeServiceConnectionImpl::HandleStop() {
+void FakeServiceConnectionImpl::HandleStopCall() {
   // Do something on the client
 }
 
-void FakeServiceConnectionImpl::HandleStart() {
+void FakeServiceConnectionImpl::HandleStartCall() {
   // Do something on the client.
 }
 
-void FakeServiceConnectionImpl::HandleMarkDone() {
-  HandleStop();
+void FakeServiceConnectionImpl::HandleMarkDoneCall() {
+  HandleStopCall();
 }
 
 void FakeServiceConnectionImpl::AddAudio(const std::vector<uint8_t>& audio) {}
 void FakeServiceConnectionImpl::Stop() {
-  ScheduleCall(base::BindOnce(&FakeServiceConnectionImpl::HandleStop,
+  ScheduleCall(base::BindOnce(&FakeServiceConnectionImpl::HandleStopCall,
                               base::Unretained(this)));
 }
 void FakeServiceConnectionImpl::Start() {
-  ScheduleCall(base::BindOnce(&FakeServiceConnectionImpl::HandleStart,
+  ScheduleCall(base::BindOnce(&FakeServiceConnectionImpl::HandleStartCall,
                               base::Unretained(this)));
 }
 void FakeServiceConnectionImpl::MarkDone() {
-  ScheduleCall(base::BindOnce(&FakeServiceConnectionImpl::HandleMarkDone,
+  ScheduleCall(base::BindOnce(&FakeServiceConnectionImpl::HandleMarkDoneCall,
                               base::Unretained(this)));
 }
 
-void FakeServiceConnectionImpl::HandleLoadHandwritingModel(
+void FakeServiceConnectionImpl::HandleLoadHandwritingModelCall(
     mojo::PendingReceiver<mojom::HandwritingRecognizer> receiver,
     mojom::MachineLearningService::LoadHandwritingModelCallback callback) {
   if (load_handwriting_model_result_ == mojom::LoadHandwritingModelResult::OK)
@@ -349,7 +349,7 @@
   std::move(callback).Run(load_handwriting_model_result_);
 }
 
-void FakeServiceConnectionImpl::HandleLoadHandwritingModelWithSpec(
+void FakeServiceConnectionImpl::HandleLoadHandwritingModelWithSpecCall(
     mojo::PendingReceiver<mojom::HandwritingRecognizer> receiver,
     mojom::MachineLearningService::LoadHandwritingModelWithSpecCallback
         callback) {
@@ -359,13 +359,13 @@
   std::move(callback).Run(load_model_result_);
 }
 
-void FakeServiceConnectionImpl::HandleRecognize(
+void FakeServiceConnectionImpl::HandleRecognizeCall(
     mojom::HandwritingRecognitionQueryPtr query,
     mojom::HandwritingRecognizer::RecognizeCallback callback) {
   std::move(callback).Run(handwriting_result_.Clone());
 }
 
-void FakeServiceConnectionImpl::HandleLoadGrammarChecker(
+void FakeServiceConnectionImpl::HandleLoadGrammarCheckerCall(
     mojo::PendingReceiver<mojom::GrammarChecker> receiver,
     mojom::MachineLearningService::LoadGrammarCheckerCallback callback) {
   if (load_model_result_ == mojom::LoadModelResult::OK)
@@ -373,7 +373,7 @@
 
   std::move(callback).Run(load_model_result_);
 }
-void FakeServiceConnectionImpl::HandleLoadSpeechRecognizer(
+void FakeServiceConnectionImpl::HandleLoadSpeechRecognizerCall(
     mojo::PendingRemote<mojom::SodaClient> soda_client,
     mojo::PendingReceiver<mojom::SodaRecognizer> soda_recognizer,
     mojom::MachineLearningService::LoadSpeechRecognizerCallback callback) {
@@ -384,7 +384,7 @@
   std::move(callback).Run(load_soda_result_);
 }
 
-void FakeServiceConnectionImpl::HandleGrammarCheckerQuery(
+void FakeServiceConnectionImpl::HandleGrammarCheckerQueryCall(
     mojom::GrammarCheckerQueryPtr query,
     mojom::GrammarChecker::CheckCallback callback) {
   std::move(callback).Run(grammar_checker_result_.Clone());
diff --git a/chromeos/services/machine_learning/public/cpp/fake_service_connection.h b/chromeos/services/machine_learning/public/cpp/fake_service_connection.h
index c517308f..f052bc5 100644
--- a/chromeos/services/machine_learning/public/cpp/fake_service_connection.h
+++ b/chromeos/services/machine_learning/public/cpp/fake_service_connection.h
@@ -204,29 +204,29 @@
   void HandleFindLanguagesCall(
       std::string text,
       mojom::TextClassifier::FindLanguagesCallback callback);
-  void HandleLoadHandwritingModel(
+  void HandleLoadHandwritingModelCall(
       mojo::PendingReceiver<mojom::HandwritingRecognizer> receiver,
       mojom::MachineLearningService::LoadHandwritingModelCallback callback);
-  void HandleLoadHandwritingModelWithSpec(
+  void HandleLoadHandwritingModelWithSpecCall(
       mojo::PendingReceiver<mojom::HandwritingRecognizer> receiver,
       mojom::MachineLearningService::LoadHandwritingModelWithSpecCallback
           callback);
-  void HandleRecognize(
+  void HandleRecognizeCall(
       mojom::HandwritingRecognitionQueryPtr query,
       mojom::HandwritingRecognizer::RecognizeCallback callback);
-  void HandleLoadGrammarChecker(
+  void HandleLoadGrammarCheckerCall(
       mojo::PendingReceiver<mojom::GrammarChecker> receiver,
       mojom::MachineLearningService::LoadGrammarCheckerCallback callback);
-  void HandleGrammarCheckerQuery(mojom::GrammarCheckerQueryPtr query,
+  void HandleGrammarCheckerQueryCall(mojom::GrammarCheckerQueryPtr query,
                                  mojom::GrammarChecker::CheckCallback callback);
-  void HandleLoadSpeechRecognizer(
+  void HandleLoadSpeechRecognizerCall(
       mojo::PendingRemote<mojom::SodaClient> soda_client,
       mojo::PendingReceiver<mojom::SodaRecognizer> soda_recognizer,
       mojom::MachineLearningService::LoadSpeechRecognizerCallback callback);
 
-  void HandleStop();
-  void HandleStart();
-  void HandleMarkDone();
+  void HandleStopCall();
+  void HandleStartCall();
+  void HandleMarkDoneCall();
 
   mojo::ReceiverSet<mojom::Model> model_receivers_;
   mojo::ReceiverSet<mojom::GraphExecutor> graph_receivers_;
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 287b9d43..0aea246 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -75,6 +75,7 @@
     "//components/client_update_protocol:unit_tests",
     "//components/cloud_devices/common:unit_tests",
     "//components/component_updater:unit_tests",
+    "//components/component_updater/installer_policies:unit_tests",
     "//components/consent_auditor:unit_tests",
     "//components/content_capture/common:unit_tests",
     "//components/content_settings/core/browser:unit_tests",
@@ -595,7 +596,6 @@
       "dom_distiller/content/browser/test/test_util.cc",
       "dom_distiller/content/browser/test/test_util.h",
       "metrics/content/subprocess_metrics_provider_browsertest.cc",
-      "offline_pages/content/renovations/test/page_renovator_browsertest.cc",
       "paint_preview/renderer/paint_preview_recorder_browsertest.cc",
       "security_state/content/content_utils_browsertest.cc",
       "ukm/content/source_url_recorder_browsertest.cc",
@@ -628,8 +628,6 @@
       "//components/dom_distiller/core:test_support",
       "//components/error_page/content/browser:browser_tests",
       "//components/metrics:content",
-      "//components/offline_pages/content/renovations",
-      "//components/offline_pages/core/renovations",
       "//components/paint_preview/renderer",
       "//components/password_manager/content/browser",
       "//components/performance_manager:browser_tests",
diff --git a/components/arc/arc_util_unittest.cc b/components/arc/arc_util_unittest.cc
index b3153c5..fde639c 100644
--- a/components/arc/arc_util_unittest.cc
+++ b/components/arc/arc_util_unittest.cc
@@ -280,7 +280,7 @@
       {user_manager::USER_TYPE_REGULAR, true},
       {user_manager::USER_TYPE_GUEST, false},
       {user_manager::USER_TYPE_PUBLIC_ACCOUNT, true},
-      {user_manager::USER_TYPE_SUPERVISED, false},
+      {user_manager::USER_TYPE_SUPERVISED_DEPRECATED, false},
       {user_manager::USER_TYPE_KIOSK_APP, false},
       {user_manager::USER_TYPE_CHILD, true},
       {user_manager::USER_TYPE_ARC_KIOSK_APP, true},
diff --git a/components/arc/session/arc_container_client_adapter.cc b/components/arc/session/arc_container_client_adapter.cc
index 336d2dd..976327d 100644
--- a/components/arc/session/arc_container_client_adapter.cc
+++ b/components/arc/session/arc_container_client_adapter.cc
@@ -116,6 +116,8 @@
     request.set_arc_custom_tabs_experiment(params.arc_custom_tabs_experiment);
     request.set_disable_system_default_app(
         params.arc_disable_system_default_app);
+    request.set_disable_media_store_maintenance(
+        params.disable_media_store_maintenance);
     chromeos::SessionManagerClient::Get()->StartArcMiniContainer(
         request, std::move(callback));
   }
diff --git a/components/arc/session/arc_container_client_adapter_unittest.cc b/components/arc/session/arc_container_client_adapter_unittest.cc
index a15f22d..30eedc02 100644
--- a/components/arc/session/arc_container_client_adapter_unittest.cc
+++ b/components/arc/session/arc_container_client_adapter_unittest.cc
@@ -41,6 +41,10 @@
   content::BrowserTaskEnvironment browser_task_environment_;
 };
 
+void OnMiniInstanceStarted(bool result) {
+  DCHECK(result);
+}
+
 // b/164816080 This test ensures that a new container instance that is
 // created while handling the shutting down of the previous instance,
 // doesn't incorrectly receive the shutdown event as well.
@@ -91,6 +95,17 @@
   EXPECT_FALSE(child_observer.stopped_called());
 }
 
+TEST_F(ArcContainerClientAdapterTest, StartArc_DisableMediaStoreMaintenance) {
+  StartParams start_params;
+  start_params.disable_media_store_maintenance = true;
+  client_adapter()->StartMiniArc(std::move(start_params),
+                                 base::BindOnce(&OnMiniInstanceStarted));
+  const auto& request = chromeos::FakeSessionManagerClient::Get()
+                            ->last_start_arc_mini_container_request();
+  EXPECT_TRUE(request.has_disable_media_store_maintenance());
+  EXPECT_TRUE(request.disable_media_store_maintenance());
+}
+
 struct DalvikMemoryProfileTestParam {
   // Requested profile.
   StartParams::DalvikMemoryProfile profile;
@@ -120,9 +135,6 @@
                          ArcContainerClientAdapterDalvikMemoryProfileTest,
                          ::testing::ValuesIn(kDalvikMemoryProfileTestCases));
 
-void OnMiniInstanceStarted(bool result) {
-  DCHECK(result);
-}
 
 TEST_P(ArcContainerClientAdapterDalvikMemoryProfileTest, Profile) {
   const auto& test_param = GetParam();
diff --git a/components/arc/session/arc_session_impl.cc b/components/arc/session/arc_session_impl.cc
index c91672d..72ad8d9 100644
--- a/components/arc/session/arc_session_impl.cc
+++ b/components/arc/session/arc_session_impl.cc
@@ -460,6 +460,14 @@
   params.arc_disable_system_default_app =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
           chromeos::switches::kArcDisableSystemDefaultApps);
+  if (params.arc_disable_system_default_app)
+    VLOG(1) << "System default app(s) are disabled";
+
+  params.disable_media_store_maintenance =
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          chromeos::switches::kArcDisableMediaStoreMaintenance);
+  if (params.disable_media_store_maintenance)
+    VLOG(1) << "MediaStore maintenance task(s) are disabled";
 
   VLOG(1) << "Starting ARC mini instance with lcd_density="
           << params.lcd_density
diff --git a/components/arc/session/arc_start_params.h b/components/arc/session/arc_start_params.h
index e906422..5e5889b3 100644
--- a/components/arc/session/arc_start_params.h
+++ b/components/arc/session/arc_start_params.h
@@ -56,6 +56,9 @@
   // Flag to disable system default apps.
   bool arc_disable_system_default_app = false;
 
+  // Flag to disable scheduling of media store periodic maintenance tasks.
+  bool disable_media_store_maintenance = false;
+
   // The number of logical CPU cores that are currently disabled on the host.
   // This parameter is used only for starting ARCVM.
   uint32_t num_cores_disabled = 0;
diff --git a/components/arc/session/arc_vm_client_adapter.cc b/components/arc/session/arc_vm_client_adapter.cc
index 5ee39ee..0b62850 100644
--- a/components/arc/session/arc_vm_client_adapter.cc
+++ b/components/arc/session/arc_vm_client_adapter.cc
@@ -238,6 +238,10 @@
   // TODO(niwa): Check if we need to set ro.boot.enable_adb_sideloading for
   // ARCVM.
 
+  // Only add boot property if flag to disable media store maintenance is set.
+  if (start_params.disable_media_store_maintenance)
+    result.push_back("androidboot.disable_media_store_maintenance=1");
+
   // Conditionally sets some properties based on |start_params|.
   switch (start_params.play_store_auto_update) {
     case StartParams::PlayStoreAutoUpdate::AUTO_UPDATE_DEFAULT:
diff --git a/components/arc/session/arc_vm_client_adapter_unittest.cc b/components/arc/session/arc_vm_client_adapter_unittest.cc
index 9e4639e6..742b85a8 100644
--- a/components/arc/session/arc_vm_client_adapter_unittest.cc
+++ b/components/arc/session/arc_vm_client_adapter_unittest.cc
@@ -1226,6 +1226,20 @@
                      "androidboot.disable_system_default_app=1"));
 }
 
+TEST_F(ArcVmClientAdapterTest, StartUpgradeArc_DisableMediaStoreMaintenance) {
+  StartParams start_params(GetPopulatedStartParams());
+  start_params.disable_media_store_maintenance = true;
+  SetValidUserInfo();
+  StartMiniArcWithParams(true, std::move(start_params));
+  UpgradeParams params(GetPopulatedUpgradeParams());
+  UpgradeArcWithParams(true, std::move(params));
+  EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
+  EXPECT_FALSE(arc_instance_stopped_called());
+  EXPECT_TRUE(
+      base::Contains(GetTestConciergeClient()->start_arc_vm_request().params(),
+                     "androidboot.disable_media_store_maintenance=1"));
+}
+
 // Tests that StartArcVm() is called with valid parameters.
 TEST_F(ArcVmClientAdapterTest, StartMiniArc_StartArcVmParams) {
   SetValidUserInfo();
diff --git a/components/autofill/core/browser/autofill_download_manager_unittest.cc b/components/autofill/core/browser/autofill_download_manager_unittest.cc
index abfd223..f939418 100644
--- a/components/autofill/core/browser/autofill_download_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_download_manager_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/base64url.h"
 #include "base/bind.h"
 #include "base/format_macros.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -84,23 +85,14 @@
 // the response body data is utilized.
 std::string GetStringFromDataElements(
     const std::vector<network::DataElement>* data_elements) {
-  network::DataElement unified_data_element;
-  auto data_elements_it = data_elements->begin();
-  if (data_elements_it != data_elements->end()) {
-    unified_data_element.SetToBytes(data_elements_it->bytes(),
-                                    data_elements_it->length());
+  std::string result;
+  for (const network::DataElement& e : *data_elements) {
+    DCHECK_EQ(e.type(), network::mojom::DataElementType::kBytes);
+    // Provide the length of the bytes explicitly, not to rely on the null
+    // termination.
+    result.append(e.bytes(), base::checked_cast<size_t>(e.length()));
   }
-  ++data_elements_it;
-  while (data_elements_it != data_elements->end()) {
-    unified_data_element.AppendBytes(data_elements_it->bytes(),
-                                     data_elements_it->length());
-    ++data_elements_it;
-  }
-  // Using the std::string constructor with length ensures that we don't rely
-  // on having a termination character to delimit the string. This is the
-  // safest approach.
-  return std::string(unified_data_element.bytes(),
-                     unified_data_element.length());
+  return result;
 }
 
 // Gets the AutofillUploadRequest proto from the HTTP loader request payload.
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/AlwaysDismissedDialog.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/AlwaysDismissedDialog.java
index b99ce4c..68cbd01 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/AlwaysDismissedDialog.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/AlwaysDismissedDialog.java
@@ -9,7 +9,6 @@
 
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
-import org.chromium.base.Log;
 
 /**
  * Dialog subclass that ensures that dismiss() is called, even if the dialog is implicitly dismissed
@@ -19,7 +18,6 @@
  */
 public class AlwaysDismissedDialog
         extends Dialog implements ApplicationStatus.ActivityStateListener {
-    private static final String TAG = "AlwaysDisDialog";
     public AlwaysDismissedDialog(Activity ownerActivity, int theme) {
         super(ownerActivity, theme);
         ApplicationStatus.registerStateListenerForActivity(this, ownerActivity);
@@ -35,10 +33,6 @@
 
     @Override
     public void onActivityStateChange(Activity activity, int newState) {
-        if (newState == ActivityState.DESTROYED) {
-            Thread.dumpStack();
-            Log.i(TAG, "Activity " + activity + " is destroyed. Dismissing dialog " + this);
-            dismiss();
-        }
+        if (newState == ActivityState.DESTROYED) dismiss();
     }
 }
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialog.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialog.java
index c79803cb..6ddd7c43 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialog.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialog.java
@@ -20,7 +20,6 @@
 import android.widget.FrameLayout;
 
 import org.chromium.base.ContextUtils;
-import org.chromium.base.Log;
 import org.chromium.components.browser_ui.widget.animation.Interpolators;
 import org.chromium.ui.widget.AnchoredPopupWindow;
 import org.chromium.ui.widget.RectProvider;
@@ -32,7 +31,6 @@
 public class ContextMenuDialog extends AlwaysDismissedDialog {
     public static final int NO_CUSTOM_MARGIN = -1;
 
-    private static final String TAG = "ContextMenuDialog";
     private static final long ENTER_ANIMATION_DURATION_MS = 250;
     // Exit animation duration should be set to 60% of the enter animation duration.
     private static final long EXIT_ANIMATION_DURATION_MS = 150;
@@ -159,9 +157,6 @@
             return;
         }
 
-        Thread.dumpStack();
-        Log.i(TAG, "Dialog " + this + " is being dismissed.");
-
         int[] contextMenuFinalLocationPx = new int[2];
         mContentView.getLocationOnScreen(contextMenuFinalLocationPx);
         // Recalculate mContextMenuDestinationY because the context menu's final location may not be
@@ -180,7 +175,6 @@
 
             @Override
             public void onAnimationEnd(Animation animation) {
-                Log.i(TAG, "Dismiss animation just ended for " + this);
                 ContextMenuDialog.super.dismiss();
             }
         });
diff --git a/components/component_updater/installer_policies/BUILD.gn b/components/component_updater/installer_policies/BUILD.gn
index 1e0d8eab..92a11b9f 100644
--- a/components/component_updater/installer_policies/BUILD.gn
+++ b/components/component_updater/installer_policies/BUILD.gn
@@ -6,6 +6,8 @@
   sources = [
     "on_device_head_suggest_component_installer.cc",
     "on_device_head_suggest_component_installer.h",
+    "optimization_hints_component_installer.cc",
+    "optimization_hints_component_installer.h",
     "safety_tips_component_installer.cc",
     "safety_tips_component_installer.h",
   ]
@@ -15,7 +17,23 @@
     "//components/component_updater",
     "//components/omnibox/browser",
     "//components/omnibox/common",
+    "//components/optimization_guide/core",
     "//components/reputation/core",
     "//components/reputation/core:proto",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [ "optimization_hints_component_installer_unittest.cc" ]
+
+  deps = [
+    ":installer_policies",
+    "//base",
+    "//base/test:test_support",
+    "//components/component_updater:test_support",
+    "//components/optimization_guide/core",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/component_updater/installer_policies/DEPS b/components/component_updater/installer_policies/DEPS
index 188a1ba..774802a 100644
--- a/components/component_updater/installer_policies/DEPS
+++ b/components/component_updater/installer_policies/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+components/omnibox/browser",
   "+components/omnibox/common",
+  "+components/optimization_guide/core",
   "+components/reputation/core",
 ]
diff --git a/chrome/browser/component_updater/optimization_hints_component_installer.cc b/components/component_updater/installer_policies/optimization_hints_component_installer.cc
similarity index 85%
rename from chrome/browser/component_updater/optimization_hints_component_installer.cc
rename to components/component_updater/installer_policies/optimization_hints_component_installer.cc
index b8cd86ea..cc3bcb8 100644
--- a/chrome/browser/component_updater/optimization_hints_component_installer.cc
+++ b/components/component_updater/installer_policies/optimization_hints_component_installer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/component_updater/optimization_hints_component_installer.h"
+#include "components/component_updater/installer_policies/optimization_hints_component_installer.h"
 
 #include <utility>
 
@@ -12,12 +12,10 @@
 #include "base/path_service.h"
 #include "base/task/post_task.h"
 #include "base/version.h"
-#include "chrome/browser/browser_process.h"
 #include "components/component_updater/component_updater_paths.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
 #include "components/optimization_guide/core/optimization_guide_constants.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/optimization_guide_service.h"
+#include "components/optimization_guide/core/optimization_hints_component_update_listener.h"
 
 using component_updater::ComponentUpdateService;
 
@@ -88,15 +86,15 @@
     DVLOG(1) << "Got incompatible ruleset_format. Bailing out.";
     return;
   }
-  optimization_guide::OptimizationGuideService* optimization_guide_service =
-      g_browser_process->optimization_guide_service();
-  if (optimization_guide_service &&
-      !base::CommandLine::ForCurrentProcess()->HasSwitch(
-          kDisableInstallerUpdate)) {
+  optimization_guide::OptimizationHintsComponentUpdateListener*
+      update_listener = optimization_guide::
+          OptimizationHintsComponentUpdateListener::GetInstance();
+  if (update_listener && !base::CommandLine::ForCurrentProcess()->HasSwitch(
+                             kDisableInstallerUpdate)) {
     optimization_guide::HintsComponentInfo info(
         version,
         install_dir.Append(optimization_guide::kUnindexedHintsFileName));
-    optimization_guide_service->MaybeUpdateHintsComponent(info);
+    update_listener->MaybeUpdateHintsComponent(info);
   }
 }
 
@@ -135,15 +133,9 @@
   return std::vector<std::string>();
 }
 
-void RegisterOptimizationHintsComponent(ComponentUpdateService* cus,
-                                        bool is_off_the_record_profile) {
-  if (is_off_the_record_profile) {
+void RegisterOptimizationHintsComponent(ComponentUpdateService* cus) {
+  if (!optimization_guide::features::IsOptimizationHintsEnabled())
     return;
-  }
-
-  if (!optimization_guide::features::IsOptimizationHintsEnabled()) {
-    return;
-  }
 
   auto installer = base::MakeRefCounted<ComponentInstaller>(
       std::make_unique<OptimizationHintsComponentInstallerPolicy>());
diff --git a/chrome/browser/component_updater/optimization_hints_component_installer.h b/components/component_updater/installer_policies/optimization_hints_component_installer.h
similarity index 85%
rename from chrome/browser/component_updater/optimization_hints_component_installer.h
rename to components/component_updater/installer_policies/optimization_hints_component_installer.h
index 38a3566..b1f78ea 100644
--- a/chrome/browser/component_updater/optimization_hints_component_installer.h
+++ b/components/component_updater/installer_policies/optimization_hints_component_installer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_COMPONENT_UPDATER_OPTIMIZATION_HINTS_COMPONENT_INSTALLER_H_
-#define CHROME_BROWSER_COMPONENT_UPDATER_OPTIMIZATION_HINTS_COMPONENT_INSTALLER_H_
+#ifndef COMPONENTS_COMPONENT_UPDATER_INSTALLER_POLICIES_OPTIMIZATION_HINTS_COMPONENT_INSTALLER_H_
+#define COMPONENTS_COMPONENT_UPDATER_INSTALLER_POLICIES_OPTIMIZATION_HINTS_COMPONENT_INSTALLER_H_
 
 #include <memory>
 #include <string>
@@ -56,9 +56,8 @@
   DISALLOW_COPY_AND_ASSIGN(OptimizationHintsComponentInstallerPolicy);
 };
 
-void RegisterOptimizationHintsComponent(ComponentUpdateService* cus,
-                                        bool is_off_the_record_profile);
+void RegisterOptimizationHintsComponent(ComponentUpdateService* cus);
 
 }  // namespace component_updater
 
-#endif  // CHROME_BROWSER_COMPONENT_UPDATER_OPTIMIZATION_HINTS_COMPONENT_INSTALLER_H_
+#endif  // COMPONENTS_COMPONENT_UPDATER_INSTALLER_POLICIES_OPTIMIZATION_HINTS_COMPONENT_INSTALLER_H_
diff --git a/chrome/browser/component_updater/optimization_hints_component_installer_unittest.cc b/components/component_updater/installer_policies/optimization_hints_component_installer_unittest.cc
similarity index 64%
rename from chrome/browser/component_updater/optimization_hints_component_installer_unittest.cc
rename to components/component_updater/installer_policies/optimization_hints_component_installer_unittest.cc
index 6d9d5fd..6538af9 100644
--- a/chrome/browser/component_updater/optimization_hints_component_installer_unittest.cc
+++ b/components/component_updater/installer_policies/optimization_hints_component_installer_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/component_updater/optimization_hints_component_installer.h"
+#include "components/component_updater/installer_policies/optimization_hints_component_installer.h"
 
 #include <utility>
 
@@ -16,14 +16,10 @@
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/version.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_browser_process.h"
 #include "components/component_updater/mock_component_updater_service.h"
 #include "components/optimization_guide/core/optimization_guide_constants.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/optimization_guide_service.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/testing_pref_service.h"
+#include "components/optimization_guide/core/optimization_hints_component_update_listener.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
@@ -31,30 +27,6 @@
 
 static const char kTestHintsVersion[] = "1.2.3";
 
-class TestOptimizationGuideService
-    : public optimization_guide::OptimizationGuideService {
- public:
-  explicit TestOptimizationGuideService(
-      scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner)
-      : optimization_guide::OptimizationGuideService(io_thread_task_runner) {}
-  ~TestOptimizationGuideService() override = default;
-
-  void MaybeUpdateHintsComponent(
-      const optimization_guide::HintsComponentInfo& info) override {
-    hints_component_info_ =
-        std::make_unique<optimization_guide::HintsComponentInfo>(info);
-  }
-
-  optimization_guide::HintsComponentInfo* hints_component_info() const {
-    return hints_component_info_.get();
-  }
-
- private:
-  std::unique_ptr<optimization_guide::HintsComponentInfo> hints_component_info_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestOptimizationGuideService);
-};
-
 class OptimizationHintsMockComponentUpdateService
     : public component_updater::MockComponentUpdateService {
  public:
@@ -72,31 +44,16 @@
 class OptimizationHintsComponentInstallerTest : public PlatformTest {
  public:
   OptimizationHintsComponentInstallerTest() = default;
+  ~OptimizationHintsComponentInstallerTest() override = default;
 
   void SetUp() override {
     PlatformTest::SetUp();
 
     ASSERT_TRUE(component_install_dir_.CreateUniqueTempDir());
 
-    auto optimization_guide_service =
-        std::make_unique<TestOptimizationGuideService>(
-            base::ThreadTaskRunnerHandle::Get());
-    optimization_guide_service_ = optimization_guide_service.get();
-
-    TestingBrowserProcess::GetGlobal()->SetOptimizationGuideService(
-        std::move(optimization_guide_service));
     policy_ = std::make_unique<OptimizationHintsComponentInstallerPolicy>();
   }
 
-  void TearDown() override {
-    TestingBrowserProcess::GetGlobal()->SetOptimizationGuideService(nullptr);
-    PlatformTest::TearDown();
-  }
-
-  TestOptimizationGuideService* service() {
-    return optimization_guide_service_;
-  }
-
   base::FilePath component_install_dir() {
     return component_install_dir_.GetPath();
   }
@@ -136,12 +93,9 @@
   base::test::TaskEnvironment task_environment_;
 
   base::ScopedTempDir component_install_dir_;
-  std::unique_ptr<TestingPrefServiceSimple> pref_service_;
 
   std::unique_ptr<OptimizationHintsComponentInstallerPolicy> policy_;
 
-  TestOptimizationGuideService* optimization_guide_service_ = nullptr;
-
   DISALLOW_COPY_AND_ASSIGN(OptimizationHintsComponentInstallerTest);
 };
 
@@ -153,7 +107,7 @@
   std::unique_ptr<OptimizationHintsMockComponentUpdateService> cus(
       new OptimizationHintsMockComponentUpdateService());
   EXPECT_CALL(*cus, RegisterComponent(testing::_)).Times(0);
-  RegisterOptimizationHintsComponent(cus.get(), false);
+  RegisterOptimizationHintsComponent(cus.get());
   RunUntilIdle();
 }
 
@@ -167,32 +121,21 @@
   EXPECT_CALL(*cus, RegisterComponent(testing::_))
       .Times(1)
       .WillOnce(testing::Return(true));
-  RegisterOptimizationHintsComponent(cus.get(), false);
-  RunUntilIdle();
-}
-
-TEST_F(OptimizationHintsComponentInstallerTest,
-       ComponentRegistrationWhenFeatureEnabledButOffTheRecordProfile) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeature(
-      optimization_guide::features::kOptimizationHints);
-  std::unique_ptr<OptimizationHintsMockComponentUpdateService> cus(
-      new OptimizationHintsMockComponentUpdateService());
-  EXPECT_CALL(*cus, RegisterComponent(testing::_)).Times(0);
-  RegisterOptimizationHintsComponent(cus.get(), true);
+  RegisterOptimizationHintsComponent(cus.get());
   RunUntilIdle();
 }
 
 TEST_F(OptimizationHintsComponentInstallerTest, NoRulesetFormatIgnored) {
-  ASSERT_TRUE(service());
   ASSERT_NO_FATAL_FAILURE(CreateTestOptimizationHints("some hints"));
 
   ASSERT_NO_FATAL_FAILURE(LoadOptimizationHints(base::Version("")));
-  EXPECT_EQ(nullptr, service()->hints_component_info());
+  EXPECT_FALSE(optimization_guide::OptimizationHintsComponentUpdateListener::
+                   GetInstance()
+                       ->hints_component_info()
+                       .has_value());
 }
 
 TEST_F(OptimizationHintsComponentInstallerTest, FutureRulesetFormatIgnored) {
-  ASSERT_TRUE(service());
   ASSERT_NO_FATAL_FAILURE(CreateTestOptimizationHints("some hints"));
   base::Version version = ruleset_format_version();
   const std::vector<uint32_t> future_ruleset_components = {
@@ -201,18 +144,22 @@
 
   ASSERT_NO_FATAL_FAILURE(
       LoadOptimizationHints(base::Version(future_ruleset_components)));
-  EXPECT_EQ(nullptr, service()->hints_component_info());
+  EXPECT_FALSE(optimization_guide::OptimizationHintsComponentUpdateListener::
+                   GetInstance()
+                       ->hints_component_info()
+                       .has_value());
 }
 
 TEST_F(OptimizationHintsComponentInstallerTest, LoadFileWithData) {
-  ASSERT_TRUE(service());
-
   const std::string expected_hints = "some hints";
   ASSERT_NO_FATAL_FAILURE(CreateTestOptimizationHints(expected_hints));
   ASSERT_NO_FATAL_FAILURE(LoadOptimizationHints(ruleset_format_version()));
 
-  auto* component_info = service()->hints_component_info();
-  EXPECT_NE(nullptr, component_info);
+  base::Optional<optimization_guide::HintsComponentInfo> component_info =
+      optimization_guide::OptimizationHintsComponentUpdateListener::
+          GetInstance()
+              ->hints_component_info();
+  EXPECT_TRUE(component_info.has_value());
   EXPECT_EQ(base::Version(kTestHintsVersion), component_info->version);
   std::string actual_hints;
   ASSERT_TRUE(base::ReadFileToString(component_info->path, &actual_hints));
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
index bf7134d..74bd827 100644
--- a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
+++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
@@ -30,6 +30,7 @@
         FeatureConstants.KEYBOARD_ACCESSORY_BAR_SWIPING_FEATURE,
         FeatureConstants.KEYBOARD_ACCESSORY_PASSWORD_FILLING_FEATURE,
         FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_FILLING_FEATURE,
+        FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_OFFER_FEATURE,
         FeatureConstants.DOWNLOAD_SETTINGS_FEATURE,
         FeatureConstants.DOWNLOAD_INFOBAR_DOWNLOAD_CONTINUING_FEATURE,
         FeatureConstants.DOWNLOAD_INFOBAR_DOWNLOADS_ARE_FASTER_FEATURE,
@@ -60,6 +61,7 @@
     String KEYBOARD_ACCESSORY_ADDRESS_FILL_FEATURE = "IPH_KeyboardAccessoryAddressFilling";
     String KEYBOARD_ACCESSORY_PASSWORD_FILLING_FEATURE = "IPH_KeyboardAccessoryPasswordFilling";
     String KEYBOARD_ACCESSORY_PAYMENT_FILLING_FEATURE = "IPH_KeyboardAccessoryPaymentFilling";
+    String KEYBOARD_ACCESSORY_PAYMENT_OFFER_FEATURE = "IPH_KeyboardAccessoryPaymentOffer";
     String KEYBOARD_ACCESSORY_BAR_SWIPING_FEATURE = "IPH_KeyboardAccessoryBarSwiping";
     String PREVIEWS_OMNIBOX_UI_FEATURE = "IPH_PreviewsOmniboxUI";
     String TRANSLATE_MENU_BUTTON_FEATURE = "IPH_TranslateMenuButton";
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc
index fde7586..31f4000 100644
--- a/components/feature_engagement/public/feature_constants.cc
+++ b/components/feature_engagement/public/feature_constants.cc
@@ -99,6 +99,8 @@
     "IPH_KeyboardAccessoryPasswordFilling", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kIPHKeyboardAccessoryPaymentFillingFeature{
     "IPH_KeyboardAccessoryPaymentFilling", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kIPHKeyboardAccessoryPaymentOfferFeature{
+    "IPH_KeyboardAccessoryPaymentOffer", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kIPHNewTabPageHomeButtonFeature{
     "IPH_NewTabPageHomeButton", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kIPHPreviewsOmniboxUIFeature{
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h
index c6bc56f2..57f5e07 100644
--- a/components/feature_engagement/public/feature_constants.h
+++ b/components/feature_engagement/public/feature_constants.h
@@ -62,6 +62,7 @@
 extern const base::Feature kIPHKeyboardAccessoryBarSwipingFeature;
 extern const base::Feature kIPHKeyboardAccessoryPasswordFillingFeature;
 extern const base::Feature kIPHKeyboardAccessoryPaymentFillingFeature;
+extern const base::Feature kIPHKeyboardAccessoryPaymentOfferFeature;
 extern const base::Feature kIPHNewTabPageHomeButtonFeature;
 extern const base::Feature kIPHPreviewsOmniboxUIFeature;
 extern const base::Feature kIPHQuietNotificationPromptsFeature;
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc
index 7b2f29d..ffd6921 100644
--- a/components/feature_engagement/public/feature_list.cc
+++ b/components/feature_engagement/public/feature_list.cc
@@ -45,6 +45,7 @@
     &kIPHKeyboardAccessoryBarSwipingFeature,
     &kIPHKeyboardAccessoryPasswordFillingFeature,
     &kIPHKeyboardAccessoryPaymentFillingFeature,
+    &kIPHKeyboardAccessoryPaymentOfferFeature,
     &kIPHNewTabPageHomeButtonFeature,
     &kIPHPreviewsOmniboxUIFeature,
     &kIPHPwaInstallAvailableFeature,
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h
index 38fb616..aa5dd8f 100644
--- a/components/feature_engagement/public/feature_list.h
+++ b/components/feature_engagement/public/feature_list.h
@@ -93,6 +93,8 @@
                        "IPH_KeyboardAccessoryPasswordFilling");
 DEFINE_VARIATION_PARAM(kIPHKeyboardAccessoryPaymentFillingFeature,
                        "IPH_KeyboardAccessoryPaymentFilling");
+DEFINE_VARIATION_PARAM(kIPHKeyboardAccessoryPaymentOfferFeature,
+                       "IPH_KeyboardAccessoryPaymentOffer");
 DEFINE_VARIATION_PARAM(kIPHNewTabPageButtonFeature, "IPH_NewTabPageHomeButton");
 DEFINE_VARIATION_PARAM(kIPHPreviewsOmniboxUIFeature, "IPH_PreviewsOmniboxUI");
 DEFINE_VARIATION_PARAM(kIPHPwaInstallAvailableFeature,
@@ -196,6 +198,7 @@
         VARIATION_ENTRY(kIPHKeyboardAccessoryBarSwipingFeature),
         VARIATION_ENTRY(kIPHKeyboardAccessoryPasswordFillingFeature),
         VARIATION_ENTRY(kIPHKeyboardAccessoryPaymentFillingFeature),
+        VARIATION_ENTRY(kIPHKeyboardAccessoryPaymentOfferFeature),
         VARIATION_ENTRY(kIPHNewTabPageButtonFeature),
         VARIATION_ENTRY(kIPHPreviewsOmniboxUIFeature),
         VARIATION_ENTRY(kIPHPwaInstallAvailableFeature),
diff --git a/components/feed/core/proto/BUILD.gn b/components/feed/core/proto/BUILD.gn
index 9244b9ae..3221e2c 100644
--- a/components/feed/core/proto/BUILD.gn
+++ b/components/feed/core/proto/BUILD.gn
@@ -23,6 +23,7 @@
     #UNUSED_IN_CHROME "v2/wire/in_place_update_handle.proto",
     #UNUSED_IN_CHROME "v2/wire/response_status_code.proto",
     #UNUSED_IN_CHROME "v2/wire/templates.proto",
+    "v2/keyvalue_store.proto",
     "v2/packing.proto",
     "v2/store.proto",
     "v2/ui.proto",
diff --git a/components/feed/core/proto/v2/keyvalue_store.proto b/components/feed/core/proto/v2/keyvalue_store.proto
new file mode 100644
index 0000000..7bdfbe4
--- /dev/null
+++ b/components/feed/core/proto/v2/keyvalue_store.proto
@@ -0,0 +1,15 @@
+// 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.
+
+syntax = "proto3";
+
+package feedkvstore;
+
+option optimize_for = LITE_RUNTIME;
+
+message Entry {
+  bytes value = 1;
+  // Unix timetamp in milliseconds.
+  int64 modification_time = 2;
+}
diff --git a/components/feed/core/v2/BUILD.gn b/components/feed/core/v2/BUILD.gn
index 9fb5329a..d8bde6d 100644
--- a/components/feed/core/v2/BUILD.gn
+++ b/components/feed/core/v2/BUILD.gn
@@ -31,6 +31,8 @@
     "notice_card_tracker.h",
     "offline_page_spy.cc",
     "offline_page_spy.h",
+    "persistent_key_value_store_impl.cc",
+    "persistent_key_value_store_impl.h",
     "prefs.cc",
     "prefs.h",
     "proto_util.cc",
@@ -41,6 +43,8 @@
     "public/feed_service.h",
     "public/feed_stream_api.cc",
     "public/feed_stream_api.h",
+    "public/persistent_key_value_store.cc",
+    "public/persistent_key_value_store.h",
     "public/types.h",
     "refresh_task_scheduler.h",
     "request_throttler.cc",
@@ -117,12 +121,14 @@
     "image_fetcher_unittest.cc",
     "metrics_reporter_unittest.cc",
     "notice_card_tracker_unittest.cc",
+    "persistent_key_value_store_impl_unittest.cc",
     "proto_util_unittest.cc",
     "protocol_translator_unittest.cc",
     "public/feed_service_unittest.cc",
     "request_throttler_unittest.cc",
     "scheduling_unittest.cc",
     "stream_model_unittest.cc",
+    "test/callback_receiver.cc",
     "test/callback_receiver.h",
     "test/callback_receiver_unittest.cc",
     "test/proto_printer.cc",
diff --git a/components/feed/core/v2/config.h b/components/feed/core/v2/config.h
index 1eb0d61..025b287 100644
--- a/components/feed/core/v2/config.h
+++ b/components/feed/core/v2/config.h
@@ -44,6 +44,14 @@
   base::TimeDelta session_id_max_age = base::TimeDelta::FromDays(30);
   // Maximum number of images prefetched per refresh.
   int max_prefetch_image_requests_per_refresh = 50;
+
+  // Configuration for `PersistentKeyValueStore`.
+
+  // Maximum total database size before items are evicted.
+  int64_t persistent_kv_store_maximum_size_before_eviction = 1000000;
+  // Eviction task is performed after this many bytes are written.
+  int persistent_kv_store_cleanup_interval_in_written_bytes = 1000000;
+
   // Set of optional capabilities included in requests. See
   // CreateFeedQueryRequest() for required capabilities.
   base::flat_set<feedwire::Capability> experimental_capabilities = {
diff --git a/components/feed/core/v2/feed_stream.cc b/components/feed/core/v2/feed_stream.cc
index 15e3d41..cc73904 100644
--- a/components/feed/core/v2/feed_stream.cc
+++ b/components/feed/core/v2/feed_stream.cc
@@ -168,6 +168,7 @@
                        FeedNetwork* feed_network,
                        ImageFetcher* image_fetcher,
                        FeedStore* feed_store,
+                       PersistentKeyValueStoreImpl* persistent_key_value_store,
                        offline_pages::PrefetchService* prefetch_service,
                        offline_pages::OfflinePageModel* offline_page_model,
                        const ChromeInfo& chrome_info)
@@ -179,6 +180,7 @@
       feed_network_(feed_network),
       image_fetcher_(image_fetcher),
       store_(feed_store),
+      persistent_key_value_store_(persistent_key_value_store),
       chrome_info_(chrome_info),
       task_queue_(this),
       request_throttler_(profile_prefs),
@@ -727,6 +729,10 @@
   return image_fetcher_->Fetch(url, std::move(callback));
 }
 
+PersistentKeyValueStoreImpl* FeedStream::GetPersistentKeyValueStore() {
+  return persistent_key_value_store_;
+}
+
 void FeedStream::CancelImageFetch(ImageFetchId id) {
   image_fetcher_->Cancel(id);
 }
diff --git a/components/feed/core/v2/feed_stream.h b/components/feed/core/v2/feed_stream.h
index 1a269812..e40d7a9 100644
--- a/components/feed/core/v2/feed_stream.h
+++ b/components/feed/core/v2/feed_stream.h
@@ -20,6 +20,7 @@
 #include "components/feed/core/proto/v2/wire/response.pb.h"
 #include "components/feed/core/v2/enums.h"
 #include "components/feed/core/v2/notice_card_tracker.h"
+#include "components/feed/core/v2/persistent_key_value_store_impl.h"
 #include "components/feed/core/v2/protocol_translator.h"
 #include "components/feed/core/v2/public/feed_stream_api.h"
 #include "components/feed/core/v2/request_throttler.h"
@@ -44,6 +45,7 @@
 class MetricsReporter;
 class OfflinePageSpy;
 class RefreshTaskScheduler;
+class PersistentKeyValueStoreImpl;
 class StreamModel;
 class SurfaceUpdater;
 struct StreamModelUpdateRequest;
@@ -110,6 +112,7 @@
              FeedNetwork* feed_network,
              ImageFetcher* image_fetcher,
              FeedStore* feed_store,
+             PersistentKeyValueStoreImpl* persistent_key_value_store,
              offline_pages::PrefetchService* prefetch_service,
              offline_pages::OfflinePageModel* offline_page_model,
              const ChromeInfo& chrome_info);
@@ -135,6 +138,7 @@
       const GURL& url,
       base::OnceCallback<void(NetworkResponse)> callback) override;
   void CancelImageFetch(ImageFetchId id) override;
+  PersistentKeyValueStoreImpl* GetPersistentKeyValueStore() override;
   void LoadMore(SurfaceId surface_id,
                 base::OnceCallback<void(bool)> callback) override;
   void ExecuteOperations(
@@ -327,6 +331,7 @@
   FeedNetwork* feed_network_;
   ImageFetcher* image_fetcher_;
   FeedStore* store_;
+  PersistentKeyValueStoreImpl* persistent_key_value_store_;
   const WireResponseTranslator* wire_response_translator_;
 
   ChromeInfo chrome_info_;
diff --git a/components/feed/core/v2/feed_stream_unittest.cc b/components/feed/core/v2/feed_stream_unittest.cc
index 78cbb82..ccb9683a 100644
--- a/components/feed/core/v2/feed_stream_unittest.cc
+++ b/components/feed/core/v2/feed_stream_unittest.cc
@@ -26,6 +26,7 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/clock.h"
 #include "components/feed/core/common/pref_names.h"
+#include "components/feed/core/proto/v2/keyvalue_store.pb.h"
 #include "components/feed/core/proto/v2/store.pb.h"
 #include "components/feed/core/proto/v2/ui.pb.h"
 #include "components/feed/core/proto/v2/wire/chrome_client_info.pb.h"
@@ -37,8 +38,10 @@
 #include "components/feed/core/v2/feed_network.h"
 #include "components/feed/core/v2/image_fetcher.h"
 #include "components/feed/core/v2/metrics_reporter.h"
+#include "components/feed/core/v2/persistent_key_value_store_impl.h"
 #include "components/feed/core/v2/prefs.h"
 #include "components/feed/core/v2/protocol_translator.h"
+#include "components/feed/core/v2/public/persistent_key_value_store.h"
 #include "components/feed/core/v2/refresh_task_scheduler.h"
 #include "components/feed/core/v2/scheduling.h"
 #include "components/feed/core/v2/stream_model.h"
@@ -593,8 +596,9 @@
     // Ensure the task queue can return to idle. Failure to do so may be due
     // to a stuck task that never called |TaskComplete()|.
     WaitForIdleTaskQueue();
-    // Store requires PostTask to clean up.
+    // ProtoDatabase requires PostTask to clean up.
     store_.reset();
+    persistent_key_value_store_.reset();
     task_environment_.RunUntilIdle();
   }
 
@@ -625,7 +629,8 @@
     chrome_info.version = base::Version({99, 1, 9911, 2});
     stream_ = std::make_unique<FeedStream>(
         &refresh_scheduler_, metrics_reporter_.get(), this, &profile_prefs_,
-        &network_, image_fetcher_.get(), store_.get(), &prefetch_service_,
+        &network_, image_fetcher_.get(), store_.get(),
+        persistent_key_value_store_.get(), &prefetch_service_,
         &offline_page_model_, chrome_info);
 
     WaitForIdleTaskQueue();  // Wait for any initialization.
@@ -694,8 +699,16 @@
   std::unique_ptr<FeedStore> store_ = std::make_unique<FeedStore>(
       leveldb_proto::ProtoDatabaseProvider::GetUniqueDB<feedstore::Record>(
           leveldb_proto::ProtoDbType::FEED_STREAM_DATABASE,
-          /*file_path=*/{},
+          /*db_dir=*/{},
           task_environment_.GetMainThreadTaskRunner()));
+
+  std::unique_ptr<PersistentKeyValueStoreImpl> persistent_key_value_store_ =
+      std::make_unique<PersistentKeyValueStoreImpl>(
+          leveldb_proto::ProtoDatabaseProvider::GetUniqueDB<feedkvstore::Entry>(
+              leveldb_proto::ProtoDbType::FEED_KEY_VALUE_DATABASE,
+              /*db_dir=*/{},
+              task_environment_.GetMainThreadTaskRunner()));
+
   FakeRefreshTaskScheduler refresh_scheduler_;
   TestPrefetchService prefetch_service_;
   TestOfflinePageModel offline_page_model_;
@@ -2789,5 +2802,22 @@
   EXPECT_TIME_EQ(kExpiryTime, stream_->GetMetadata()->GetSessionIdExpiryTime());
 }
 
+TEST_F(FeedStreamTest, PersistentKeyValueStoreIsClearedOnClearAll) {
+  // Store some data and verify it exists.
+  PersistentKeyValueStore* store = stream_->GetPersistentKeyValueStore();
+  store->Put("x", "y", base::DoNothing());
+  CallbackReceiver<PersistentKeyValueStore::Result> get_result;
+  store->Get("x", get_result.Bind());
+  ASSERT_EQ("y", *get_result.RunAndGetResult().get_result);
+
+  stream_->OnCacheDataCleared();  // triggers ClearAll().
+  WaitForIdleTaskQueue();
+
+  // Verify ClearAll() deleted the data.
+  get_result.Clear();
+  store->Get("x", get_result.Bind());
+  EXPECT_FALSE(get_result.RunAndGetResult().get_result);
+}
+
 }  // namespace
 }  // namespace feed
diff --git a/components/feed/core/v2/persistent_key_value_store_impl.cc b/components/feed/core/v2/persistent_key_value_store_impl.cc
new file mode 100644
index 0000000..7be37daf
--- /dev/null
+++ b/components/feed/core/v2/persistent_key_value_store_impl.cc
@@ -0,0 +1,325 @@
+// 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 "components/feed/core/v2/persistent_key_value_store_impl.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/rand_util.h"
+#include "base/time/time.h"
+#include "components/feed/core/proto/v2/keyvalue_store.pb.h"
+#include "components/feed/core/v2/config.h"
+#include "components/offline_pages/task/task.h"
+
+namespace feed {
+namespace {
+using ::feed::internal::kMaxEntriesInMemory;
+using feedkvstore::Entry;
+}  // namespace
+
+// Eviction task functionality.
+class EvictTask {
+ public:
+  static void Start(base::WeakPtr<PersistentKeyValueStoreImpl> store,
+                    base::OnceCallback<void(bool)> done_callback) {
+    auto state = std::make_unique<State>();
+    state->store = store;
+    state->done_callback = std::move(done_callback);
+
+    auto* db = GetDbOrFinish(state);
+    if (!db)
+      return;
+    db->LoadKeys(base::BindOnce(&EvictTask::LoadKeysDone, std::move(state)));
+  }
+
+ private:
+  struct EntryMetadata {
+    std::string key;
+    int64_t size_bytes;
+    int64_t modification_time;
+  };
+
+  struct State {
+    base::WeakPtr<PersistentKeyValueStoreImpl> store;
+    base::OnceCallback<void(bool)> done_callback;
+
+    std::vector<std::string> all_keys;
+    size_t next_key_index = 0;
+    std::vector<EntryMetadata> metadata;
+  };
+
+  static void Finish(std::unique_ptr<State> state) {
+    std::move(state->done_callback).Run(true);
+  }
+
+  static leveldb_proto::ProtoDatabase<Entry>* GetDbOrFinish(
+      std::unique_ptr<State>& state) {
+    if (state->store) {
+      return state->store->GetDatabase();
+    }
+    Finish(std::move(state));
+    return nullptr;
+  }
+
+  static void IndexMore(std::unique_ptr<State> state) {
+    auto* db = GetDbOrFinish(state);
+    if (!db)
+      return;
+    if (state->next_key_index >= state->all_keys.size()) {
+      IndexingDone(std::move(state));
+      return;
+    }
+    const size_t first_index = state->next_key_index;
+    std::string last_key;
+    state->next_key_index = std::min(
+        state->next_key_index + kMaxEntriesInMemory, state->all_keys.size());
+
+    std::string lower_bound = state->all_keys[first_index],
+                upper_bound = state->all_keys[state->next_key_index - 1];
+    db->LoadKeysAndEntriesInRange(
+        lower_bound, upper_bound,
+        base::BindOnce(&EvictTask::IndexMore_LoadChunkDone, std::move(state)));
+  }
+
+  static void IndexMore_LoadChunkDone(
+      std::unique_ptr<State> state,
+      bool ok,
+      std::unique_ptr<std::map<std::string, feedkvstore::Entry>> entries) {
+    const int64_t now =
+        base::Time::Now().ToDeltaSinceWindowsEpoch().InMilliseconds();
+    for (auto& entry : *entries) {
+      EntryMetadata m;
+      m.key = entry.first;
+      m.modification_time = entry.second.modification_time();
+      // If modification time is in the future, assume the information is out
+      // of date.
+      if (m.modification_time > now)
+        m.modification_time = 0;
+      m.size_bytes = entry.second.value().size();
+      state->metadata.push_back(m);
+    }
+    IndexMore(std::move(state));
+  }
+
+  static void LoadKeysDone(std::unique_ptr<State> state,
+                           bool ok,
+                           std::unique_ptr<std::vector<std::string>> keys) {
+    if (!ok || !keys) {
+      Finish(std::move(state));
+      return;
+    }
+    state->all_keys = std::move(*keys);
+    IndexMore(std::move(state));
+  }
+
+  static void IndexingDone(std::unique_ptr<State> state) {
+    auto* db = GetDbOrFinish(state);
+    if (!db)
+      return;
+    std::sort(state->metadata.begin(), state->metadata.end(),
+              [&](const EntryMetadata& a, const EntryMetadata& b) {
+                return a.modification_time > b.modification_time;
+              });
+
+    size_t i = 0;
+    int64_t total_size = 0;
+    const int64_t max_db_size_bytes =
+        GetFeedConfig().persistent_kv_store_maximum_size_before_eviction;
+    for (; i < state->metadata.size(); ++i) {
+      total_size += state->metadata[i].size_bytes;
+      if (total_size > max_db_size_bytes) {
+        break;
+      }
+    }
+
+    auto keys_to_remove = std::make_unique<std::vector<std::string>>();
+    for (; i < state->metadata.size(); ++i) {
+      keys_to_remove->push_back(state->metadata[i].key);
+    }
+
+    db->UpdateEntries(
+        std::make_unique<std::vector<std::pair<std::string, Entry>>>(),
+        std::move(keys_to_remove),
+        base::BindOnce([](std::unique_ptr<State> state,
+                          bool ok) { Finish(std::move(state)); },
+                       std::move(state)));
+  }
+};
+
+PersistentKeyValueStoreImpl::Task::Task() = default;
+PersistentKeyValueStoreImpl::Task::Task(TaskType task_type,
+                                        ResultCallback callback)
+    : Task(task_type, std::string(), std::move(callback)) {}
+PersistentKeyValueStoreImpl::Task::Task(TaskType task_type,
+                                        std::string key,
+                                        ResultCallback callback)
+    : type(task_type), key(key), done_callback(std::move(callback)) {}
+PersistentKeyValueStoreImpl::Task::Task(Task&&) noexcept = default;
+PersistentKeyValueStoreImpl::Task::~Task() = default;
+
+PersistentKeyValueStoreImpl::PersistentKeyValueStoreImpl(
+    std::unique_ptr<leveldb_proto::ProtoDatabase<feedkvstore::Entry>> database)
+    : database_(std::move(database)) {}
+
+PersistentKeyValueStoreImpl::~PersistentKeyValueStoreImpl() = default;
+
+void PersistentKeyValueStoreImpl::OnDatabaseInitialized(
+    leveldb_proto::Enums::InitStatus status) {
+  database_status_ = status;
+  TaskComplete({}, {});
+}
+
+bool PersistentKeyValueStoreImpl::IsInitialized() const {
+  return database_status_ == leveldb_proto::Enums::InitStatus::kOK;
+}
+
+void PersistentKeyValueStoreImpl::AddTask(Task task) {
+  if (!triggered_initialize_) {
+    triggered_initialize_ = true;
+    running_task_ = true;
+    database_->Init(base::BindOnce(
+        &PersistentKeyValueStoreImpl::OnDatabaseInitialized, GetWeakPtr()));
+  }
+  if (!running_task_) {
+    StartTask(std::move(task));
+  } else {
+    queued_tasks_.push(std::move(task));
+  }
+}
+
+void PersistentKeyValueStoreImpl::ClearAll(ResultCallback callback) {
+  AddTask({TaskType::kClearAll, std::move(callback)});
+}
+
+void PersistentKeyValueStoreImpl::Put(const std::string& key,
+                                      const std::string& value,
+                                      ResultCallback callback) {
+  // Use a random number to trigger EvictOldEntries().
+  // The expected number of calls to EvictOldEntries() is =~
+  // (sum of bytes written) / `cleanup_interval_in_written_bytes`.
+  int cleanup_interval_in_written_bytes =
+      GetFeedConfig().persistent_kv_store_cleanup_interval_in_written_bytes;
+  int rand_int = base::RandInt(0, cleanup_interval_in_written_bytes);
+  if (cleanup_interval_in_written_bytes > 0 &&
+      rand_int < static_cast<int>(value.size())) {
+    EvictOldEntries(base::DoNothing());
+  }
+  Task task(TaskType::kPut, key, std::move(callback));
+  task.put_value = value;
+  AddTask(std::move(task));
+}
+
+void PersistentKeyValueStoreImpl::Get(const std::string& key,
+                                      ResultCallback callback) {
+  AddTask({TaskType::kGet, key, std::move(callback)});
+}
+
+void PersistentKeyValueStoreImpl::Delete(const std::string& key,
+                                         ResultCallback callback) {
+  AddTask({TaskType::kDelete, key, std::move(callback)});
+}
+
+void PersistentKeyValueStoreImpl::EvictOldEntries(ResultCallback callback) {
+  AddTask({TaskType::kEvictOldEntries, std::move(callback)});
+}
+
+void PersistentKeyValueStoreImpl::StartTask(Task task) {
+  if (!IsInitialized()) {
+    TaskComplete(std::move(task), {});
+    return;
+  }
+  running_task_ = true;
+
+  switch (task.type) {
+    case TaskType::kGet: {
+      std::string key = std::move(task.key);
+      database_->GetEntry(key,
+                          base::BindOnce(&PersistentKeyValueStoreImpl::GetDone,
+                                         GetWeakPtr(), std::move(task)));
+      break;
+    }
+    case TaskType::kPut: {
+      auto entries_to_save = std::make_unique<
+          leveldb_proto::ProtoDatabase<feedkvstore::Entry>::KeyEntryVector>();
+      {
+        feedkvstore::Entry new_entry;
+        new_entry.set_value(std::move(task.put_value));
+        new_entry.set_modification_time(
+            base::Time::Now().ToDeltaSinceWindowsEpoch().InMilliseconds());
+        entries_to_save->emplace_back(task.key, std::move(new_entry));
+      }
+      database_->UpdateEntries(
+          std::move(entries_to_save),
+          /*keys_to_remove=*/std::make_unique<std::vector<std::string>>(),
+          base::BindOnce(&PersistentKeyValueStoreImpl::TaskCompleteBool,
+                         GetWeakPtr(), std::move(task)));
+      break;
+    }
+    case TaskType::kDelete: {
+      auto keys_to_remove = std::make_unique<std::vector<std::string>>();
+      keys_to_remove->push_back(task.key);
+      database_->UpdateEntries(
+          std::make_unique<std::vector<std::pair<std::string, Entry>>>(),
+          std::move(keys_to_remove),
+          base::BindOnce(&PersistentKeyValueStoreImpl::TaskCompleteBool,
+                         GetWeakPtr(), std::move(task)));
+      break;
+    }
+    case TaskType::kClearAll: {
+      auto filter = [](const std::string& key) { return true; };
+      database_->UpdateEntriesWithRemoveFilter(
+          std::make_unique<
+              std::vector<std::pair<std::string, feedkvstore::Entry>>>(),
+          base::BindRepeating(filter),
+          base::BindOnce(&PersistentKeyValueStoreImpl::TaskCompleteBool,
+                         GetWeakPtr(), std::move(task)));
+      break;
+    }
+    case TaskType::kEvictOldEntries: {
+      EvictTask::Start(
+          GetWeakPtr(),
+          base::BindOnce(&PersistentKeyValueStoreImpl::TaskCompleteBool,
+                         GetWeakPtr(), std::move(task)));
+      break;
+    }
+  }
+}
+
+void PersistentKeyValueStoreImpl::GetDone(
+    Task task,
+    bool ok,
+    std::unique_ptr<feedkvstore::Entry> get_entry) {
+  Result result;
+  if (ok && get_entry) {
+    result.success = true;
+    result.get_result = std::move(get_entry->value());
+  } else {
+    result.success = ok;
+  }
+  TaskComplete(std::move(task), std::move(result));
+}
+
+void PersistentKeyValueStoreImpl::TaskComplete(Task complete_task,
+                                               Result result) {
+  if (complete_task.done_callback) {
+    std::move(complete_task.done_callback).Run(std::move(result));
+  }
+  if (queued_tasks_.empty()) {
+    running_task_ = false;
+    return;
+  }
+  Task new_task = std::move(queued_tasks_.front());
+  queued_tasks_.pop();
+  StartTask(std::move(new_task));
+}
+
+void PersistentKeyValueStoreImpl::TaskCompleteBool(Task complete_task,
+                                                   bool ok) {
+  Result result;
+  result.success = ok;
+  return TaskComplete(std::move(complete_task), std::move(result));
+}
+
+}  // namespace feed
diff --git a/components/feed/core/v2/persistent_key_value_store_impl.h b/components/feed/core/v2/persistent_key_value_store_impl.h
new file mode 100644
index 0000000..520b760
--- /dev/null
+++ b/components/feed/core/v2/persistent_key_value_store_impl.h
@@ -0,0 +1,115 @@
+// 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.
+
+#ifndef COMPONENTS_FEED_CORE_V2_PERSISTENT_KEY_VALUE_STORE_IMPL_H_
+#define COMPONENTS_FEED_CORE_V2_PERSISTENT_KEY_VALUE_STORE_IMPL_H_
+
+#include <list>
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "components/feed/core/v2/public/persistent_key_value_store.h"
+#include "components/leveldb_proto/public/proto_database.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
+#include "components/offline_pages/task/task_queue.h"
+
+namespace feedkvstore {
+class Entry;
+}
+namespace feed {
+namespace internal {
+constexpr int kMaxEntriesInMemory = 50;
+}  // namespace internal
+
+// A generic persistent key-value cache. Has a maximum size determined by
+// `feed::Config`. Once size of all values exceed the maximum, older keys
+// are eventually evicted. Key age is determined only by the last call to
+// `Put()`.
+class PersistentKeyValueStoreImpl : public PersistentKeyValueStore {
+ public:
+  using Result = PersistentKeyValueStore::Result;
+  using ResultCallback = base::OnceCallback<void(Result)>;
+
+  explicit PersistentKeyValueStoreImpl(
+      std::unique_ptr<leveldb_proto::ProtoDatabase<feedkvstore::Entry>>
+          database);
+  ~PersistentKeyValueStoreImpl() override;
+  PersistentKeyValueStoreImpl(const PersistentKeyValueStoreImpl&) = delete;
+  PersistentKeyValueStoreImpl& operator=(const PersistentKeyValueStoreImpl&) =
+      delete;
+
+  // PersistentKeyValueStore methods.
+
+  // Erase all data in the store.
+  void ClearAll(ResultCallback callback) override;
+  // Write/overwrite a key/value pair.
+  void Put(const std::string& key,
+           const std::string& value,
+           ResultCallback callback) override;
+  // Get a value by key.
+  void Get(const std::string& key, ResultCallback callback) override;
+  // Delete a value by key.
+  void Delete(const std::string& key, ResultCallback callback) override;
+
+  // Evict old stored entries until total size of all values in the database
+  // is less than max_db_size_bytes.
+  void EvictOldEntries(ResultCallback callback);
+
+  leveldb_proto::ProtoDatabase<feedkvstore::Entry>* GetDatabase() {
+    return database_.get();
+  }
+
+  base::WeakPtr<PersistentKeyValueStoreImpl> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+  bool IsTaskRunningForTesting() const { return running_task_; }
+
+ private:
+  enum class TaskType { kGet, kPut, kDelete, kClearAll, kEvictOldEntries };
+  // Represents any operation on the database. Allows us to easily perform lazy
+  // initialization, and serialize db operations.
+  struct Task {
+    Task();
+    Task(TaskType type, ResultCallback callback);
+    Task(TaskType type, std::string key, ResultCallback callback);
+    Task(Task&&) noexcept;
+    Task(const Task&) = delete;
+    Task& operator=(const Task&) = delete;
+    ~Task();
+
+    TaskType type;
+    // Key for kGet, kPut, and kDelete.
+    std::string key;
+    // Value for kPut.
+    std::string put_value;
+    ResultCallback done_callback;
+  };
+
+  void AddTask(Task task);
+  // Implementation functions for potentially queueable actions.
+  void StartTask(Task task);
+
+  void GetDone(Task task, bool ok, std::unique_ptr<feedkvstore::Entry> entry);
+  void OnDatabaseInitialized(leveldb_proto::Enums::InitStatus status);
+  void TaskComplete(Task task, Result result);
+  void TaskCompleteBool(Task task, bool ok);
+
+  bool IsInitialized() const;
+
+  bool running_task_ = false;
+  base::queue<Task> queued_tasks_;
+  bool triggered_initialize_ = false;
+  leveldb_proto::Enums::InitStatus database_status_ =
+      leveldb_proto::Enums::InitStatus::kNotInitialized;
+  std::unique_ptr<leveldb_proto::ProtoDatabase<feedkvstore::Entry>> database_;
+  base::WeakPtrFactory<PersistentKeyValueStoreImpl> weak_ptr_factory_{this};
+};
+
+}  // namespace feed
+
+#endif  // COMPONENTS_FEED_CORE_V2_PERSISTENT_KEY_VALUE_STORE_IMPL_H_
diff --git a/components/feed/core/v2/persistent_key_value_store_impl_unittest.cc b/components/feed/core/v2/persistent_key_value_store_impl_unittest.cc
new file mode 100644
index 0000000..2f9b3a4
--- /dev/null
+++ b/components/feed/core/v2/persistent_key_value_store_impl_unittest.cc
@@ -0,0 +1,411 @@
+// 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 "components/feed/core/v2/persistent_key_value_store_impl.h"
+
+#include <map>
+#include <set>
+#include <utility>
+
+#include "base/hash/hash.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "components/feed/core/proto/v2/keyvalue_store.pb.h"
+#include "components/feed/core/v2/config.h"
+#include "components/feed/core/v2/public/persistent_key_value_store.h"
+#include "components/feed/core/v2/test/callback_receiver.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace feed {
+namespace {
+using ::feed::internal::kMaxEntriesInMemory;
+
+int hash_int(int v) {
+  return static_cast<int>(base::PersistentHash(base::NumberToString(v)));
+}
+
+class PersistentKeyValueStoreTest : public testing::Test {
+ public:
+  void SetUp() override {
+    // Disable automatic cleanup for deterministic testing.
+    Config config = GetFeedConfig();
+    config.persistent_kv_store_cleanup_interval_in_written_bytes = 0;
+    SetFeedConfigForTesting(config);
+    MakeStore();
+  }
+
+  void TearDown() override {
+    if (store_) {
+      ASSERT_FALSE(store_->IsTaskRunningForTesting());
+    }
+    // ProtoDatabase requires PostTask to clean up.
+    store_.reset();
+    base::RunLoop().RunUntilIdle();
+  }
+
+ protected:
+  void MakeStore() {
+    store_ = std::make_unique<PersistentKeyValueStoreImpl>(
+        leveldb_proto::ProtoDatabaseProvider::GetUniqueDB<feedkvstore::Entry>(
+            leveldb_proto::ProtoDbType::FEED_STREAM_DATABASE,
+            /*db_dir=*/{}, task_environment_.GetMainThreadTaskRunner()));
+  }
+
+  void SetMaxSizeBeforeEviction(int size_in_bytes) {
+    Config config = GetFeedConfig();
+    config.persistent_kv_store_maximum_size_before_eviction = size_in_bytes;
+    SetFeedConfigForTesting(config);
+  }
+
+  void Put(const std::string& key, const std::string& value) {
+    CallbackReceiver<PersistentKeyValueStore::Result> callback;
+    store_->Put(key, value, callback.Bind());
+    ASSERT_TRUE(callback.RunAndGetResult().success);
+  }
+
+  std::string Get(const std::string& key) {
+    CallbackReceiver<PersistentKeyValueStore::Result> callback;
+    store_->Get(key, callback.Bind());
+    return callback.RunAndGetResult().get_result.value_or("<not-found>");
+  }
+
+  std::map<std::string, std::string> GetAllEntries() {
+    // Make sure any queued tasks are complete.
+    base::RunLoop().RunUntilIdle();
+    std::map<std::string, std::string> result;
+    auto callback =
+        [&](bool ok,
+            std::unique_ptr<std::map<std::string, feedkvstore::Entry>> data) {
+          CHECK(ok);
+          for (auto& entry : *data) {
+            result.emplace(entry.first, entry.second.value());
+          }
+        };
+    store_->GetDatabase()->LoadKeysAndEntries(
+        base::BindLambdaForTesting(callback));
+
+    base::RunLoop().RunUntilIdle();
+    return result;
+  }
+
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  std::unique_ptr<PersistentKeyValueStoreImpl> store_;
+};
+
+TEST_F(PersistentKeyValueStoreTest, Put) {
+  CallbackReceiver<PersistentKeyValueStore::Result> callback;
+  store_->Put("x", "y", callback.Bind());
+
+  ASSERT_TRUE(callback.RunAndGetResult().success);
+  EXPECT_EQ((std::map<std::string, std::string>{{"x", "y"}}), GetAllEntries());
+}
+
+TEST_F(PersistentKeyValueStoreTest, GetEmptyKey) {
+  CallbackReceiver<PersistentKeyValueStore::Result> callback;
+  store_->Get("", callback.Bind());
+
+  EXPECT_TRUE(callback.RunAndGetResult().success);
+  EXPECT_FALSE(callback.GetResult()->get_result);
+}
+
+TEST_F(PersistentKeyValueStoreTest, GetKeyNotPresent) {
+  CallbackReceiver<PersistentKeyValueStore::Result> callback;
+  store_->Get("x", callback.Bind());
+
+  EXPECT_TRUE(callback.RunAndGetResult().success);
+  EXPECT_FALSE(callback.GetResult()->get_result);
+}
+
+TEST_F(PersistentKeyValueStoreTest, GetKeyPresent) {
+  Put("x", "y");
+
+  CallbackReceiver<PersistentKeyValueStore::Result> callback;
+  store_->Get("x", callback.Bind());
+  EXPECT_TRUE(callback.RunAndGetResult().success);
+  EXPECT_EQ("y", callback.RunAndGetResult().get_result);
+}
+
+TEST_F(PersistentKeyValueStoreTest, Delete) {
+  Put("x", "y");
+
+  CallbackReceiver<PersistentKeyValueStore::Result> callback;
+  store_->Delete("x", callback.Bind());
+  EXPECT_TRUE(callback.RunAndGetResult().success);
+  EXPECT_EQ("<not-found>", Get("x"));
+}
+
+TEST_F(PersistentKeyValueStoreTest, DeleteNotPresent) {
+  Put("x", "y");
+
+  CallbackReceiver<PersistentKeyValueStore::Result> callback;
+  store_->Delete("y", callback.Bind());
+  EXPECT_TRUE(callback.RunAndGetResult().success);
+  EXPECT_EQ("y", Get("x"));
+}
+
+TEST_F(PersistentKeyValueStoreTest, ClearAll) {
+  Put("x", "y");
+  Put("a", "b");
+
+  CallbackReceiver<PersistentKeyValueStore::Result> callback;
+  store_->ClearAll(callback.Bind());
+  EXPECT_TRUE(callback.RunAndGetResult().success);
+
+  EXPECT_EQ((std::map<std::string, std::string>{}), GetAllEntries());
+}
+
+TEST_F(PersistentKeyValueStoreTest, EvictOldEntriesOnEmptyDatabaseDoesntCrash) {
+  CallbackReceiver<PersistentKeyValueStore::Result> callback;
+  store_->EvictOldEntries(callback.Bind());
+  EXPECT_TRUE(callback.RunAndGetResult().success);
+}
+
+TEST_F(PersistentKeyValueStoreTest, EvictOldEntriesBelowSizeLimit) {
+  Put("x", "12345");
+
+  // Set config db size limit to equal size of 'x'.
+  SetMaxSizeBeforeEviction(5);
+  CallbackReceiver<PersistentKeyValueStore::Result> callback;
+  store_->EvictOldEntries(callback.Bind());
+  EXPECT_TRUE(callback.RunAndGetResult().success);
+
+  EXPECT_EQ((std::map<std::string, std::string>{{"x", "12345"}}),
+            GetAllEntries());
+}
+
+TEST_F(PersistentKeyValueStoreTest, EvictOldEntriesAboveSizeLimit) {
+  Put("x", "12345");
+
+  // Set config db size limit to just below size of 'x'.
+  SetMaxSizeBeforeEviction(4);
+  CallbackReceiver<PersistentKeyValueStore::Result> callback;
+  store_->EvictOldEntries(callback.Bind());
+  EXPECT_TRUE(callback.RunAndGetResult().success);
+
+  EXPECT_EQ((std::map<std::string, std::string>{}), GetAllEntries());
+}
+
+TEST_F(PersistentKeyValueStoreTest, PutAndGetAreQueuedWhileEvicting) {
+  SetMaxSizeBeforeEviction(0);
+  std::vector<std::string> calls;
+  auto record_call = base::BindLambdaForTesting(
+      [&](std::string label, PersistentKeyValueStore::Result) {
+        calls.push_back(label);
+      });
+  store_->Put("x", "12345", base::BindOnce(record_call, "put1"));
+  store_->EvictOldEntries(base::BindOnce(record_call, "evict"));
+  store_->Put("y", "123456", base::BindOnce(record_call, "put2"));
+  std::string get_result = Get("y");
+
+  EXPECT_EQ(std::vector<std::string>({"put1", "evict", "put2"}), calls);
+  EXPECT_EQ((std::map<std::string, std::string>{
+                {"y", "123456"},
+            }),
+            GetAllEntries());
+  EXPECT_EQ("123456", get_result);
+}
+
+TEST_F(PersistentKeyValueStoreTest, EvictOldEntriesDeletesOldEntriesFirst) {
+  Put("1", "x");
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  Put("2", "x");
+
+  SetMaxSizeBeforeEviction(1);
+  store_->EvictOldEntries(base::DoNothing());
+
+  EXPECT_EQ((std::map<std::string, std::string>{{"2", "x"}}), GetAllEntries());
+}
+
+TEST_F(PersistentKeyValueStoreTest,
+       EvictOldEntriesDeletesOldEntriesFirstReverseKeys) {
+  Put("2", "x");
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  Put("1", "x");
+
+  SetMaxSizeBeforeEviction(1);
+  store_->EvictOldEntries(base::DoNothing());
+
+  EXPECT_EQ((std::map<std::string, std::string>{{"1", "x"}}), GetAllEntries());
+}
+
+TEST_F(PersistentKeyValueStoreTest, EvictOldEntriesDeleteFutureEntriesFirst) {
+  // Insert two entries manually. The second entry has a modification time in
+  // the future, so it will be evicted preferentially.
+  {
+    // Trigger and wait for db initialization.
+    Get("foo");
+
+    auto entries_to_save = std::make_unique<
+        leveldb_proto::ProtoDatabase<feedkvstore::Entry>::KeyEntryVector>();
+    {
+      feedkvstore::Entry new_entry;
+      new_entry.set_value("1");
+      new_entry.set_modification_time(
+          (base::Time::Now().ToDeltaSinceWindowsEpoch()).InMilliseconds());
+      entries_to_save->emplace_back("key1", std::move(new_entry));
+    }
+    {
+      feedkvstore::Entry new_entry;
+      new_entry.set_value("2");
+      new_entry.set_modification_time(
+          (base::Time::Now().ToDeltaSinceWindowsEpoch() +
+           base::TimeDelta::FromMinutes(1))
+              .InMilliseconds());
+      entries_to_save->emplace_back("key2", std::move(new_entry));
+    }
+
+    CallbackReceiver<bool> callback;
+    store_->GetDatabase()->UpdateEntries(
+        std::move(entries_to_save),
+        /*keys_to_remove=*/std::make_unique<std::vector<std::string>>(),
+        callback.Bind());
+    ASSERT_TRUE(callback.RunAndGetResult());
+  }
+
+  SetMaxSizeBeforeEviction(1);
+  store_->EvictOldEntries(base::DoNothing());
+
+  EXPECT_EQ((std::map<std::string, std::string>{{"key1", "1"}}),
+            GetAllEntries());
+}
+
+TEST_F(PersistentKeyValueStoreTest, EvictOldEntriesManyEntries) {
+  const int kFinalEntryCount = kMaxEntriesInMemory * 2;
+  const int kInitialEntryCount = kFinalEntryCount + kMaxEntriesInMemory / 2;
+
+  for (int i = 0; i < kInitialEntryCount; ++i) {
+    // Make key order different than insertion order.
+    int key = hash_int(i);
+    Put(base::NumberToString(key), "x");
+    task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  }
+
+  SetMaxSizeBeforeEviction(kFinalEntryCount);
+  store_->EvictOldEntries(base::DoNothing());
+
+  std::map<std::string, std::string> want_entries;
+  for (int i = kInitialEntryCount - kFinalEntryCount; i < kInitialEntryCount;
+       ++i) {
+    want_entries[base::NumberToString(hash_int(i))] = "x";
+  }
+  EXPECT_EQ(want_entries, GetAllEntries());
+}
+
+TEST_F(PersistentKeyValueStoreTest, EvictOldEntriesExactlyMaxEntriesInMemory) {
+  for (int i = 0; i < kMaxEntriesInMemory; ++i) {
+    // Make key order different than insertion order.
+    int key = hash_int(i);
+    Put(base::NumberToString(key), "x");
+    task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  }
+
+  SetMaxSizeBeforeEviction(kMaxEntriesInMemory - 1);
+  store_->EvictOldEntries(base::DoNothing());
+
+  std::map<std::string, std::string> want_entries;
+  for (int i = 1; i < kMaxEntriesInMemory; ++i) {
+    want_entries[base::NumberToString(hash_int(i))] = "x";
+  }
+  EXPECT_EQ(want_entries, GetAllEntries());
+}
+
+TEST_F(PersistentKeyValueStoreTest, EvictOldEntriesMaxEntriesInMemoryPlusOne) {
+  for (int i = 0; i < kMaxEntriesInMemory + 1; ++i) {
+    // Make key order different than insertion order.
+    int key = hash_int(i);
+    Put(base::NumberToString(key), "x");
+    task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  }
+
+  SetMaxSizeBeforeEviction(kMaxEntriesInMemory + 1 - 1);
+  store_->EvictOldEntries(base::DoNothing());
+
+  std::map<std::string, std::string> want_entries;
+  for (int i = 1; i < kMaxEntriesInMemory + 1; ++i) {
+    want_entries[base::NumberToString(hash_int(i))] = "x";
+  }
+  EXPECT_EQ(want_entries, GetAllEntries());
+}
+
+void CallAfterNPostTasks(int post_task_count, base::OnceClosure done) {
+  if (post_task_count == 0) {
+    std::move(done).Run();
+  } else {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(base::BindOnce(&CallAfterNPostTasks, post_task_count - 1,
+                                      std::move(done))));
+  }
+}
+
+// Test that `EvictOldEntries()` completes without crashing, even when the
+// store is deleted between posted tasks.
+TEST_F(PersistentKeyValueStoreTest, DeleteStoreWhileEvictOldEntriesIsRunning) {
+  SetMaxSizeBeforeEviction(kMaxEntriesInMemory + 1);
+
+  constexpr int kMaxPostTasks = 32;  // Today, must be at least 16.
+  for (int post_tasks_before_delete = 0;
+       post_tasks_before_delete < kMaxPostTasks; ++post_tasks_before_delete) {
+    MakeStore();
+    for (int i = 0; i < kMaxEntriesInMemory + 1; ++i) {
+      Put(base::NumberToString(i), "x");
+      task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+    }
+    // Call EvictOldEntries(), and then eventually delete the store while
+    // EvictOldEntries() is running. If EvictOldEntries() completes first,
+    // then exit the loop because we've tried all possible orderings.
+    base::RunLoop run_loop;
+    bool evict_complete = false, delete_complete = false;
+    bool evict_complete_first = false;
+    auto complete_func = [&](bool is_evict_call) {
+      evict_complete |= is_evict_call;
+      delete_complete |= !is_evict_call;
+      if (evict_complete && delete_complete) {
+        evict_complete_first = !is_evict_call;
+        run_loop.QuitClosure().Run();
+      }
+    };
+    store_->EvictOldEntries(base::BindLambdaForTesting(
+        [&](PersistentKeyValueStore::Result) { complete_func(true); }));
+    CallAfterNPostTasks(post_tasks_before_delete,
+                        base::BindLambdaForTesting([&]() {
+                          store_.reset();
+                          complete_func(false);
+                        }));
+    run_loop.RunUntilIdle();
+    if (evict_complete_first) {
+      ASSERT_GT(post_tasks_before_delete, 2)
+          << "EvictOldEntries completed with fewer post tasks than expected";
+      return;
+    }
+  }
+  ASSERT_TRUE(false)
+      << "EvictOldEntries didn't complete after kMaxPostTasks post tasks?";
+}
+
+TEST_F(PersistentKeyValueStoreTest, DataStoreCleansOldDataAutomatically) {
+  // Simulate use of the store by inserting 10 byte entries. On average, we
+  // should perform eviction on every 10 Put() calls -- with a 1/10 chance on
+  // each call. We have a negligible probability of ~1.0e-46 of failing to run
+  // eviction after 1000 iterations.
+  Config config = GetFeedConfig();
+  config.persistent_kv_store_cleanup_interval_in_written_bytes = 100;
+  config.persistent_kv_store_maximum_size_before_eviction = 10;
+  SetFeedConfigForTesting(config);
+  MakeStore();
+
+  for (int i = 0;; ++i) {
+    ASSERT_LT(i, 1000);
+    Put(base::NumberToString(i), "1234567890");
+    task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+    if (Get("0") == "<not-found>")
+      break;
+  }
+}
+
+}  // namespace
+}  // namespace feed
diff --git a/components/feed/core/v2/public/feed_service.cc b/components/feed/core/v2/public/feed_service.cc
index 135f2313..4c14bfd 100644
--- a/components/feed/core/v2/public/feed_service.cc
+++ b/components/feed/core/v2/public/feed_service.cc
@@ -15,6 +15,7 @@
 #include "components/feed/core/v2/feed_stream.h"
 #include "components/feed/core/v2/image_fetcher.h"
 #include "components/feed/core/v2/metrics_reporter.h"
+#include "components/feed/core/v2/persistent_key_value_store_impl.h"
 #include "components/feed/core/v2/refresh_task_scheduler.h"
 #include "components/feed/feed_feature_list.h"
 #include "components/history/core/browser/history_service.h"
@@ -181,6 +182,8 @@
     PrefService* profile_prefs,
     PrefService* local_state,
     std::unique_ptr<leveldb_proto::ProtoDatabase<feedstore::Record>> database,
+    std::unique_ptr<leveldb_proto::ProtoDatabase<feedkvstore::Entry>>
+        key_value_store_database,
     signin::IdentityManager* identity_manager,
     history::HistoryService* history_service,
     offline_pages::PrefetchService* prefetch_service,
@@ -200,12 +203,14 @@
       profile_prefs);
   image_fetcher_ = std::make_unique<ImageFetcher>(url_loader_factory);
   store_ = std::make_unique<FeedStore>(std::move(database));
+  persistent_key_value_store_ = std::make_unique<PersistentKeyValueStoreImpl>(
+      std::move(key_value_store_database));
 
   stream_ = std::make_unique<FeedStream>(
       refresh_task_scheduler_.get(), metrics_reporter_.get(),
       stream_delegate_.get(), profile_prefs, feed_network_.get(),
-      image_fetcher_.get(), store_.get(), prefetch_service, offline_page_model,
-      chrome_info);
+      image_fetcher_.get(), store_.get(), persistent_key_value_store_.get(),
+      prefetch_service, offline_page_model, chrome_info);
 
   history_observer_ = std::make_unique<HistoryObserverImpl>(
       history_service, static_cast<FeedStream*>(stream_.get()),
diff --git a/components/feed/core/v2/public/feed_service.h b/components/feed/core/v2/public/feed_service.h
index d8c1f2a..2890d65 100644
--- a/components/feed/core/v2/public/feed_service.h
+++ b/components/feed/core/v2/public/feed_service.h
@@ -31,6 +31,9 @@
 namespace feedstore {
 class Record;
 }  // namespace feedstore
+namespace feedkvstore {
+class Entry;
+}  // namespace feedkvstore
 namespace network {
 class SharedURLLoaderFactory;
 }  // namespace network
@@ -48,6 +51,7 @@
 class FeedNetwork;
 class FeedStore;
 class FeedStream;
+class PersistentKeyValueStoreImpl;
 class ImageFetcher;
 
 namespace internal {
@@ -82,6 +86,8 @@
       PrefService* profile_prefs,
       PrefService* local_state,
       std::unique_ptr<leveldb_proto::ProtoDatabase<feedstore::Record>> database,
+      std::unique_ptr<leveldb_proto::ProtoDatabase<feedkvstore::Entry>>
+          key_value_store_database,
       signin::IdentityManager* identity_manager,
       history::HistoryService* history_service,
       offline_pages::PrefetchService* prefetch_service,
@@ -123,6 +129,7 @@
   std::unique_ptr<FeedNetwork> feed_network_;
   std::unique_ptr<ImageFetcher> image_fetcher_;
   std::unique_ptr<FeedStore> store_;
+  std::unique_ptr<PersistentKeyValueStoreImpl> persistent_key_value_store_;
   std::unique_ptr<RefreshTaskScheduler> refresh_task_scheduler_;
   std::unique_ptr<HistoryObserverImpl> history_observer_;
   std::unique_ptr<IdentityManagerObserverImpl> identity_manager_observer_;
diff --git a/components/feed/core/v2/public/feed_stream_api.h b/components/feed/core/v2/public/feed_stream_api.h
index b33d9e8..c531bed2 100644
--- a/components/feed/core/v2/public/feed_stream_api.h
+++ b/components/feed/core/v2/public/feed_stream_api.h
@@ -23,6 +23,7 @@
 }  // namespace feedstore
 
 namespace feed {
+class PersistentKeyValueStore;
 
 // This is the public access point for interacting with the Feed stream
 // contents.
@@ -93,6 +94,8 @@
   // |id| doesn't match an active fetch, nothing happens.
   virtual void CancelImageFetch(ImageFetchId id) = 0;
 
+  virtual PersistentKeyValueStore* GetPersistentKeyValueStore() = 0;
+
   // Apply |operations| to the stream model. Does nothing if the model is not
   // yet loaded.
   virtual void ExecuteOperations(
diff --git a/components/feed/core/v2/public/persistent_key_value_store.cc b/components/feed/core/v2/public/persistent_key_value_store.cc
new file mode 100644
index 0000000..ba15023
--- /dev/null
+++ b/components/feed/core/v2/public/persistent_key_value_store.cc
@@ -0,0 +1,15 @@
+// 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 "components/feed/core/v2/public/persistent_key_value_store.h"
+
+namespace feed {
+
+PersistentKeyValueStore::Result::Result() = default;
+PersistentKeyValueStore::Result::Result(Result&&) = default;
+PersistentKeyValueStore::Result& PersistentKeyValueStore::Result::operator=(
+    Result&&) = default;
+PersistentKeyValueStore::Result::~Result() = default;
+
+}  // namespace feed
diff --git a/components/feed/core/v2/public/persistent_key_value_store.h b/components/feed/core/v2/public/persistent_key_value_store.h
new file mode 100644
index 0000000..070d21e8
--- /dev/null
+++ b/components/feed/core/v2/public/persistent_key_value_store.h
@@ -0,0 +1,57 @@
+// 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.
+
+#ifndef COMPONENTS_FEED_CORE_V2_PUBLIC_PERSISTENT_KEY_VALUE_STORE_H_
+#define COMPONENTS_FEED_CORE_V2_PUBLIC_PERSISTENT_KEY_VALUE_STORE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/optional.h"
+
+namespace feed {
+
+// A generic persistent key-value cache. Has a maximum size determined by
+// `feed::Config`. Once size of all values exceed the maximum, older keys
+// are eventually evicted. Key age is determined only by the last call to
+// `Put()`.
+class PersistentKeyValueStore {
+ public:
+  struct Result {
+    Result();
+    Result(Result&&);
+    Result& operator=(Result&&);
+    ~Result();
+    // Whether the operation succeeded. Failure may be due to a low level
+    // database error, or a missing key/value pair.
+    bool success = false;
+    // For `Get()` operations, the value of the key if it exists.
+    base::Optional<std::string> get_result;
+  };
+
+  using ResultCallback = base::OnceCallback<void(Result)>;
+
+  PersistentKeyValueStore() = default;
+  virtual ~PersistentKeyValueStore() = default;
+  PersistentKeyValueStore(const PersistentKeyValueStore&) = delete;
+  PersistentKeyValueStore& operator=(const PersistentKeyValueStore&) = delete;
+
+  // Erase all data in the store.
+  virtual void ClearAll(ResultCallback callback) = 0;
+  // Write/overwrite a key/value pair.
+  virtual void Put(const std::string& key,
+                   const std::string& value,
+                   ResultCallback callback) = 0;
+  // Get a value by key.
+  virtual void Get(const std::string& key, ResultCallback callback) = 0;
+  // Delete a value by key.
+  virtual void Delete(const std::string& key, ResultCallback callback) = 0;
+
+ private:
+};
+
+}  // namespace feed
+
+#endif  // COMPONENTS_FEED_CORE_V2_PUBLIC_PERSISTENT_KEY_VALUE_STORE_H_
diff --git a/components/feed/core/v2/tasks/clear_all_task.cc b/components/feed/core/v2/tasks/clear_all_task.cc
index ddcf65d1..45fb2c7 100644
--- a/components/feed/core/v2/tasks/clear_all_task.cc
+++ b/components/feed/core/v2/tasks/clear_all_task.cc
@@ -9,6 +9,7 @@
 
 #include "components/feed/core/v2/feed_store.h"
 #include "components/feed/core/v2/feed_stream.h"
+#include "components/feed/core/v2/public/persistent_key_value_store.h"
 
 namespace feed {
 
@@ -17,6 +18,7 @@
 
 void ClearAllTask::Run() {
   stream_->UnloadModel();
+  stream_->GetPersistentKeyValueStore()->ClearAll(base::DoNothing());
   stream_->GetStore()->ClearAll(
       base::BindOnce(&ClearAllTask::StoreClearComplete, GetWeakPtr()));
 }
diff --git a/components/feed/core/v2/test/callback_receiver.cc b/components/feed/core/v2/test/callback_receiver.cc
new file mode 100644
index 0000000..a9621ee
--- /dev/null
+++ b/components/feed/core/v2/test/callback_receiver.cc
@@ -0,0 +1,28 @@
+// 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 "components/feed/core/v2/test/callback_receiver.h"
+
+namespace feed {
+namespace internal {
+void CallbackReceiverBase::RunUntilCalled() {
+  if (called_)
+    return;
+  if (run_loop_) {
+    run_loop_->Run();
+  } else {
+    base::RunLoop run_loop;
+    run_loop_ = &run_loop;
+    run_loop.Run();
+    run_loop_ = nullptr;
+  }
+}
+void CallbackReceiverBase::Done() {
+  called_ = true;
+  if (run_loop_)
+    run_loop_->Quit();
+}
+
+}  // namespace internal
+}  // namespace feed
diff --git a/components/feed/core/v2/test/callback_receiver.h b/components/feed/core/v2/test/callback_receiver.h
index 1cd9fc6..3b2c4549 100644
--- a/components/feed/core/v2/test/callback_receiver.h
+++ b/components/feed/core/v2/test/callback_receiver.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_FEED_CORE_V2_TEST_CALLBACK_RECEIVER_H_
 #define COMPONENTS_FEED_CORE_V2_TEST_CALLBACK_RECEIVER_H_
 
+#include <memory>
 #include <tuple>
 #include <utility>
 
@@ -21,23 +22,41 @@
   return base::nullopt;
 }
 
+class CallbackReceiverBase {
+ public:
+  explicit CallbackReceiverBase(base::RunLoop* run_loop = nullptr)
+      : run_loop_(run_loop) {}
+
+  void Clear() { called_ = false; }
+  bool called() const { return called_; }
+  void RunUntilCalled();
+  void Done();
+
+ private:
+  bool called_ = false;
+  base::RunLoop* run_loop_;
+};
+
 }  // namespace internal
 
 template <typename... T>
-class CallbackReceiver {
+class CallbackReceiver : public internal::CallbackReceiverBase {
  public:
   explicit CallbackReceiver(base::RunLoop* run_loop = nullptr)
-      : run_loop_(run_loop) {}
+      : CallbackReceiverBase(run_loop) {}
+
   void Done(T... results) {
     results_ = std::make_tuple(std::move(results)...);
-    if (run_loop_)
-      run_loop_->Quit();
+    CallbackReceiverBase::Done();
   }
   base::OnceCallback<void(T...)> Bind() {
     return base::BindOnce(&CallbackReceiver::Done, base::Unretained(this));
   }
 
-  void Clear() { results_ = std::make_tuple(internal::Nullopt<T>()...); }
+  void Clear() {
+    CallbackReceiverBase::Clear();
+    results_ = std::make_tuple(internal::Nullopt<T>()...);
+  }
 
   // Get a result by its position in the arguments to Done().
   // Call GetResult() for the first argument or GetResult<I>().
@@ -47,6 +66,12 @@
     return std::get<I>(results_);
   }
 
+  template <size_t I = 0>
+  typename std::tuple_element<I, std::tuple<T...>>::type& RunAndGetResult() {
+    RunUntilCalled();
+    return std::get<I>(results_).value();
+  }
+
   // Get a result by its type. Won't compile if there is more than one matching
   // type.
   template <class C>
@@ -56,7 +81,17 @@
 
  private:
   std::tuple<base::Optional<T>...> results_;
-  base::RunLoop* run_loop_;
+};
+
+template <>
+class CallbackReceiver<> : public internal::CallbackReceiverBase {
+ public:
+  explicit CallbackReceiver(base::RunLoop* run_loop = nullptr)
+      : CallbackReceiverBase(run_loop) {}
+
+  base::OnceClosure Bind() {
+    return base::BindOnce(&CallbackReceiverBase::Done, base::Unretained(this));
+  }
 };
 
 }  // namespace feed
diff --git a/components/feed/core/v2/test/callback_receiver_unittest.cc b/components/feed/core/v2/test/callback_receiver_unittest.cc
index 301cec7..c3a3e85 100644
--- a/components/feed/core/v2/test/callback_receiver_unittest.cc
+++ b/components/feed/core/v2/test/callback_receiver_unittest.cc
@@ -5,6 +5,9 @@
 #include "components/feed/core/v2/test/callback_receiver.h"
 
 #include "base/optional.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace feed {
@@ -41,4 +44,25 @@
   EXPECT_EQ(cr.GetResult<1>(), base::nullopt);
 }
 
+TEST(CallbackReceiverTest, RunAndGetResult) {
+  base::test::TaskEnvironment task_environment{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+  CallbackReceiver<int> cr1;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(cr1.Bind(), 42));
+  EXPECT_EQ(42, cr1.RunAndGetResult());
+}
+
+TEST(CallbackReceiverTest, RunAndGetResultExternalRunLoop) {
+  base::test::TaskEnvironment task_environment{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+  base::RunLoop run_loop;
+  CallbackReceiver<int> cr1(&run_loop);
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(cr1.Bind(), 42));
+  EXPECT_EQ(42, cr1.RunAndGetResult());
+}
+
 }  // namespace feed
diff --git a/components/full_restore/full_restore_save_handler.cc b/components/full_restore/full_restore_save_handler.cc
index 0c45667..30686585 100644
--- a/components/full_restore/full_restore_save_handler.cc
+++ b/components/full_restore/full_restore_save_handler.cc
@@ -54,7 +54,7 @@
   observed_windows_.AddObservation(window);
 }
 
-void FullRestoreSaveHandler::OnWindowDestroying(aura::Window* window) {
+void FullRestoreSaveHandler::OnWindowDestroyed(aura::Window* window) {
   // TODO(crbug.com/1146900): Handle ARC app windows.
 
   DCHECK(observed_windows_.IsObservingSource(window));
diff --git a/components/full_restore/full_restore_save_handler.h b/components/full_restore/full_restore_save_handler.h
index d1a81bd..b0f3ebd1 100644
--- a/components/full_restore/full_restore_save_handler.h
+++ b/components/full_restore/full_restore_save_handler.h
@@ -52,7 +52,7 @@
   void OnWindowInitialized(aura::Window* window) override;
 
   // aura::WindowObserver:
-  void OnWindowDestroying(aura::Window* window) override;
+  void OnWindowDestroyed(aura::Window* window) override;
 
   // Save |app_launch_info| to the full restore file in |profile_path|.
   void SaveAppLaunchInfo(const base::FilePath& profile_path,
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc
index 7d43755..4ded644 100644
--- a/components/history/core/browser/history_backend.cc
+++ b/components/history/core/browser/history_backend.cc
@@ -462,6 +462,24 @@
   UpdateVisitDuration(visit_id, end_ts);
 }
 
+void HistoryBackend::SetFlocAllowed(ContextID context_id,
+                                    int nav_entry_id,
+                                    const GURL& url) {
+  TRACE_EVENT0("browser", "HistoryBackend::SetFlocAllowed");
+
+  if (!db_)
+    return;
+
+  VisitID visit_id = tracker_.GetLastVisit(context_id, nav_entry_id, url);
+
+  VisitRow visit_row;
+  if (db_->GetRowForVisit(visit_id, &visit_row)) {
+    visit_row.floc_allowed = true;
+    db_->UpdateVisitRow(visit_row);
+    ScheduleCommit();
+  }
+}
+
 void HistoryBackend::UpdateVisitDuration(VisitID visit_id, const Time end_ts) {
   if (!db_)
     return;
diff --git a/components/history/core/browser/history_backend.h b/components/history/core/browser/history_backend.h
index 3c36058c..3af2191f 100644
--- a/components/history/core/browser/history_backend.h
+++ b/components/history/core/browser/history_backend.h
@@ -242,6 +242,7 @@
                              int nav_entry_id,
                              const GURL& url,
                              base::Time end_ts);
+  void SetFlocAllowed(ContextID context_id, int nav_entry_id, const GURL& url);
 
   // Querying ------------------------------------------------------------------
 
diff --git a/components/history/core/browser/history_backend_unittest.cc b/components/history/core/browser/history_backend_unittest.cc
index 3165787..eb3cd99e 100644
--- a/components/history/core/browser/history_backend_unittest.cc
+++ b/components/history/core/browser/history_backend_unittest.cc
@@ -1465,6 +1465,36 @@
   EXPECT_EQ(history::SOURCE_SYNCED, visit_sources.begin()->second);
 }
 
+TEST_F(HistoryBackendTest, SetFlocAllowed) {
+  ASSERT_TRUE(backend_.get());
+
+  GURL url("http://test-set-floc-allowed.com");
+  ContextID context_id = reinterpret_cast<ContextID>(1);
+  int nav_entry_id = 1;
+
+  HistoryAddPageArgs request(url, base::Time::Now(), context_id, nav_entry_id,
+                             GURL(), history::RedirectList(),
+                             ui::PAGE_TRANSITION_TYPED, false,
+                             history::SOURCE_BROWSED, false, true, false);
+  backend_->AddPage(request);
+
+  VisitVector visits;
+  URLRow row;
+  URLID id = backend_->db()->GetRowForURL(url, &row);
+  ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+  ASSERT_EQ(1U, visits.size());
+  VisitRow visit = visits[0];
+  EXPECT_FALSE(visit.floc_allowed);
+
+  backend_->SetFlocAllowed(context_id, nav_entry_id, url);
+
+  id = backend_->db()->GetRowForURL(url, &row);
+  ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+  ASSERT_EQ(1U, visits.size());
+  visit = visits[0];
+  EXPECT_TRUE(visit.floc_allowed);
+}
+
 TEST_F(HistoryBackendTest, AddVisitsSource) {
   ASSERT_TRUE(backend_.get());
 
diff --git a/components/history/core/browser/history_service.cc b/components/history/core/browser/history_service.cc
index 1cb3f16..64c35f25 100644
--- a/components/history/core/browser/history_service.cc
+++ b/components/history/core/browser/history_service.cc
@@ -413,6 +413,17 @@
                      context_id, nav_entry_id, url, end_ts));
 }
 
+void HistoryService::SetFlocAllowed(ContextID context_id,
+                                    int nav_entry_id,
+                                    const GURL& url) {
+  TRACE_EVENT0("browser", "HistoryService::SetFlocAllowed");
+  DCHECK(backend_task_runner_) << "History service being called after cleanup";
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  ScheduleTask(PRIORITY_NORMAL,
+               base::BindOnce(&HistoryBackend::SetFlocAllowed, history_backend_,
+                              context_id, nav_entry_id, url));
+}
+
 void HistoryService::AddPageWithDetails(const GURL& url,
                                         const base::string16& title,
                                         int visit_count,
diff --git a/components/history/core/browser/history_service.h b/components/history/core/browser/history_service.h
index 0411226..9efc3fa 100644
--- a/components/history/core/browser/history_service.h
+++ b/components/history/core/browser/history_service.h
@@ -165,6 +165,8 @@
   //
   // |floc_allowed| indicates whether this URL visit can be included in FLoC
   // computation. See VisitRow::floc_allowed for details.
+  // TODO(yaoxia): Remove the floc_allowed param from this API as well as from
+  // HistoryAddPageArgs. This bit will never be set at this point.
   //
   // TODO(avi): This is no longer true. 'page id' was removed years ago, and
   // their uses replaced by globally-unique nav_entry_ids. Is ContextID still
@@ -214,6 +216,11 @@
                              const GURL& url,
                              base::Time end_ts);
 
+  // Updates the history database by setting the floc allowed bit. The page can
+  // be identified by the combination of the context id, the navigation entry id
+  // and the url. No-op if the page is not found.
+  void SetFlocAllowed(ContextID context_id, int nav_entry_id, const GURL& url);
+
   // Querying ------------------------------------------------------------------
 
   // Returns the information about the requested URL. If the URL is found,
diff --git a/components/leveldb_proto/public/shared_proto_database_client_list.cc b/components/leveldb_proto/public/shared_proto_database_client_list.cc
index eae251d..c2d3f632 100644
--- a/components/leveldb_proto/public/shared_proto_database_client_list.cc
+++ b/components/leveldb_proto/public/shared_proto_database_client_list.cc
@@ -88,6 +88,8 @@
       return "NearbySharePublicCertificateDatabase";
     case ProtoDbType::VIDEO_TUTORIALS_DATABASE:
       return "VideoTutorialsDatabase";
+    case ProtoDbType::FEED_KEY_VALUE_DATABASE:
+      return "FeedKeyValueDatabase";
     case ProtoDbType::LAST:
       NOTREACHED();
       return std::string();
diff --git a/components/leveldb_proto/public/shared_proto_database_client_list.h b/components/leveldb_proto/public/shared_proto_database_client_list.h
index 57d6886..55e2f9a77 100644
--- a/components/leveldb_proto/public/shared_proto_database_client_list.h
+++ b/components/leveldb_proto/public/shared_proto_database_client_list.h
@@ -18,7 +18,8 @@
 // The enum values are used to index into the shared database. Do not rearrange
 // or reuse the integer values. Add new database types at the end of the enum,
 // and update the string mapping in ProtoDbTypeToString(). Also update the
-// suffix LevelDBClients in histograms.xml to match the strings for the types.
+// suffix LevelDBClients in histogram_suffixes_list.xml to match the strings for
+// the types.
 enum class ProtoDbType {
   TEST_DATABASE0 = 0,
   TEST_DATABASE1 = 1,
@@ -53,6 +54,7 @@
   UPBOARDING_QUERY_TILE_STORE = 28,
   NEARBY_SHARE_PUBLIC_CERTIFICATE_DATABASE = 29,
   VIDEO_TUTORIALS_DATABASE = 30,
+  FEED_KEY_VALUE_DATABASE = 31,
   LAST,
 };
 
@@ -68,6 +70,7 @@
     ProtoDbType::UPBOARDING_QUERY_TILE_STORE,
     ProtoDbType::NEARBY_SHARE_PUBLIC_CERTIFICATE_DATABASE,
     ProtoDbType::VIDEO_TUTORIALS_DATABASE,
+    ProtoDbType::FEED_KEY_VALUE_DATABASE,
     ProtoDbType::LAST,  // Marks the end of list.
 };
 
diff --git a/components/offline_pages/content/renovations/BUILD.gn b/components/offline_pages/content/renovations/BUILD.gn
deleted file mode 100644
index 955803e1..0000000
--- a/components/offline_pages/content/renovations/BUILD.gn
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-if (is_android) {
-  import("//build/config/android/rules.gni")
-}
-
-source_set("renovations") {
-  sources = [
-    "render_frame_script_injector.cc",
-    "render_frame_script_injector.h",
-  ]
-
-  deps = [
-    "//components/offline_pages/core/renovations",
-    "//content/public/browser",
-  ]
-}
diff --git a/components/offline_pages/content/renovations/DEPS b/components/offline_pages/content/renovations/DEPS
deleted file mode 100644
index c24130e..0000000
--- a/components/offline_pages/content/renovations/DEPS
+++ /dev/null
@@ -1,4 +0,0 @@
-include_rules = [
-  "+content/public/browser",
-  "+content/public/common",
-]
diff --git a/components/offline_pages/content/renovations/render_frame_script_injector.cc b/components/offline_pages/content/renovations/render_frame_script_injector.cc
deleted file mode 100644
index cc90769..0000000
--- a/components/offline_pages/content/renovations/render_frame_script_injector.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/content/renovations/render_frame_script_injector.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/check.h"
-#include "base/values.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/common/isolated_world_ids.h"
-
-namespace offline_pages {
-
-RenderFrameScriptInjector::RenderFrameScriptInjector(
-    content::RenderFrameHost* render_frame_host,
-    int32_t isolated_world_id)
-    : render_frame_host_(render_frame_host),
-      isolated_world_id_(isolated_world_id) {
-  DCHECK(render_frame_host_);
-  DCHECK(isolated_world_id_ > content::ISOLATED_WORLD_ID_GLOBAL &&
-         isolated_world_id_ < content::ISOLATED_WORLD_ID_MAX);
-}
-
-void RenderFrameScriptInjector::Inject(base::string16 script,
-                                       ResultCallback callback) {
-  // |render_frame_host_| should still be alive if the
-  // caller is using this class correctly.
-  DCHECK(render_frame_host_);
-  render_frame_host_->ExecuteJavaScriptInIsolatedWorld(
-      script, std::move(callback), isolated_world_id_);
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/content/renovations/render_frame_script_injector.h b/components/offline_pages/content/renovations/render_frame_script_injector.h
deleted file mode 100644
index e572ee1c..0000000
--- a/components/offline_pages/content/renovations/render_frame_script_injector.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_CONTENT_RENOVATIONS_RENDER_FRAME_SCRIPT_INJECTOR_H_
-#define COMPONENTS_OFFLINE_PAGES_CONTENT_RENOVATIONS_RENDER_FRAME_SCRIPT_INJECTOR_H_
-
-#include "components/offline_pages/core/renovations/script_injector.h"
-
-namespace content {
-class RenderFrameHost;
-}
-
-namespace offline_pages {
-
-// ScriptInjector for running scripts in a RenderFrame within a given isolated
-// world.
-class RenderFrameScriptInjector : public ScriptInjector {
- public:
-  ~RenderFrameScriptInjector() override = default;
-
-  // The |render_frame_host| is expected to outlive this
-  // RenderFrameScriptInjector instance.
-  RenderFrameScriptInjector(content::RenderFrameHost* render_frame_host,
-                            int32_t isolated_world_id);
-
-  // ScriptInjector implementation.
-  void Inject(base::string16 script, ResultCallback callback) override;
-
- private:
-  content::RenderFrameHost* render_frame_host_;
-  int32_t isolated_world_id_;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_CONTENT_RENOVATIONS_RENDER_FRAME_SCRIPT_INJECTOR_H_
diff --git a/components/offline_pages/content/renovations/test/DEPS b/components/offline_pages/content/renovations/test/DEPS
deleted file mode 100644
index 6bfde01..0000000
--- a/components/offline_pages/content/renovations/test/DEPS
+++ /dev/null
@@ -1,6 +0,0 @@
-include_rules = [
-  "+content/public/test",
-  "+content/shell/browser",
-  "+net/test/embedded_test_server",
-  "+ui/base/resource/resource_bundle.h",
-]
diff --git a/components/offline_pages/content/renovations/test/page_renovator_browsertest.cc b/components/offline_pages/content/renovations/test/page_renovator_browsertest.cc
deleted file mode 100644
index 67f49e2..0000000
--- a/components/offline_pages/content/renovations/test/page_renovator_browsertest.cc
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/core/renovations/page_renovator.h"
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/path_service.h"
-#include "base/run_loop.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "build/build_config.h"
-#include "components/offline_pages/content/renovations/render_frame_script_injector.h"
-#include "components/offline_pages/core/renovations/page_renovation_loader.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/isolated_world_ids.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/content_browser_test.h"
-#include "content/public/test/content_browser_test_utils.h"
-#include "content/public/test/test_utils.h"
-#include "content/shell/browser/shell.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "url/gurl.h"
-
-namespace offline_pages {
-
-namespace {
-
-const char kDocRoot[] = "components/test/data/offline_pages";
-
-// For testing real renovations.
-const char kWikipediaTestPagePath[] = "/wikipedia_renovation_test_page.html";
-
-const char kCheckUnfoldBlockScript[] =
-    "document.getElementById('block1').classList.contains('open-block') && "
-    "document.getElementById('block2').classList.contains('open-block')";
-const char kCheckUnfoldHeadingScript[] =
-    "document.getElementById('heading1').classList.contains('open-block') && "
-    "document.getElementById('heading2').classList.contains('open-block')";
-
-// For running against the test renovations.
-const char kTestPagePath[] = "/renovator_test_page.html";
-const char kTestRenovationScript[] =
-    R"*(function foo() {
-      var node = document.getElementById('foo');
-      node.innerHTML = 'Good';
-    }
-    function bar() {
-      var node = document.getElementById('bar');
-      node.parentNode.removeChild(node);
-    }
-    function always() {
-      var node = document.getElementById('always');
-      node.parentNode.removeChild(node);
-    }
-    var mapRenovations = {'foo':foo, 'bar':bar, 'always':always};
-    function run_renovations(idlist) {
-      for (var id of idlist) {
-        mapRenovations[id]();
-      }
-    })*";
-
-// Scripts to check whether each renovation ran in the test page.
-const char kCheckFooScript[] =
-    "document.getElementById('foo').innerHTML == 'Good'";
-const char kCheckBarScript[] = "document.getElementById('bar') == null";
-const char kCheckAlwaysScript[] = "document.getElementById('always') == null";
-
-// Renovation that should only run on pages from URL foo.bar
-class FooPageRenovation : public PageRenovation {
- public:
-  bool ShouldRun(const GURL& url) const override {
-    return url.host() == "foo.bar";
-  }
-  std::string GetID() const override { return "foo"; }
-};
-
-// Renovation that should only run on pages from URL bar.qux
-class BarPageRenovation : public PageRenovation {
- public:
-  bool ShouldRun(const GURL& url) const override {
-    return url.host() == "bar.qux";
-  }
-  std::string GetID() const override { return "bar"; }
-};
-
-// Renovation that should run on every page.
-class AlwaysRenovation : public PageRenovation {
- public:
-  bool ShouldRun(const GURL& url) const override { return true; }
-  std::string GetID() const override { return "always"; }
-};
-
-}  // namespace
-
-class PageRenovatorBrowserTest : public content::ContentBrowserTest {
- public:
-  void SetUpOnMainThread() override;
-
-  // Navigates the content shell to the path indicated. This path is
-  // relative to the Chromium source root.
-  void Navigate(const std::string& test_page_path);
-
-  // These functions initialize the PageRenovator and dependencies
-  // with either testing renovations defined above, or with production
-  // renovations. |fake_url| is the URL passed to the PageRenovator
-  // and determines which renovations should be run.
-  //
-  // Only one of these should be called, and |Navigate| should be called
-  // beforehand.
-  void InitializeWithTestingRenovations(const GURL& fake_url);
-  void InitializeWithRealRenovations(const GURL& fake_url);
-
-  void QuitRunLoop();
-
- protected:
-  net::EmbeddedTestServer test_server_;
-  content::RenderFrameHost* render_frame_;
-  std::unique_ptr<PageRenovationLoader> page_renovation_loader_;
-  std::unique_ptr<PageRenovator> page_renovator_;
-
-  std::unique_ptr<base::RunLoop> run_loop_;
-};
-
-void PageRenovatorBrowserTest::SetUpOnMainThread() {
-  // Add resources pack to resource bundle so PageRenovationLoader can
-  // load our renovation script.
-  base::FilePath pak_dir;
-#if defined(OS_ANDROID)
-  base::PathService::Get(base::DIR_ANDROID_APP_DATA, &pak_dir);
-  pak_dir = pak_dir.Append(FILE_PATH_LITERAL("paks"));
-#else
-  base::PathService::Get(base::DIR_MODULE, &pak_dir);
-#endif  // OS_ANDROID
-  base::FilePath pak_file =
-      pak_dir.Append(FILE_PATH_LITERAL("components_tests_resources.pak"));
-  ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
-      pak_file, ui::SCALE_FACTOR_NONE);
-
-  // Serve our test HTML.
-  test_server_.ServeFilesFromSourceDirectory(kDocRoot);
-  ASSERT_TRUE(test_server_.Start());
-
-  run_loop_.reset(new base::RunLoop);
-}
-
-void PageRenovatorBrowserTest::Navigate(const std::string& test_page_path) {
-  GURL url = test_server_.GetURL(test_page_path);
-  EXPECT_TRUE(content::NavigateToURL(shell(), url));
-  render_frame_ = shell()->web_contents()->GetMainFrame();
-}
-
-void PageRenovatorBrowserTest::InitializeWithTestingRenovations(
-    const GURL& fake_url) {
-  ASSERT_TRUE(render_frame_) << "Navigate should have been called.";
-
-  std::vector<std::unique_ptr<PageRenovation>> renovations;
-  renovations.push_back(std::make_unique<FooPageRenovation>());
-  renovations.push_back(std::make_unique<BarPageRenovation>());
-  renovations.push_back(std::make_unique<AlwaysRenovation>());
-
-  page_renovation_loader_.reset(new PageRenovationLoader);
-  page_renovation_loader_->SetSourceForTest(
-      base::ASCIIToUTF16(kTestRenovationScript));
-  page_renovation_loader_->SetRenovationsForTest(std::move(renovations));
-
-  auto script_injector = std::make_unique<RenderFrameScriptInjector>(
-      render_frame_, content::ISOLATED_WORLD_ID_CONTENT_END);
-  page_renovator_.reset(new PageRenovator(
-      page_renovation_loader_.get(), std::move(script_injector), fake_url));
-}
-
-void PageRenovatorBrowserTest::InitializeWithRealRenovations(
-    const GURL& fake_url) {
-  ASSERT_TRUE(render_frame_) << "Navigate should have been called.";
-
-  page_renovation_loader_.reset(new PageRenovationLoader);
-
-  auto script_injector = std::make_unique<RenderFrameScriptInjector>(
-      render_frame_, content::ISOLATED_WORLD_ID_CONTENT_END);
-  page_renovator_.reset(new PageRenovator(
-      page_renovation_loader_.get(), std::move(script_injector), fake_url));
-}
-
-void PageRenovatorBrowserTest::QuitRunLoop() {
-  content::GetUIThreadTaskRunner({})->PostTask(
-      FROM_HERE, content::GetDeferredQuitTaskForRunLoop(run_loop_.get()));
-}
-
-#if defined(OS_WIN)
-#define MAYBE_CorrectRenovationsRun DISABLED_CorrectRenovationsRun
-#else
-#define MAYBE_CorrectRenovationsRun CorrectRenovationsRun
-#endif
-IN_PROC_BROWSER_TEST_F(PageRenovatorBrowserTest, MAYBE_CorrectRenovationsRun) {
-  Navigate(kTestPagePath);
-  InitializeWithTestingRenovations(GURL("http://foo.bar/"));
-  // This should run FooPageRenovation and AlwaysRenovation, but not
-  // BarPageRenovation.
-  page_renovator_->RunRenovations(base::BindOnce(
-      &PageRenovatorBrowserTest::QuitRunLoop, base::Unretained(this)));
-  content::RunThisRunLoop(run_loop_.get());
-
-  // Check that correct modifications were made to the page.
-  base::Value fooResult =
-      content::ExecuteScriptAndGetValue(render_frame_, kCheckFooScript);
-  base::Value barResult =
-      content::ExecuteScriptAndGetValue(render_frame_, kCheckBarScript);
-  base::Value alwaysResult =
-      content::ExecuteScriptAndGetValue(render_frame_, kCheckAlwaysScript);
-
-  EXPECT_TRUE(fooResult.GetBool());
-  EXPECT_FALSE(barResult.GetBool());
-  EXPECT_TRUE(alwaysResult.GetBool());
-}
-
-#if defined(OS_WIN)
-#define MAYBE_WikipediaRenovationRuns DISABLED_WikipediaRenovationRuns
-#else
-#define MAYBE_WikipediaRenovationRuns WikipediaRenovationRuns
-#endif
-IN_PROC_BROWSER_TEST_F(PageRenovatorBrowserTest,
-                       MAYBE_WikipediaRenovationRuns) {
-  Navigate(kWikipediaTestPagePath);
-  InitializeWithRealRenovations(GURL("http://en.m.wikipedia.org/"));
-  page_renovator_->RunRenovations(base::BindOnce(
-      &PageRenovatorBrowserTest::QuitRunLoop, base::Unretained(this)));
-  content::RunThisRunLoop(run_loop_.get());
-
-  base::Value unfoldBlockResult =
-      content::ExecuteScriptAndGetValue(render_frame_, kCheckUnfoldBlockScript);
-  base::Value unfoldHeadingResult = content::ExecuteScriptAndGetValue(
-      render_frame_, kCheckUnfoldHeadingScript);
-
-  EXPECT_TRUE(unfoldBlockResult.GetBool());
-  EXPECT_TRUE(unfoldHeadingResult.GetBool());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/core/BUILD.gn b/components/offline_pages/core/BUILD.gn
index 5ff892f..e674e792 100644
--- a/components/offline_pages/core/BUILD.gn
+++ b/components/offline_pages/core/BUILD.gn
@@ -180,7 +180,6 @@
     ":switches",
     ":test_support",
     "prefetch:unit_tests",
-    "renovations:unit_tests",
     "//base",
     "//base/test:test_support",
     "//components/offline_pages/task:test_support",
diff --git a/components/offline_pages/core/background_snapshot_controller.cc b/components/offline_pages/core/background_snapshot_controller.cc
index b13ee560..6488af9 100644
--- a/components/offline_pages/core/background_snapshot_controller.cc
+++ b/components/offline_pages/core/background_snapshot_controller.cc
@@ -35,8 +35,8 @@
       state_(State::READY),
       delay_after_document_on_load_completed_ms_(
           kDelayAfterDocumentOnLoadCompletedMsBackground),
-      delay_after_renovations_completed_ms_(kDelayAfterRenovationsCompletedMs),
-      renovations_enabled_(renovations_enabled) {
+      delay_after_renovations_completed_ms_(kDelayAfterRenovationsCompletedMs) {
+  DCHECK(!renovations_enabled);
   if (offline_pages::ShouldUseTestingSnapshotDelay()) {
     delay_after_document_on_load_completed_ms_ = kDelayForTests;
     delay_after_renovations_completed_ms_ = kDelayForTests;
@@ -56,23 +56,9 @@
 }
 
 void BackgroundSnapshotController::RenovationsCompleted() {
-  if (renovations_enabled_) {
-    task_runner_->PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(
-            &BackgroundSnapshotController::MaybeStartSnapshotThenStop,
-            weak_ptr_factory_.GetWeakPtr()),
-        base::TimeDelta::FromMilliseconds(
-            delay_after_renovations_completed_ms_));
-  }
 }
 
 void BackgroundSnapshotController::DocumentOnLoadCompletedInMainFrame() {
-  if (renovations_enabled_) {
-    // Run renovations. After renovations complete, a snapshot will be
-    // triggered after a delay.
-    client_->RunRenovations();
-  } else {
     // Post a delayed task to snapshot and then stop this controller.
     task_runner_->PostDelayedTask(
         FROM_HERE,
@@ -81,7 +67,6 @@
             weak_ptr_factory_.GetWeakPtr()),
         base::TimeDelta::FromMilliseconds(
             delay_after_document_on_load_completed_ms_));
-  }
 }
 
 void BackgroundSnapshotController::MaybeStartSnapshot() {
diff --git a/components/offline_pages/core/background_snapshot_controller.h b/components/offline_pages/core/background_snapshot_controller.h
index cdf0836b..d46ec9c29e 100644
--- a/components/offline_pages/core/background_snapshot_controller.h
+++ b/components/offline_pages/core/background_snapshot_controller.h
@@ -47,11 +47,6 @@
     // Invoked at a good moment to start a snapshot.
     virtual void StartSnapshot() = 0;
 
-    // Invoked when the page is sufficiently loaded for running
-    // renovations. The client should call the RenovationsCompleted()
-    // when they finish.
-    virtual void RunRenovations() = 0;
-
    protected:
     virtual ~Client() {}
   };
@@ -90,7 +85,6 @@
   BackgroundSnapshotController::State state_;
   int64_t delay_after_document_on_load_completed_ms_;
   int64_t delay_after_renovations_completed_ms_;
-  bool renovations_enabled_;
 
   base::WeakPtrFactory<BackgroundSnapshotController> weak_ptr_factory_{this};
 
diff --git a/components/offline_pages/core/background_snapshot_controller_unittest.cc b/components/offline_pages/core/background_snapshot_controller_unittest.cc
index 91c579b..f30b829 100644
--- a/components/offline_pages/core/background_snapshot_controller_unittest.cc
+++ b/components/offline_pages/core/background_snapshot_controller_unittest.cc
@@ -30,7 +30,6 @@
 
   // BackgroundSnapshotController::Client
   void StartSnapshot() override;
-  void RunRenovations() override;
 
   // Utility methods.
   // Runs until all of the tasks that are not delayed are gone from the task
@@ -68,10 +67,6 @@
   snapshot_count_++;
 }
 
-void BackgroundSnapshotControllerTest::RunRenovations() {
-  controller_->RenovationsCompleted();
-}
-
 void BackgroundSnapshotControllerTest::PumpLoop() {
   task_runner_->RunUntilIdle();
 }
diff --git a/components/offline_pages/core/offline_page_feature.cc b/components/offline_pages/core/offline_page_feature.cc
index 27591b9..38a3858 100644
--- a/components/offline_pages/core/offline_page_feature.cc
+++ b/components/offline_pages/core/offline_page_feature.cc
@@ -29,15 +29,6 @@
 const base::Feature kOfflinePagesLivePageSharingFeature{
     "OfflinePagesLivePageSharing", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kOfflinePagesLoadSignalCollectingFeature{
-    "OfflinePagesLoadSignalCollecting", base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kOfflinePagesRenovationsFeature{
-    "OfflinePagesRenovations", base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kOfflinePagesResourceBasedSnapshotFeature{
-    "OfflinePagesResourceBasedSnapshot", base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kPrefetchingOfflinePagesFeature{
     "OfflinePagesPrefetching", base::FEATURE_ENABLED_BY_DEFAULT};
 
@@ -86,19 +77,6 @@
   return base::FeatureList::IsEnabled(kPrefetchingOfflinePagesFeature);
 }
 
-bool IsOfflinePagesLoadSignalCollectingEnabled() {
-  return base::FeatureList::IsEnabled(kOfflinePagesLoadSignalCollectingFeature);
-}
-
-bool IsOfflinePagesRenovationsEnabled() {
-  return base::FeatureList::IsEnabled(kOfflinePagesRenovationsFeature);
-}
-
-bool IsOfflinePagesResourceBasedSnapshotEnabled() {
-  return base::FeatureList::IsEnabled(
-      kOfflinePagesResourceBasedSnapshotFeature);
-}
-
 bool ShouldUseTestingSnapshotDelay() {
   base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
   return cl->HasSwitch(kOfflinePagesUseTestingSnapshotDelay);
diff --git a/components/offline_pages/core/offline_page_feature.h b/components/offline_pages/core/offline_page_feature.h
index adf9ab7..6295a79b 100644
--- a/components/offline_pages/core/offline_page_feature.h
+++ b/components/offline_pages/core/offline_page_feature.h
@@ -15,10 +15,7 @@
 extern const base::Feature kOfflinePagesLivePageSharingFeature;
 extern const base::Feature kBackgroundLoaderForDownloadsFeature;
 extern const base::Feature kPrefetchingOfflinePagesFeature;
-extern const base::Feature kOfflinePagesLoadSignalCollectingFeature;
 extern const base::Feature kOfflinePagesCTV2Feature;
-extern const base::Feature kOfflinePagesRenovationsFeature;
-extern const base::Feature kOfflinePagesResourceBasedSnapshotFeature;
 extern const base::Feature kOfflinePagesDescriptivePendingStatusFeature;
 extern const base::Feature kOfflinePagesInDownloadHomeOpenInCctFeature;
 extern const base::Feature kOfflinePagesDescriptiveFailStatusFeature;
@@ -45,18 +42,6 @@
 // Returns true if prefetching offline pages is enabled.
 bool IsPrefetchingOfflinePagesEnabled();
 
-// Returns true if we enable load timing signals to be collected.
-bool IsOfflinePagesLoadSignalCollectingEnabled();
-
-// Returns true if we should use the "page renovation" framework in
-// the BackgroundLoaderOffliner.
-bool IsOfflinePagesRenovationsEnabled();
-
-// Returns true if we should use the "Resource percentage signal" for taking
-// snapshots instead of a time delay after the document is loaded in the main
-// frame.
-bool IsOfflinePagesResourceBasedSnapshotEnabled();
-
 // Returns true if a command line for test has been set that shortens the
 // snapshot delay.
 bool ShouldUseTestingSnapshotDelay();
diff --git a/components/offline_pages/core/offline_page_feature_unittest.cc b/components/offline_pages/core/offline_page_feature_unittest.cc
index cdaf1f0..35362d268 100644
--- a/components/offline_pages/core/offline_page_feature_unittest.cc
+++ b/components/offline_pages/core/offline_page_feature_unittest.cc
@@ -31,17 +31,6 @@
   EXPECT_TRUE(offline_pages::IsOfflinePagesLivePageSharingEnabled());
 }
 
-TEST(OfflinePageFeatureTest, OfflinePagesLoadSignalCollecting) {
-  // Disabled by default.
-  EXPECT_FALSE(offline_pages::IsOfflinePagesLoadSignalCollectingEnabled());
-
-  // Check if helper method works correctly when the features is enabled.
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      kOfflinePagesLoadSignalCollectingFeature);
-  EXPECT_TRUE(offline_pages::IsOfflinePagesLoadSignalCollectingEnabled());
-}
-
 TEST(OfflinePageFeatureTest, OfflinePagesPrefetching) {
   // Enabled by default.
   EXPECT_TRUE(offline_pages::IsPrefetchingOfflinePagesEnabled());
diff --git a/components/offline_pages/core/renovations/BUILD.gn b/components/offline_pages/core/renovations/BUILD.gn
deleted file mode 100644
index f7712136..0000000
--- a/components/offline_pages/core/renovations/BUILD.gn
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-if (is_android) {
-  import("//build/config/android/rules.gni")
-}
-
-static_library("renovations") {
-  sources = [
-    "page_renovation.h",
-    "page_renovation_loader.cc",
-    "page_renovation_loader.h",
-    "page_renovator.cc",
-    "page_renovator.h",
-    "script_injector.h",
-  ]
-
-  deps = [
-    "//base",
-    "//components/offline_pages/core",
-    "//components/resources:components_resources",
-    "//ui/base",
-    "//url",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "page_renovation_loader_unittest.cc",
-    "page_renovator_unittest.cc",
-  ]
-
-  deps = [
-    ":renovations",
-    "//base",
-    "//base/test:test_support",
-    "//testing/gmock",
-    "//testing/gtest",
-    "//url",
-  ]
-}
diff --git a/components/offline_pages/core/renovations/DEPS b/components/offline_pages/core/renovations/DEPS
deleted file mode 100644
index 65f9de2..0000000
--- a/components/offline_pages/core/renovations/DEPS
+++ /dev/null
@@ -1,4 +0,0 @@
-include_rules = [
-  "+components/grit/components_resources.h",
-  "+ui/base/resource",
-]
diff --git a/components/offline_pages/core/renovations/page_renovation.h b/components/offline_pages/core/renovations/page_renovation.h
deleted file mode 100644
index 6a8696f70..0000000
--- a/components/offline_pages/core/renovations/page_renovation.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PAGE_RENOVATION_H_
-#define COMPONENTS_OFFLINE_PAGES_CORE_PAGE_RENOVATION_H_
-
-#include <string>
-
-#include "url/gurl.h"
-
-namespace offline_pages {
-
-// Objects implementing this interface represent individual renovations
-// that can be run in a page pre-snapshot.
-class PageRenovation {
- public:
-  virtual ~PageRenovation() {}
-
-  // Returns |true| if this renovation should run in the page from |url|.
-  virtual bool ShouldRun(const GURL& url) const = 0;
-  // Returns an ID that identifies this renovation's script. This ID
-  // can be passed to the PageRenovationLoader.
-  virtual std::string GetID() const = 0;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_CORE_PAGE_RENOVATION_H_
diff --git a/components/offline_pages/core/renovations/page_renovation_loader.cc b/components/offline_pages/core/renovations/page_renovation_loader.cc
deleted file mode 100644
index 5e766f4..0000000
--- a/components/offline_pages/core/renovations/page_renovation_loader.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/core/renovations/page_renovation_loader.h"
-
-#include <algorithm>
-#include <string>
-#include <utility>
-
-#include "base/check_op.h"
-#include "base/strings/utf_string_conversions.h"
-#include "components/grit/components_resources.h"
-#include "ui/base/resource/resource_bundle.h"
-
-namespace offline_pages {
-
-namespace {
-
-// Helper function used in WikipediaPageRenovation::ShouldRun.
-bool EndsWith(const std::string& host, const std::string& suffix) {
-  if (suffix.size() > host.size())
-    return false;
-
-  return std::equal(suffix.rbegin(), suffix.rend(), host.rbegin());
-}
-
-// Concrete PageRenovation instances
-class WikipediaPageRenovation : public PageRenovation {
- public:
-  bool ShouldRun(const GURL& url) const override {
-    return EndsWith(url.host(), "m.wikipedia.org");
-  }
-
-  std::string GetID() const override { return "wikipedia"; }
-};
-
-// Construct list of implemented renovations
-std::vector<std::unique_ptr<PageRenovation>> MakeRenovationList() {
-  std::vector<std::unique_ptr<PageRenovation>> list;
-  list.emplace_back(new WikipediaPageRenovation);
-  return list;
-}
-
-}  // namespace
-
-PageRenovationLoader::PageRenovationLoader()
-    : renovations_(MakeRenovationList()), is_loaded_(false) {}
-
-PageRenovationLoader::~PageRenovationLoader() {}
-
-bool PageRenovationLoader::GetRenovationScript(
-    const std::vector<std::string>& renovation_ids,
-    base::string16* script) {
-  if (!LoadSource()) {
-    return false;
-  }
-
-  // The loaded script contains a function called run_renovations
-  // which takes an array of renovation names and runs all the
-  // associated renovation code. Create call to run_renovations
-  // function in JavaScript file.
-  std::string jsCall = "\nrun_renovations([";
-  for (const std::string& renovation_id : renovation_ids) {
-    jsCall += "\"";
-    jsCall += renovation_id;
-    jsCall += "\",";
-  }
-  jsCall += "]);";
-
-  // Append run_renovations call to combined_source_, which contains
-  // the definition of run_renovations.
-  *script = combined_source_;
-  *script += base::UTF8ToUTF16(jsCall);
-
-  return true;
-}
-
-bool PageRenovationLoader::LoadSource() {
-  // We only need to load the script from storage once.
-  if (is_loaded_) {
-    return true;
-  }
-
-  // Our script file is stored in the resource bundle. Get this script.
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  combined_source_ = base::UTF8ToUTF16(
-      rb.LoadDataResourceString(IDR_OFFLINE_PAGES_RENOVATIONS_JS));
-
-  // If built correctly, IDR_OFFLINE_PAGES_RENOVATIONS_JS should
-  // always exist in the resource pack and loading should never fail.
-  DCHECK_GT(combined_source_.size(), 0U);
-
-  is_loaded_ = true;
-  return true;
-}
-
-void PageRenovationLoader::SetSourceForTest(base::string16 combined_source) {
-  combined_source_ = std::move(combined_source);
-  is_loaded_ = true;
-}
-
-void PageRenovationLoader::SetRenovationsForTest(
-    std::vector<std::unique_ptr<PageRenovation>> renovations) {
-  renovations_ = std::move(renovations);
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/core/renovations/page_renovation_loader.h b/components/offline_pages/core/renovations/page_renovation_loader.h
deleted file mode 100644
index bfdfd9dc..0000000
--- a/components/offline_pages/core/renovations/page_renovation_loader.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_CORE_RENOVATIONS_PAGE_RENOVATION_LOADER_H_
-#define COMPONENTS_OFFLINE_PAGES_CORE_RENOVATIONS_PAGE_RENOVATION_LOADER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "components/offline_pages/core/renovations/page_renovation.h"
-
-namespace offline_pages {
-
-// Class for preparing page renovation scripts. Handles loading
-// JavaScript from storage and creating script to run particular
-// renovations.
-class PageRenovationLoader {
- public:
-  PageRenovationLoader();
-  ~PageRenovationLoader();
-
-  // Takes a list of renovation IDs and outputs a script to be run in
-  // page. Returns whether loading was successful. The script is a
-  // string16 to match the rest of Chrome.
-  bool GetRenovationScript(const std::vector<std::string>& renovation_ids,
-                           base::string16* script);
-
-  // Returns the list of known renovations.
-  const std::vector<std::unique_ptr<PageRenovation>>& renovations() {
-    return renovations_;
-  }
-
-  // Methods for testing.
-  void SetSourceForTest(base::string16 combined_source);
-  void SetRenovationsForTest(
-      std::vector<std::unique_ptr<PageRenovation>> renovations);
-
- private:
-  // Called to load JavaScript source from storage.
-  bool LoadSource();
-
-  // List of registered page renovations
-  std::vector<std::unique_ptr<PageRenovation>> renovations_;
-
-  // Whether JavaScript source has been loaded.
-  bool is_loaded_;
-  // Contains JavaScript source.
-  base::string16 combined_source_;
-
-  DISALLOW_COPY_AND_ASSIGN(PageRenovationLoader);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_CORE_RENOVATIONS_PAGE_RENOVATION_LOADER_H_
diff --git a/components/offline_pages/core/renovations/page_renovation_loader_unittest.cc b/components/offline_pages/core/renovations/page_renovation_loader_unittest.cc
deleted file mode 100644
index 66ad606..0000000
--- a/components/offline_pages/core/renovations/page_renovation_loader_unittest.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/core/renovations/page_renovation_loader.h"
-
-#include <memory>
-
-#include "base/strings/string16.h"
-#include "base/strings/utf_string_conversions.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-namespace {
-const char kTestRenovationKey1[] = "abc_renovation";
-const char kTestRenovationKey2[] = "xyz_renovation";
-const char kTestRenovationScript[] =
-    "\nrun_renovations([\"abc_renovation\",\"xyz_renovation\",]);";
-}  // namespace
-
-class PageRenovationLoaderTest : public testing::Test {
- public:
-  void SetUp() override;
-  void TearDown() override;
-
- protected:
-  std::unique_ptr<PageRenovationLoader> page_renovation_loader_;
-};
-
-void PageRenovationLoaderTest::SetUp() {
-  page_renovation_loader_.reset(new PageRenovationLoader);
-  page_renovation_loader_->SetSourceForTest(base::string16());
-}
-
-void PageRenovationLoaderTest::TearDown() {
-  page_renovation_loader_.reset();
-}
-
-TEST_F(PageRenovationLoaderTest, NoRenovations) {
-  std::vector<std::string> renovation_keys;
-  // Pass empty list
-  base::string16 script;
-  EXPECT_TRUE(
-      page_renovation_loader_->GetRenovationScript(renovation_keys, &script));
-
-  EXPECT_EQ(script, base::UTF8ToUTF16("\nrun_renovations([]);"));
-}
-
-TEST_F(PageRenovationLoaderTest, TestRenovations) {
-  std::vector<std::string> renovation_keys;
-  renovation_keys.push_back(kTestRenovationKey1);
-  renovation_keys.push_back(kTestRenovationKey2);
-
-  base::string16 script;
-  EXPECT_TRUE(
-      page_renovation_loader_->GetRenovationScript(renovation_keys, &script));
-
-  EXPECT_EQ(script, base::UTF8ToUTF16(kTestRenovationScript));
-}
-
-TEST_F(PageRenovationLoaderTest, ReturnsSameScript) {
-  std::vector<std::string> renovation_keys;
-  renovation_keys.push_back(kTestRenovationKey1);
-  renovation_keys.push_back(kTestRenovationKey2);
-
-  base::string16 script1, script2;
-  EXPECT_TRUE(
-      page_renovation_loader_->GetRenovationScript(renovation_keys, &script1));
-  EXPECT_TRUE(
-      page_renovation_loader_->GetRenovationScript(renovation_keys, &script2));
-
-  EXPECT_EQ(script1, script2);
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/core/renovations/page_renovator.cc b/components/offline_pages/core/renovations/page_renovator.cc
deleted file mode 100644
index 95219330..0000000
--- a/components/offline_pages/core/renovations/page_renovator.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/core/renovations/page_renovator.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/check.h"
-#include "base/values.h"
-
-namespace offline_pages {
-
-PageRenovator::PageRenovator(PageRenovationLoader* renovation_loader,
-                             std::unique_ptr<ScriptInjector> script_injector,
-                             const GURL& request_url)
-    : renovation_loader_(renovation_loader),
-      script_injector_(std::move(script_injector)) {
-  DCHECK(renovation_loader);
-
-  PrepareScript(request_url);
-}
-
-PageRenovator::~PageRenovator() {}
-
-void PageRenovator::RunRenovations(CompletionCallback callback) {
-  // Prepare callback and inject combined script.
-  base::OnceCallback<void(base::Value)> cb = base::BindOnce(
-      [](base::OnceClosure callback, base::Value) {
-        if (callback)
-          std::move(callback).Run();
-      },
-      std::move(callback));
-
-  script_injector_->Inject(script_, std::move(cb));
-}
-
-void PageRenovator::PrepareScript(const GURL& url) {
-  std::vector<std::string> renovation_keys;
-
-  // Pick which renovations to run.
-  for (const std::unique_ptr<PageRenovation>& renovation :
-       renovation_loader_->renovations()) {
-    if (renovation->ShouldRun(url)) {
-      renovation_keys.push_back(renovation->GetID());
-    }
-  }
-
-  // Store combined renovation script. TODO(crbug.com/736933): handle
-  // failed GetRenovationScript call.
-  renovation_loader_->GetRenovationScript(renovation_keys, &script_);
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/core/renovations/page_renovator.h b/components/offline_pages/core/renovations/page_renovator.h
deleted file mode 100644
index f26137a..0000000
--- a/components/offline_pages/core/renovations/page_renovator.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_CORE_RENOVATIONS_PAGE_RENOVATOR_H_
-#define COMPONENTS_OFFLINE_PAGES_CORE_RENOVATIONS_PAGE_RENOVATOR_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "base/strings/string16.h"
-#include "base/time/time.h"
-#include "components/offline_pages/core/renovations/page_renovation_loader.h"
-#include "components/offline_pages/core/renovations/script_injector.h"
-#include "components/offline_pages/core/snapshot_controller.h"
-
-namespace offline_pages {
-
-// This class runs renovations in a page being offlined. Upon
-// construction, it determines which renovations to run. The user
-// should then call RunRenovations when the page is sufficiently
-// loaded. When complete (if ever) the passed CompletionCallback will
-// be called.
-class PageRenovator {
- public:
-  using CompletionCallback = base::OnceClosure;
-
-  // |renovation_loader| should be owned by the user and is expected to
-  // outlive this PageRenovator instance.
-  PageRenovator(PageRenovationLoader* renovation_loader,
-                std::unique_ptr<ScriptInjector> script_injector,
-                const GURL& request_url);
-  ~PageRenovator();
-
-  // Run renovation scripts and call callback when they complete.
-  void RunRenovations(CompletionCallback callback);
-
- private:
-  // This method is used in the constructor to determine which
-  // renovations to run and populate |script_| with the renovations.
-  void PrepareScript(const GURL& url);
-
-  PageRenovationLoader* renovation_loader_;
-  std::unique_ptr<ScriptInjector> script_injector_;
-  base::string16 script_;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_CORE_RENOVATIONS_PAGE_RENOVATOR_H_
diff --git a/components/offline_pages/core/renovations/page_renovator_unittest.cc b/components/offline_pages/core/renovations/page_renovator_unittest.cc
deleted file mode 100644
index 78502c4..0000000
--- a/components/offline_pages/core/renovations/page_renovator_unittest.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/core/renovations/page_renovator.h"
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/test/mock_callback.h"
-#include "base/values.h"
-#include "components/offline_pages/core/renovations/script_injector.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace offline_pages {
-
-class PageRenovatorTest : public testing::Test {
- public:
-  PageRenovatorTest();
-  ~PageRenovatorTest() override;
-
- protected:
-  // ScriptInjector for testing PageRenovator. When Inject is called,
-  // it sets |script_injector_inject_called_| in the fixture to true,
-  // then calls the callback.
-  class FakeScriptInjector : public ScriptInjector {
-   public:
-    FakeScriptInjector(PageRenovatorTest* fixture);
-    ~FakeScriptInjector() override = default;
-
-    void Inject(base::string16 script, ResultCallback callback) override;
-
-   private:
-    PageRenovatorTest* fixture_;
-  };
-
-  // Creates a PageRenovator with simple defaults for testing.
-  void CreatePageRenovator(const GURL& url);
-
-  PageRenovationLoader page_renovation_loader_;
-  bool script_injector_inject_called_ = false;
-
-  // PageRenovator under test.
-  std::unique_ptr<PageRenovator> page_renovator_;
-};
-
-PageRenovatorTest::FakeScriptInjector::FakeScriptInjector(
-    PageRenovatorTest* fixture)
-    : fixture_(fixture) {}
-
-void PageRenovatorTest::FakeScriptInjector::Inject(base::string16 script,
-                                                   ResultCallback callback) {
-  if (callback)
-    std::move(callback).Run(base::Value());
-  fixture_->script_injector_inject_called_ = true;
-}
-
-PageRenovatorTest::PageRenovatorTest() {
-  // Set PageRenovationLoader to have empty script and Renovation list.
-  page_renovation_loader_.SetSourceForTest(base::string16());
-  page_renovation_loader_.SetRenovationsForTest(
-      std::vector<std::unique_ptr<PageRenovation>>());
-
-  page_renovator_ = std::make_unique<PageRenovator>(
-      &page_renovation_loader_, std::make_unique<FakeScriptInjector>(this),
-      GURL("example.com"));
-}
-
-PageRenovatorTest::~PageRenovatorTest() {}
-
-TEST_F(PageRenovatorTest, InjectsScript) {
-  EXPECT_FALSE(script_injector_inject_called_);
-  page_renovator_->RunRenovations(base::NullCallback());
-  EXPECT_TRUE(script_injector_inject_called_);
-}
-
-TEST_F(PageRenovatorTest, CallsCallback) {
-  base::MockCallback<base::OnceClosure> mock_callback;
-  EXPECT_CALL(mock_callback, Run()).Times(1);
-
-  page_renovator_->RunRenovations(mock_callback.Get());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/core/renovations/script_injector.h b/components/offline_pages/core/renovations/script_injector.h
deleted file mode 100644
index 5f628b4..0000000
--- a/components/offline_pages/core/renovations/script_injector.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_CORE_RENOVATIONS_SCRIPT_INJECTOR_H_
-#define COMPONENTS_OFFLINE_PAGES_CORE_RENOVATIONS_SCRIPT_INJECTOR_H_
-
-#include "base/callback_forward.h"
-#include "base/strings/string16.h"
-
-namespace base {
-class Value;
-}  // namespace base
-
-namespace offline_pages {
-
-// Interface for injecting and running scripts in some
-// context. Inject() takes a string of JavaScript, injects it into the
-// context, then calls the ResultCallback with the expression value.
-class ScriptInjector {
- public:
-  using ResultCallback = base::OnceCallback<void(base::Value)>;
-
-  virtual ~ScriptInjector() = default;
-
-  // Takes a string of JavaScript, injects it into the context, then
-  // calls the ResultCallback with the expression value.
-  virtual void Inject(base::string16 script, ResultCallback callback) = 0;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_CORE_RENOVATIONS_SCRIPT_INJECTOR_H_
diff --git a/components/omnibox/browser/autocomplete_input.cc b/components/omnibox/browser/autocomplete_input.cc
index 9f4fe574..13a4872 100644
--- a/components/omnibox/browser/autocomplete_input.cc
+++ b/components/omnibox/browser/autocomplete_input.cc
@@ -153,7 +153,9 @@
   if (should_use_https_as_default_scheme_ &&
       type_ == metrics::OmniboxInputType::URL &&
       scheme_ == base::ASCIIToUTF16(url::kHttpScheme) &&
-      !base::StartsWith(text_, scheme_, base::CompareCase::INSENSITIVE_ASCII)) {
+      !base::StartsWith(text_, scheme_, base::CompareCase::INSENSITIVE_ASCII) &&
+      !url::HostIsIPAddress(base::UTF16ToUTF8(text)) &&
+      !net::IsHostnameNonUnique(base::UTF16ToUTF8(text))) {
     // Use HTTPS as the default scheme for URLs that are typed without a scheme.
     // Inputs of type UNKNOWN can still be valid URLs, but these will be mainly
     // intranet hosts which we don't to upgrade to HTTPS so we only check the
diff --git a/components/omnibox/browser/autocomplete_input_unittest.cc b/components/omnibox/browser/autocomplete_input_unittest.cc
index cd973b6..46a9bf33 100644
--- a/components/omnibox/browser/autocomplete_input_unittest.cc
+++ b/components/omnibox/browser/autocomplete_input_unittest.cc
@@ -384,6 +384,10 @@
       {ASCIIToUTF16("example.com"), GURL("https://example.com"), true},
       // Non-URL inputs shouldn't be upgraded.
       {ASCIIToUTF16("example query"), GURL(), false},
+      // IP addresses shouldn't be upgraded.
+      {ASCIIToUTF16("127.0.0.1"), GURL("http://127.0.0.1"), false},
+      // Non-unique hostnames shouldn't be upgraded.
+      {ASCIIToUTF16("site.test"), GURL("http://site.test"), false},
       // Fully typed URLs shouldn't be upgraded.
       {ASCIIToUTF16("http://example.com"), GURL("http://example.com"), false},
       {ASCIIToUTF16("HTTP://EXAMPLE.COM"), GURL("http://example.com"), false},
diff --git a/components/optimization_guide/core/BUILD.gn b/components/optimization_guide/core/BUILD.gn
index ddd71c2b..1f274dce 100644
--- a/components/optimization_guide/core/BUILD.gn
+++ b/components/optimization_guide/core/BUILD.gn
@@ -36,9 +36,6 @@
     "optimization_guide_features.h",
     "optimization_guide_prefs.cc",
     "optimization_guide_prefs.h",
-    "optimization_guide_service.cc",
-    "optimization_guide_service.h",
-    "optimization_guide_service_observer.h",
     "optimization_guide_session_statistic.cc",
     "optimization_guide_session_statistic.h",
     "optimization_guide_store.cc",
@@ -47,6 +44,9 @@
     "optimization_guide_switches.h",
     "optimization_guide_util.cc",
     "optimization_guide_util.h",
+    "optimization_hints_component_observer.h",
+    "optimization_hints_component_update_listener.cc",
+    "optimization_hints_component_update_listener.h",
     "optimization_metadata.cc",
     "optimization_metadata.h",
     "optimization_target_model_observer.h",
@@ -95,7 +95,6 @@
     "//base",
     "//components/leveldb_proto",
     "//components/optimization_guide/proto:optimization_guide_proto",
-    "//content/public/browser",
     "//testing/gtest",
   ]
 }
@@ -112,10 +111,10 @@
     "hints_processing_util_unittest.cc",
     "optimization_filter_unittest.cc",
     "optimization_guide_features_unittest.cc",
-    "optimization_guide_service_unittest.cc",
     "optimization_guide_session_statistic_unittest.cc",
     "optimization_guide_store_unittest.cc",
     "optimization_guide_switches_unittest.cc",
+    "optimization_hints_component_update_listener_unittest.cc",
     "optimization_metadata_unittest.cc",
     "prediction_model_fetcher_unittest.cc",
     "prediction_model_unittest.cc",
diff --git a/components/optimization_guide/core/optimization_guide_service.cc b/components/optimization_guide/core/optimization_guide_service.cc
deleted file mode 100644
index 1b546d7..0000000
--- a/components/optimization_guide/core/optimization_guide_service.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/optimization_guide/core/optimization_guide_service.h"
-
-#include "base/bind.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/task/post_task.h"
-
-namespace optimization_guide {
-
-OptimizationGuideService::OptimizationGuideService(
-    const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread_task_runner)
-    : ui_thread_task_runner_(ui_thread_task_runner) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-}
-
-OptimizationGuideService::~OptimizationGuideService() {}
-
-void OptimizationGuideService::AddObserver(
-    OptimizationGuideServiceObserver* observer) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  observers_.AddObserver(observer);
-  if (hints_component_info_) {
-    observer->OnHintsComponentAvailable(*hints_component_info_);
-  }
-}
-
-void OptimizationGuideService::RemoveObserver(
-    OptimizationGuideServiceObserver* observer) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  observers_.RemoveObserver(observer);
-}
-
-void OptimizationGuideService::MaybeUpdateHintsComponent(
-    const HintsComponentInfo& info) {
-  ui_thread_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &OptimizationGuideService::MaybeUpdateHintsComponentOnUIThread,
-          weak_ptr_factory_.GetWeakPtr(), info));
-}
-
-void OptimizationGuideService::MaybeUpdateHintsComponentOnUIThread(
-    const HintsComponentInfo& info) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(info.version.IsValid());
-  DCHECK(!info.path.empty());
-
-  // Do not update the component if the version isn't newer. This differs from
-  // the check in ComponentInstaller::InstallHelper(), because this rejects
-  // version equality, whereas InstallHelper() accepts it.
-  if (hints_component_info_ &&
-      hints_component_info_->version.CompareTo(info.version) >= 0) {
-    return;
-  }
-
-  base::UmaHistogramSparse(
-      "OptimizationGuide.OptimizationHintsComponent.MajorVersion",
-      info.version.components()[0]);
-
-  hints_component_info_.emplace(info.version, info.path);
-  for (auto& observer : observers_) {
-    observer.OnHintsComponentAvailable(*hints_component_info_);
-  }
-}
-
-}  // namespace optimization_guide
diff --git a/components/optimization_guide/core/optimization_guide_service.h b/components/optimization_guide/core/optimization_guide_service.h
deleted file mode 100644
index b4bf92d1..0000000
--- a/components/optimization_guide/core/optimization_guide_service.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_GUIDE_SERVICE_H_
-#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_GUIDE_SERVICE_H_
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
-#include "base/optional.h"
-#include "base/sequence_checker.h"
-#include "base/single_thread_task_runner.h"
-#include "components/optimization_guide/core/hints_component_info.h"
-#include "components/optimization_guide/core/optimization_guide_service_observer.h"
-
-namespace optimization_guide {
-
-// Tracks the info for the current Optimization Hints component and notifies
-// observers of newly available hints components downloaded from the Component
-// Updater.
-class OptimizationGuideService {
- public:
-  explicit OptimizationGuideService(
-      const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread_task_runner);
-
-  virtual ~OptimizationGuideService();
-
-  // Adds the observer and synchronously dispatches the current
-  // HintsComponentInfo to it if one is already available.
-  //
-  // Virtual so it can be mocked out in tests.
-  virtual void AddObserver(OptimizationGuideServiceObserver* observer);
-  // Virtual so it can be mocked out in tests.
-  virtual void RemoveObserver(OptimizationGuideServiceObserver* observer);
-
-  // Forwards the update hints component request on to the UI thread, where the
-  // actual work occurs.
-  //
-  // Virtual so it can be mocked out in tests.
-  virtual void MaybeUpdateHintsComponent(const HintsComponentInfo& info);
-
-  // If the hints component version in |info| is greater than that in
-  // |hints_component_info_|, updates |hints_component_info_| and dispatches it
-  // to all observers. In the case where the version is not greater, it does
-  // nothing.
-  void MaybeUpdateHintsComponentOnUIThread(const HintsComponentInfo& info);
-
- private:
-  // Runner for indexing tasks.
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  // Runner for UI Thread tasks.
-  scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner_;
-
-  // Observers receiving notifications on hints being processed.
-  base::ObserverList<OptimizationGuideServiceObserver>::Unchecked observers_;
-
-  // The current HintsComponentInfo available to observers. This is unset until
-  // the first time MaybeUpdateHintsComponent() is called.
-  base::Optional<HintsComponentInfo> hints_component_info_;
-
-  // Used to get |weak_ptr_| to self.
-  base::WeakPtrFactory<OptimizationGuideService> weak_ptr_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(OptimizationGuideService);
-};
-
-}  // namespace optimization_guide
-
-#endif  // COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_GUIDE_SERVICE_H_
diff --git a/components/optimization_guide/core/optimization_guide_service_observer.h b/components/optimization_guide/core/optimization_guide_service_observer.h
deleted file mode 100644
index 72b06d9..0000000
--- a/components/optimization_guide/core/optimization_guide_service_observer.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_GUIDE_SERVICE_OBSERVER_H_
-#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_GUIDE_SERVICE_OBSERVER_H_
-
-namespace optimization_guide {
-
-struct HintsComponentInfo;
-
-// Interface for objects that wish to be notified of changes in the Optimization
-// Guide Service.
-//
-// All calls will be made on the UI thread.
-class OptimizationGuideServiceObserver {
- public:
-  // Called when a new hints component is available for processing. While this
-  // is called on the UI thread, it is recommended that processing of the new
-  // component via ProcessHintsComponent() occur on a background thread.
-  virtual void OnHintsComponentAvailable(const HintsComponentInfo& info) = 0;
-
- protected:
-  virtual ~OptimizationGuideServiceObserver() {}
-};
-
-}  // namespace optimization_guide
-
-#endif  // COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_GUIDE_SERVICE_OBSERVER_H_
diff --git a/components/optimization_guide/core/optimization_hints_component_observer.h b/components/optimization_guide/core/optimization_hints_component_observer.h
new file mode 100644
index 0000000..5394b74
--- /dev/null
+++ b/components/optimization_guide/core/optimization_hints_component_observer.h
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_HINTS_COMPONENT_OBSERVER_H_
+#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_HINTS_COMPONENT_OBSERVER_H_
+
+namespace optimization_guide {
+
+struct HintsComponentInfo;
+
+// Interface for objects that wish to be notified of changes to the Optimization
+// Hints Component.
+//
+// All calls will be made on the UI thread.
+class OptimizationHintsComponentObserver {
+ public:
+  // Called when a new hints component is available for processing. While this
+  // is called on the UI thread, it is recommended that processing of the new
+  // component occur on a background thread.
+  virtual void OnHintsComponentAvailable(const HintsComponentInfo& info) = 0;
+
+ protected:
+  virtual ~OptimizationHintsComponentObserver() = default;
+};
+
+}  // namespace optimization_guide
+
+#endif  // COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_HINTS_COMPONENT_OBSERVER_H_
diff --git a/components/optimization_guide/core/optimization_hints_component_update_listener.cc b/components/optimization_guide/core/optimization_hints_component_update_listener.cc
new file mode 100644
index 0000000..3c2b04d1
--- /dev/null
+++ b/components/optimization_guide/core/optimization_hints_component_update_listener.cc
@@ -0,0 +1,67 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/optimization_guide/core/optimization_hints_component_update_listener.h"
+
+#include "base/metrics/histogram_functions.h"
+
+namespace optimization_guide {
+
+// static
+OptimizationHintsComponentUpdateListener*
+OptimizationHintsComponentUpdateListener::GetInstance() {
+  static base::NoDestructor<OptimizationHintsComponentUpdateListener> service;
+  return service.get();
+}
+
+OptimizationHintsComponentUpdateListener::
+    OptimizationHintsComponentUpdateListener() = default;
+OptimizationHintsComponentUpdateListener::
+    ~OptimizationHintsComponentUpdateListener() = default;
+
+void OptimizationHintsComponentUpdateListener::AddObserver(
+    OptimizationHintsComponentObserver* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  observers_.AddObserver(observer);
+
+  if (hints_component_info_) {
+    observer->OnHintsComponentAvailable(*hints_component_info_);
+  }
+}
+
+void OptimizationHintsComponentUpdateListener::RemoveObserver(
+    OptimizationHintsComponentObserver* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  observers_.RemoveObserver(observer);
+}
+
+void OptimizationHintsComponentUpdateListener::MaybeUpdateHintsComponent(
+    const HintsComponentInfo& info) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(info.version.IsValid());
+  DCHECK(!info.path.empty());
+
+  // Do not update the component if the version isn't newer. This differs from
+  // the check in ComponentInstaller::InstallHelper(), because this rejects
+  // version equality, whereas InstallHelper() accepts it.
+  if (hints_component_info_ &&
+      hints_component_info_->version.CompareTo(info.version) >= 0) {
+    return;
+  }
+
+  base::UmaHistogramSparse(
+      "OptimizationGuide.OptimizationHintsComponent.MajorVersion",
+      info.version.components()[0]);
+
+  hints_component_info_.emplace(info.version, info.path);
+  for (auto& observer : observers_) {
+    observer.OnHintsComponentAvailable(*hints_component_info_);
+  }
+}
+
+void OptimizationHintsComponentUpdateListener::ResetStateForTesting() {
+  hints_component_info_ = base::nullopt;
+}
+
+}  // namespace optimization_guide
diff --git a/components/optimization_guide/core/optimization_hints_component_update_listener.h b/components/optimization_guide/core/optimization_hints_component_update_listener.h
new file mode 100644
index 0000000..0cf4be6
--- /dev/null
+++ b/components/optimization_guide/core/optimization_hints_component_update_listener.h
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_HINTS_COMPONENT_UPDATE_LISTENER_H_
+#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_HINTS_COMPONENT_UPDATE_LISTENER_H_
+
+#include "base/no_destructor.h"
+#include "base/observer_list.h"
+#include "base/optional.h"
+#include "base/sequence_checker.h"
+#include "components/optimization_guide/core/hints_component_info.h"
+#include "components/optimization_guide/core/optimization_hints_component_observer.h"
+
+namespace optimization_guide {
+
+// Tracks the info for the current Optimization Hints component and notifies
+// observers of newly available hints components downloaded from the Component
+// Updater.
+class OptimizationHintsComponentUpdateListener {
+ public:
+  static OptimizationHintsComponentUpdateListener* GetInstance();
+
+  OptimizationHintsComponentUpdateListener(
+      const OptimizationHintsComponentUpdateListener&) = delete;
+  OptimizationHintsComponentUpdateListener& operator=(
+      const OptimizationHintsComponentUpdateListener&) = delete;
+
+  // Adds the observer and synchronously dispatches the current
+  // HintsComponentInfo to it if one is already available.
+  //
+  // Virtual so it can be mocked out in tests.
+  virtual void AddObserver(OptimizationHintsComponentObserver* observer);
+  // Virtual so it can be mocked out in tests.
+  virtual void RemoveObserver(OptimizationHintsComponentObserver* observer);
+
+  // If the hints component version in |info| is greater than that in
+  // |hints_component_info_|, updates |hints_component_info_| and dispatches it
+  // to all observers. In the case where the version is not greater, it does
+  // nothing.
+  void MaybeUpdateHintsComponent(const HintsComponentInfo& info);
+
+  // Currently received HintsComponentInfo.
+  base::Optional<HintsComponentInfo> hints_component_info() {
+    return hints_component_info_;
+  }
+
+ private:
+  OptimizationHintsComponentUpdateListener();
+  ~OptimizationHintsComponentUpdateListener();
+
+  friend class base::NoDestructor<OptimizationHintsComponentUpdateListener>;
+  friend class OptimizationHintsComponentUpdateListenerTest;
+
+  void ResetStateForTesting();
+
+  // Runner for indexing tasks.
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  // Observers receiving notifications on hints being processed.
+  base::ObserverList<OptimizationHintsComponentObserver>::Unchecked observers_;
+
+  // The current HintsComponentInfo available to observers. This is unset until
+  // the first time MaybeUpdateHintsComponent() is called.
+  base::Optional<HintsComponentInfo> hints_component_info_;
+};
+
+}  // namespace optimization_guide
+
+#endif  // COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_HINTS_COMPONENT_UPDATE_LISTENER_H_
diff --git a/components/optimization_guide/core/optimization_guide_service_unittest.cc b/components/optimization_guide/core/optimization_hints_component_update_listener_unittest.cc
similarity index 82%
rename from components/optimization_guide/core/optimization_guide_service_unittest.cc
rename to components/optimization_guide/core/optimization_hints_component_update_listener_unittest.cc
index 6e00e68..50a4bf3 100644
--- a/components/optimization_guide/core/optimization_guide_service_unittest.cc
+++ b/components/optimization_guide/core/optimization_hints_component_update_listener_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/optimization_guide/core/optimization_guide_service.h"
+#include "components/optimization_guide/core/optimization_hints_component_update_listener.h"
 
 #include <memory>
 #include <string>
@@ -11,6 +11,7 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
@@ -23,7 +24,7 @@
 const base::FilePath::CharType kFileName1[] = FILE_PATH_LITERAL("somefile1.pb");
 const base::FilePath::CharType kFileName2[] = FILE_PATH_LITERAL("somefile2.pb");
 
-class TestObserver : public OptimizationGuideServiceObserver {
+class TestObserver : public OptimizationHintsComponentObserver {
  public:
   TestObserver()
       : hints_component_notification_count_(0),
@@ -55,34 +56,37 @@
   DISALLOW_COPY_AND_ASSIGN(TestObserver);
 };
 
-class OptimizationGuideServiceTest : public testing::Test {
+class OptimizationHintsComponentUpdateListenerTest : public testing::Test {
  public:
-  OptimizationGuideServiceTest() {}
-
-  ~OptimizationGuideServiceTest() override {}
+  OptimizationHintsComponentUpdateListenerTest() = default;
+  ~OptimizationHintsComponentUpdateListenerTest() override = default;
 
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    optimization_guide_service_ = std::make_unique<OptimizationGuideService>(
-        task_environment_.GetMainThreadTaskRunner());
+
+    OptimizationHintsComponentUpdateListener::GetInstance()
+        ->ResetStateForTesting();
 
     observer_ = std::make_unique<TestObserver>();
   }
 
-  OptimizationGuideService* optimization_guide_service() {
-    return optimization_guide_service_.get();
-  }
+  void TearDown() override { RemoveObserver(); }
 
   TestObserver* observer() { return observer_.get(); }
 
-  void AddObserver() { optimization_guide_service_->AddObserver(observer()); }
+  void AddObserver() {
+    OptimizationHintsComponentUpdateListener::GetInstance()->AddObserver(
+        observer());
+  }
 
   void RemoveObserver() {
-    optimization_guide_service_->RemoveObserver(observer());
+    OptimizationHintsComponentUpdateListener::GetInstance()->RemoveObserver(
+        observer());
   }
 
   void MaybeUpdateHintsComponent(const HintsComponentInfo& info) {
-    optimization_guide_service_->MaybeUpdateHintsComponent(info);
+    OptimizationHintsComponentUpdateListener::GetInstance()
+        ->MaybeUpdateHintsComponent(info);
     task_environment_.RunUntilIdle();
     base::RunLoop().RunUntilIdle();
   }
@@ -93,13 +97,13 @@
   base::test::TaskEnvironment task_environment_;
   base::ScopedTempDir temp_dir_;
 
-  std::unique_ptr<OptimizationGuideService> optimization_guide_service_;
   std::unique_ptr<TestObserver> observer_;
 
-  DISALLOW_COPY_AND_ASSIGN(OptimizationGuideServiceTest);
+  DISALLOW_COPY_AND_ASSIGN(OptimizationHintsComponentUpdateListenerTest);
 };
 
-TEST_F(OptimizationGuideServiceTest, ProcessHintsIssuesNotification) {
+TEST_F(OptimizationHintsComponentUpdateListenerTest,
+       ProcessHintsIssuesNotification) {
   base::HistogramTester histogram_tester;
 
   AddObserver();
@@ -116,7 +120,8 @@
       "OptimizationGuide.OptimizationHintsComponent.MajorVersion", 1, 1);
 }
 
-TEST_F(OptimizationGuideServiceTest, ProcessHintsNewVersionProcessed) {
+TEST_F(OptimizationHintsComponentUpdateListenerTest,
+       ProcessHintsNewVersionProcessed) {
   base::HistogramTester histogram_tester;
 
   AddObserver();
@@ -141,7 +146,8 @@
       "OptimizationGuide.OptimizationHintsComponent.MajorVersion", 2, 1);
 }
 
-TEST_F(OptimizationGuideServiceTest, ProcessHintsPastVersionIgnored) {
+TEST_F(OptimizationHintsComponentUpdateListenerTest,
+       ProcessHintsPastVersionIgnored) {
   base::HistogramTester histogram_tester;
 
   AddObserver();
@@ -163,7 +169,8 @@
       "OptimizationGuide.OptimizationHintsComponent.MajorVersion", 2, 1);
 }
 
-TEST_F(OptimizationGuideServiceTest, ProcessHintsSameVersionIgnored) {
+TEST_F(OptimizationHintsComponentUpdateListenerTest,
+       ProcessHintsSameVersionIgnored) {
   base::HistogramTester histogram_tester;
 
   AddObserver();
@@ -183,7 +190,7 @@
       "OptimizationGuide.OptimizationHintsComponent.MajorVersion", 2, 1);
 }
 
-TEST_F(OptimizationGuideServiceTest,
+TEST_F(OptimizationHintsComponentUpdateListenerTest,
        UnregisteredObserverDoesNotReceiveNotification) {
   base::HistogramTester histogram_tester;
 
@@ -203,7 +210,7 @@
       "OptimizationGuide.OptimizationHintsComponent.MajorVersion", 1, 1);
 }
 
-TEST_F(OptimizationGuideServiceTest,
+TEST_F(OptimizationHintsComponentUpdateListenerTest,
        RegisteredObserverReceivesNotificationForCurrentComponent) {
   base::HistogramTester histogram_tester;
 
diff --git a/components/optimization_guide/core/test_hints_component_creator.cc b/components/optimization_guide/core/test_hints_component_creator.cc
index 359d5a9..46ca36dd 100644
--- a/components/optimization_guide/core/test_hints_component_creator.cc
+++ b/components/optimization_guide/core/test_hints_component_creator.cc
@@ -12,6 +12,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/version.h"
 #include "components/optimization_guide/core/bloom_filter.h"
+#include "components/optimization_guide/core/optimization_hints_component_update_listener.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
diff --git a/components/optimization_guide/core/test_hints_component_creator.h b/components/optimization_guide/core/test_hints_component_creator.h
index 8f24b9f..97a3141 100644
--- a/components/optimization_guide/core/test_hints_component_creator.h
+++ b/components/optimization_guide/core/test_hints_component_creator.h
@@ -11,10 +11,12 @@
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
-#include "components/optimization_guide/core/optimization_guide_service.h"
 #include "components/optimization_guide/proto/hints.pb.h"
 
 namespace optimization_guide {
+
+struct HintsComponentInfo;
+
 namespace testing {
 
 // Helper class to create test OptimizationHints components for testing.
diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java
index 3865d538..20bb219e 100644
--- a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java
+++ b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java
@@ -13,6 +13,7 @@
 import org.chromium.base.BuildInfo;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.components.content_settings.ContentSettingValues;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
@@ -52,6 +53,10 @@
     private PermissionDialogDelegate mDialogDelegate;
     private ModalDialogManager mModalDialogManager;
 
+    // Array with ints of type {@link ContentSettingsType}.
+    private int[] mLastPermissions;
+    private @ContentSettingValues int mLastResult;
+
     // As the PermissionRequestManager handles queueing for a tab and only shows prompts for active
     // tabs, we typically only have one request. This class only handles multiple requests at once
     // when either:
@@ -90,6 +95,22 @@
     }
 
     /**
+     * Returns whether this PropertyModel is for the permission dialog.
+     * @param model The PropertyModel to be checked.
+     */
+    public static boolean isPermissionDialogModel(PropertyModel model) {
+        return model.get(ModalDialogProperties.CONTROLLER) instanceof PermissionDialogController;
+    }
+
+    public int[] getLastDialogPermissions() {
+        return mLastPermissions.clone();
+    }
+
+    public @ContentSettingValues int getLastDialogResult() {
+        return mLastResult;
+    }
+
+    /**
      * Queues a modal permission dialog for display. If there are currently no dialogs on screen, it
      * will be displayed immediately. Otherwise, it will be displayed as soon as the user responds
      * to the current dialog.
@@ -115,7 +136,7 @@
             mState = State.NOT_SHOWING;
         } else {
             mDialogDelegate.onAccept();
-            destroyDelegate();
+            destroyDelegate(ContentSettingValues.ALLOW);
         }
         scheduleDisplay();
     }
@@ -129,7 +150,7 @@
             mState = State.NOT_SHOWING;
         } else {
             mDialogDelegate.onDismiss();
-            destroyDelegate();
+            destroyDelegate(ContentSettingValues.BLOCK);
         }
         scheduleDisplay();
     }
@@ -152,7 +173,7 @@
             // TODO(timloh): This probably doesn't work, as this happens synchronously when creating
             // the PermissionPromptAndroid, so the PermissionRequestManager won't be ready yet.
             mDialogDelegate.onDismiss();
-            destroyDelegate();
+            destroyDelegate(ContentSettingValues.DEFAULT);
             return;
         }
 
@@ -260,7 +281,7 @@
                 assert mState == State.PROMPT_OPEN;
                 mDialogDelegate.onDismiss();
             }
-            destroyDelegate();
+            destroyDelegate(ContentSettingValues.BLOCK);
             scheduleDisplay();
         }
     }
@@ -284,7 +305,11 @@
         }
     }
 
-    private void destroyDelegate() {
+    private void destroyDelegate(@ContentSettingValues int result) {
+        if (result != ContentSettingValues.DEFAULT) {
+            mLastPermissions = mDialogDelegate.getContentSettingsTypes();
+            mLastResult = result;
+        }
         mDialogDelegate.destroy();
         mDialogDelegate = null;
         mState = State.NOT_SHOWING;
diff --git a/components/policy/core/common/cloud/cloud_policy_client.cc b/components/policy/core/common/cloud/cloud_policy_client.cc
index 0d7f437..d363034f 100644
--- a/components/policy/core/common/cloud/cloud_policy_client.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client.cc
@@ -596,6 +596,7 @@
 
 void CloudPolicyClient::UploadSecurityEventReport(
     content::BrowserContext* context,
+    bool include_device_info,
     base::Value report,
     StatusCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -603,7 +604,7 @@
   CreateNewRealtimeReportingJob(
       std::move(report),
       service()->configuration()->GetReportingConnectorServerUrl(context),
-      add_connector_url_params_, std::move(callback));
+      include_device_info, add_connector_url_params_, std::move(callback));
 }
 
 void CloudPolicyClient::UploadEncryptedReport(
@@ -636,7 +637,8 @@
   app_install_report_request_job_ = CreateNewRealtimeReportingJob(
       std::move(report),
       service()->configuration()->GetRealtimeReportingServerUrl(),
-      /* add_connector_url_params=*/false, std::move(callback));
+      /* include_device_info */ true, /* add_connector_url_params=*/false,
+      std::move(callback));
   DCHECK(app_install_report_request_job_);
 }
 
@@ -657,6 +659,7 @@
   extension_install_report_request_job_ = CreateNewRealtimeReportingJob(
       std::move(report),
       service()->configuration()->GetRealtimeReportingServerUrl(),
+      /* include_device_info */ true,
       /* add_connector_url_params=*/false, std::move(callback));
   DCHECK(extension_install_report_request_job_);
 }
@@ -702,11 +705,12 @@
 DeviceManagementService::Job* CloudPolicyClient::CreateNewRealtimeReportingJob(
     base::Value report,
     const std::string& server_url,
+    bool include_device_info,
     bool add_connector_url_params,
     StatusCallback callback) {
   std::unique_ptr<RealtimeReportingJobConfiguration> config =
       std::make_unique<RealtimeReportingJobConfiguration>(
-          this, server_url, add_connector_url_params,
+          this, server_url, include_device_info, add_connector_url_params,
           base::BindOnce(&CloudPolicyClient::OnRealtimeReportUploadCompleted,
                          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 
diff --git a/components/policy/core/common/cloud/cloud_policy_client.h b/components/policy/core/common/cloud/cloud_policy_client.h
index c58f544e..ddc61135 100644
--- a/components/policy/core/common/cloud/cloud_policy_client.h
+++ b/components/policy/core/common/cloud/cloud_policy_client.h
@@ -313,8 +313,11 @@
 
   // Uploads a report containing enterprise connectors real-time security
   // events for |context|. As above, the client must be in a registered state.
-  // The |callback| will be called when the operation completes.
+  // If |include_device_info| is true, information specific to the device such
+  // as the device name, user, id and OS will be included in the report. The
+  // |callback| will be called when the operation completes.
   virtual void UploadSecurityEventReport(content::BrowserContext* context,
+                                         bool include_device_info,
                                          base::Value report,
                                          StatusCallback callback);
 
@@ -792,12 +795,15 @@
  private:
   // Creates a new real-time reporting job and appends it to |request_jobs_|.
   // The job will send its report to the |server_url| endpoint.  If
+  // |include_device_info| is true, information specific to the device such as
+  // the device name, user, id and OS will be included in the report. If
   // |add_connector_url_params| is true then URL paramaters specific to
   // enterprise connectors are added to the request uploading the report.
   // |callback| is invoked once the report is uploaded.
   DeviceManagementService::Job* CreateNewRealtimeReportingJob(
       base::Value report,
       const std::string& server_url,
+      bool include_device_info,
       bool add_connector_url_params,
       StatusCallback callback);
 
diff --git a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
index eb2b034..85e9b5d 100644
--- a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
@@ -1477,7 +1477,19 @@
 
 #if defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) || \
     defined(OS_CHROMEOS)
-TEST_F(CloudPolicyClientTest, UploadSecurityEventReport) {
+
+class CloudPolicyClientUploadSecurityEventTest
+    : public CloudPolicyClientTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  bool include_device_info() const { return GetParam(); }
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+                         CloudPolicyClientUploadSecurityEventTest,
+                         testing::Bool());
+
+TEST_P(CloudPolicyClientUploadSecurityEventTest, Test) {
   RegisterClient();
 
   ExpectAndCaptureJSONJob(/*response=*/"{}");
@@ -1486,7 +1498,8 @@
       base::BindOnce(&MockStatusCallbackObserver::OnCallbackComplete,
                      base::Unretained(&callback_observer_));
 
-  client_->UploadSecurityEventReport(nullptr, MakeDefaultRealtimeReport(),
+  client_->UploadSecurityEventReport(nullptr, include_device_info(),
+                                     MakeDefaultRealtimeReport(),
                                      std::move(callback));
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(
@@ -1498,33 +1511,54 @@
   base::Optional<base::Value> payload = base::JSONReader::Read(job_payload_);
   ASSERT_TRUE(payload);
 
-  EXPECT_EQ(kDMToken, *payload->FindStringPath(
-                          ReportingJobConfigurationBase::
-                              DeviceDictionaryBuilder::GetDMTokenPath()));
-  EXPECT_EQ(client_id_, *payload->FindStringPath(
-                            ReportingJobConfigurationBase::
-                                DeviceDictionaryBuilder::GetClientIdPath()));
-  EXPECT_EQ(policy::GetOSUsername(),
-            *payload->FindStringPath(
-                ReportingJobConfigurationBase::BrowserDictionaryBuilder::
-                    GetMachineUserPath()));
+  ASSERT_FALSE(policy::GetDeviceName().empty());
   EXPECT_EQ(version_info::GetVersionNumber(),
             *payload->FindStringPath(
                 ReportingJobConfigurationBase::BrowserDictionaryBuilder::
                     GetChromeVersionPath()));
-  EXPECT_EQ(policy::GetOSPlatform(),
-            *payload->FindStringPath(
-                ReportingJobConfigurationBase::DeviceDictionaryBuilder::
-                    GetOSPlatformPath()));
-  EXPECT_EQ(policy::GetOSVersion(),
-            *payload->FindStringPath(
-                ReportingJobConfigurationBase::DeviceDictionaryBuilder::
-                    GetOSVersionPath()));
-  EXPECT_FALSE(policy::GetDeviceName().empty());
-  EXPECT_EQ(
-      policy::GetDeviceName(),
-      *payload->FindStringPath(ReportingJobConfigurationBase::
-                                   DeviceDictionaryBuilder::GetNamePath()));
+
+  if (include_device_info()) {
+    EXPECT_EQ(kDMToken, *payload->FindStringPath(
+                            ReportingJobConfigurationBase::
+                                DeviceDictionaryBuilder::GetDMTokenPath()));
+    EXPECT_EQ(client_id_, *payload->FindStringPath(
+                              ReportingJobConfigurationBase::
+                                  DeviceDictionaryBuilder::GetClientIdPath()));
+    EXPECT_EQ(policy::GetOSUsername(),
+              *payload->FindStringPath(
+                  ReportingJobConfigurationBase::BrowserDictionaryBuilder::
+                      GetMachineUserPath()));
+    EXPECT_EQ(policy::GetOSPlatform(),
+              *payload->FindStringPath(
+                  ReportingJobConfigurationBase::DeviceDictionaryBuilder::
+                      GetOSPlatformPath()));
+    EXPECT_EQ(policy::GetOSVersion(),
+              *payload->FindStringPath(
+                  ReportingJobConfigurationBase::DeviceDictionaryBuilder::
+                      GetOSVersionPath()));
+    EXPECT_EQ(
+        policy::GetDeviceName(),
+        *payload->FindStringPath(ReportingJobConfigurationBase::
+                                     DeviceDictionaryBuilder::GetNamePath()));
+  } else {
+    EXPECT_FALSE(
+        payload->FindStringPath(ReportingJobConfigurationBase::
+                                    DeviceDictionaryBuilder::GetDMTokenPath()));
+    EXPECT_FALSE(payload->FindStringPath(
+        ReportingJobConfigurationBase::DeviceDictionaryBuilder::
+            GetClientIdPath()));
+    EXPECT_FALSE(payload->FindStringPath(
+        ReportingJobConfigurationBase::BrowserDictionaryBuilder::
+            GetMachineUserPath()));
+    EXPECT_FALSE(payload->FindStringPath(
+        ReportingJobConfigurationBase::DeviceDictionaryBuilder::
+            GetOSPlatformPath()));
+    EXPECT_FALSE(payload->FindStringPath(
+        ReportingJobConfigurationBase::DeviceDictionaryBuilder::
+            GetOSVersionPath()));
+    EXPECT_FALSE(payload->FindStringPath(
+        ReportingJobConfigurationBase::DeviceDictionaryBuilder::GetNamePath()));
+  }
 
   base::Value* events =
       payload->FindPath(RealtimeReportingJobConfiguration::kEventListKey);
@@ -1535,7 +1569,7 @@
 TEST_F(CloudPolicyClientTest, RealtimeReportMerge) {
   auto config = std::make_unique<RealtimeReportingJobConfiguration>(
       client_.get(), service_.configuration()->GetRealtimeReportingServerUrl(),
-      /*add_connector_url_params=*/false,
+      /*include_device_info*/ true, /*add_connector_url_params=*/false,
       RealtimeReportingJobConfiguration::UploadCompleteCallback());
 
   // Add one report to the config.
diff --git a/components/policy/core/common/cloud/encrypted_reporting_job_configuration.cc b/components/policy/core/common/cloud/encrypted_reporting_job_configuration.cc
index 2668f5c..69062241 100644
--- a/components/policy/core/common/cloud/encrypted_reporting_job_configuration.cc
+++ b/components/policy/core/common/cloud/encrypted_reporting_job_configuration.cc
@@ -31,6 +31,7 @@
                                     client->GetURLLoaderFactory(),
                                     client,
                                     server_url,
+                                    /*include_device_info*/ true,
                                     std::move(complete_cb)) {
   // Merge it into the base class payload.
   payload_.MergeDictionary(&merging_payload);
diff --git a/components/policy/core/common/cloud/mock_cloud_policy_client.h b/components/policy/core/common/cloud/mock_cloud_policy_client.h
index f730be1..1c62296 100644
--- a/components/policy/core/common/cloud/mock_cloud_policy_client.h
+++ b/components/policy/core/common/cloud/mock_cloud_policy_client.h
@@ -101,12 +101,14 @@
                     StatusCallback&));
 
   void UploadSecurityEventReport(content::BrowserContext* context,
+                                 bool include_device_info,
                                  base::Value value,
                                  StatusCallback callback) override {
-    UploadSecurityEventReport_(context, value, callback);
+    UploadSecurityEventReport_(context, include_device_info, value, callback);
   }
-  MOCK_METHOD3(UploadSecurityEventReport_,
+  MOCK_METHOD4(UploadSecurityEventReport_,
                void(content::BrowserContext* context,
+                    bool include_device_info,
                     base::Value&,
                     StatusCallback&));
 
diff --git a/components/policy/core/common/cloud/realtime_reporting_job_configuration.cc b/components/policy/core/common/cloud/realtime_reporting_job_configuration.cc
index 8f15111..e2926254 100644
--- a/components/policy/core/common/cloud/realtime_reporting_job_configuration.cc
+++ b/components/policy/core/common/cloud/realtime_reporting_job_configuration.cc
@@ -42,12 +42,14 @@
 RealtimeReportingJobConfiguration::RealtimeReportingJobConfiguration(
     CloudPolicyClient* client,
     const std::string& server_url,
+    bool include_device_info,
     bool add_connector_url_params,
     UploadCompleteCallback callback)
     : ReportingJobConfigurationBase(TYPE_UPLOAD_REAL_TIME_REPORT,
                                     client->GetURLLoaderFactory(),
                                     client,
                                     server_url,
+                                    include_device_info,
                                     std::move(callback)) {
   InitializePayloadInternal(client, add_connector_url_params);
 }
diff --git a/components/policy/core/common/cloud/realtime_reporting_job_configuration.h b/components/policy/core/common/cloud/realtime_reporting_job_configuration.h
index d15e0ae..89fa973 100644
--- a/components/policy/core/common/cloud/realtime_reporting_job_configuration.h
+++ b/components/policy/core/common/cloud/realtime_reporting_job_configuration.h
@@ -46,6 +46,7 @@
   // parameters will be used.
   RealtimeReportingJobConfiguration(CloudPolicyClient* client,
                                     const std::string& server_url,
+                                    bool include_device_info,
                                     bool add_connector_url_params,
                                     UploadCompleteCallback callback);
 
diff --git a/components/policy/core/common/cloud/realtime_reporting_job_configuration_unittest.cc b/components/policy/core/common/cloud/realtime_reporting_job_configuration_unittest.cc
index 036ff5d..9022f4d 100644
--- a/components/policy/core/common/cloud/realtime_reporting_job_configuration_unittest.cc
+++ b/components/policy/core/common/cloud/realtime_reporting_job_configuration_unittest.cc
@@ -79,7 +79,7 @@
     client_.SetDMToken(kDummyToken);
     configuration_ = std::make_unique<RealtimeReportingJobConfiguration>(
         &client_, service_.configuration()->GetRealtimeReportingServerUrl(),
-        /*add_connector_url_params=*/false,
+        /*include_device_info=*/true, /*add_connector_url_params=*/false,
         base::BindOnce(&MockCallbackObserver::OnURLLoadComplete,
                        base::Unretained(&callback_observer_)));
     base::Value context(base::Value::Type::DICTIONARY);
diff --git a/components/policy/core/common/cloud/reporting_job_configuration_base.cc b/components/policy/core/common/cloud/reporting_job_configuration_base.cc
index e37bde82..8e97e95b 100644
--- a/components/policy/core/common/cloud/reporting_job_configuration_base.cc
+++ b/components/policy/core/common/cloud/reporting_job_configuration_base.cc
@@ -112,8 +112,9 @@
         "chromeVersion";
 
 // static
-base::Value ReportingJobConfigurationBase::BrowserDictionaryBuilder::
-    BuildBrowserDictionary() {
+base::Value
+ReportingJobConfigurationBase::BrowserDictionaryBuilder::BuildBrowserDictionary(
+    bool include_device_info) {
   base::Value browser_dictionary{base::Value::Type::DICTIONARY};
 
   base::FilePath browser_id;
@@ -121,7 +122,9 @@
     browser_dictionary.SetStringKey(kBrowserId, browser_id.value());
   }
 
-  browser_dictionary.SetStringKey(kMachineUser, GetOSUsername());
+  if (include_device_info)
+    browser_dictionary.SetStringKey(kMachineUser, GetOSUsername());
+
   browser_dictionary.SetStringKey(kChromeVersion,
                                   version_info::GetVersionNumber());
   return browser_dictionary;
@@ -265,6 +268,7 @@
     scoped_refptr<network::SharedURLLoaderFactory> factory,
     CloudPolicyClient* client,
     const std::string& server_url,
+    bool include_device_info,
     UploadCompleteCallback callback)
     : JobConfigurationBase(type,
                            DMAuth::FromDMToken(client->dm_token()),
@@ -274,20 +278,24 @@
       callback_(std::move(callback)),
       server_url_(server_url) {
   DCHECK(GetAuth().has_dm_token());
-  InitializePayload(client);
+  InitializePayload(client, include_device_info);
 }
 
 ReportingJobConfigurationBase::~ReportingJobConfigurationBase() = default;
 
 void ReportingJobConfigurationBase::InitializePayload(
-    CloudPolicyClient* client) {
+    CloudPolicyClient* client,
+    bool include_device_info) {
   AddParameter("key", google_apis::GetAPIKey());
 
-  payload_.SetKey(DeviceDictionaryBuilder::kDeviceKey,
-                  DeviceDictionaryBuilder::BuildDeviceDictionary(
-                      client->dm_token(), client->client_id()));
-  payload_.SetKey(BrowserDictionaryBuilder::kBrowserKey,
-                  BrowserDictionaryBuilder::BuildBrowserDictionary());
+  if (include_device_info) {
+    payload_.SetKey(DeviceDictionaryBuilder::kDeviceKey,
+                    DeviceDictionaryBuilder::BuildDeviceDictionary(
+                        client->dm_token(), client->client_id()));
+  }
+  payload_.SetKey(
+      BrowserDictionaryBuilder::kBrowserKey,
+      BrowserDictionaryBuilder::BuildBrowserDictionary(include_device_info));
 }
 
 }  // namespace policy
diff --git a/components/policy/core/common/cloud/reporting_job_configuration_base.h b/components/policy/core/common/cloud/reporting_job_configuration_base.h
index fbe67a0..8f4ad29 100644
--- a/components/policy/core/common/cloud/reporting_job_configuration_base.h
+++ b/components/policy/core/common/cloud/reporting_job_configuration_base.h
@@ -81,7 +81,7 @@
     // Dictionary Key Name
     static const char kBrowserKey[];
 
-    static base::Value BuildBrowserDictionary();
+    static base::Value BuildBrowserDictionary(bool include_device_info);
 
     static std::string GetBrowserIdPath();
     static std::string GetUserAgentPath();
@@ -120,6 +120,7 @@
       scoped_refptr<network::SharedURLLoaderFactory> factory,
       CloudPolicyClient* client,
       const std::string& server_url,
+      bool include_device_info,
       UploadCompleteCallback callback);
   ~ReportingJobConfigurationBase() override;
 
@@ -150,8 +151,10 @@
   UploadCompleteCallback callback_;
 
  private:
-  // Initializes request payload.
-  void InitializePayload(CloudPolicyClient* client);
+  // Initializes request payload. If |include_device_info| is false, the
+  // "device" and "browser.machineUser" fields (see comment at the top of the
+  // file) are excluded from the payload.
+  void InitializePayload(CloudPolicyClient* client, bool include_device_info);
 
   const std::string server_url_;
 
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index cf783a3..06efd3af 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -2965,7 +2965,7 @@
     USER_TYPE_REGULAR = 1;
     USER_TYPE_GUEST = 2;
     USER_TYPE_PUBLIC_ACCOUNT = 3;
-    USER_TYPE_SUPERVISED = 4;
+    USER_TYPE_SUPERVISED_DEPRECATED = 4;
     USER_TYPE_KIOSK_APP = 5;
     USER_TYPE_CHILD = 6;
     USER_TYPE_ARC_KIOSK_APP = 7;
diff --git a/components/safe_browsing/core/db/v4_database.cc b/components/safe_browsing/core/db/v4_database.cc
index 8a651e3b..55d96696 100644
--- a/components/safe_browsing/core/db/v4_database.cc
+++ b/components/safe_browsing/core/db/v4_database.cc
@@ -297,6 +297,15 @@
          store_pair->second->HasValidData();
 }
 
+int64_t V4Database::GetStoreSizeInBytes(
+    const ListIdentifier& identifier) const {
+  const auto& store_pair = store_map_->find(identifier);
+  if (store_pair == store_map_->end()) {
+    return 0;
+  }
+  return store_pair->second->file_size();
+}
+
 void V4Database::RecordFileSizeHistograms() {
   int64_t db_size = 0;
   for (const auto& store_map_iter : *store_map_) {
diff --git a/components/safe_browsing/core/db/v4_database.h b/components/safe_browsing/core/db/v4_database.h
index 77381771..cb920eb 100644
--- a/components/safe_browsing/core/db/v4_database.h
+++ b/components/safe_browsing/core/db/v4_database.h
@@ -147,6 +147,10 @@
       const StoresToCheck& stores_to_check,
       StoreAndHashPrefixes* matched_store_and_full_hashes);
 
+  // Returns the file size of the store in bytes. Returns 0 if the store is not
+  // found.
+  virtual int64_t GetStoreSizeInBytes(const ListIdentifier& store) const;
+
   // Resets the stores in |stores_to_reset| to an empty state. This is done if
   // the checksum doesn't match the expected value.
   void ResetStores(const std::vector<ListIdentifier>& stores_to_reset);
diff --git a/components/safe_browsing/core/db/v4_local_database_manager.cc b/components/safe_browsing/core/db/v4_local_database_manager.cc
index 5212275..1d7cf5d 100644
--- a/components/safe_browsing/core/db/v4_local_database_manager.cc
+++ b/components/safe_browsing/core/db/v4_local_database_manager.cc
@@ -35,6 +35,13 @@
 // The expiration time of the full hash stored in the artificial database.
 const int64_t kFullHashExpiryTimeInMinutes = 60;
 
+// The number of bytes in a full hash entry.
+const int64_t kBytesPerFullHashEntry = 32;
+
+// The minimum number of entries in the allowlist. If the actual size is
+// smaller than this number, the allowlist is considered as unavailable.
+const int kHighConfidenceAllowlistMinimumEntryCount = 100;
+
 const ThreatSeverity kLeastSeverity =
     std::numeric_limits<ThreatSeverity>::max();
 
@@ -418,14 +425,19 @@
   bool all_stores_available = AreAllStoresAvailableNow(stores_to_check);
   UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.RT.AllStoresAvailable",
                         all_stores_available);
-  if (!enabled_ || !CanCheckUrl(url) ||
-      (!all_stores_available &&
-       artificially_marked_store_and_hash_prefixes_.empty())) {
+  bool is_artificial_prefix_empty =
+      artificially_marked_store_and_hash_prefixes_.empty();
+  bool is_allowlist_too_small =
+      IsStoreTooSmall(GetUrlHighConfidenceAllowlistId(), kBytesPerFullHashEntry,
+                      kHighConfidenceAllowlistMinimumEntryCount);
+  if (!enabled_ || (is_allowlist_too_small && is_artificial_prefix_empty) ||
+      !CanCheckUrl(url) ||
+      (!all_stores_available && is_artificial_prefix_empty)) {
     // NOTE(vakh): If Safe Browsing isn't enabled yet, or if the URL isn't a
-    // navigation URL, or if the allowlist isn't ready yet, return MATCH.
-    // The full URL check won't be performed, but hash-based check will still
-    // be done. If any artificial matches are present, consider the allowlist
-    // as ready.
+    // navigation URL, or if the allowlist isn't ready yet, or if the allowlist
+    // is too small, return MATCH. The full URL check won't be performed, but
+    // hash-based check will still be done. If any artificial matches are
+    // present, consider the allowlist as ready.
     return AsyncMatch::MATCH;
   }
 
@@ -1030,6 +1042,20 @@
   return (result == StoreAvailabilityResult::AVAILABLE);
 }
 
+int64_t V4LocalDatabaseManager::GetStoreEntryCount(const ListIdentifier& store,
+                                                   int bytes_per_entry) const {
+  if (!enabled_ || !v4_database_) {
+    return 0;
+  }
+  return v4_database_->GetStoreSizeInBytes(store) / bytes_per_entry;
+}
+
+bool V4LocalDatabaseManager::IsStoreTooSmall(const ListIdentifier& store,
+                                             int bytes_per_entry,
+                                             int min_entry_count) const {
+  return GetStoreEntryCount(store, bytes_per_entry) < min_entry_count;
+}
+
 bool V4LocalDatabaseManager::AreAnyStoresAvailableNow(
     const StoresToCheck& stores_to_check) const {
   return enabled_ && v4_database_ &&
diff --git a/components/safe_browsing/core/db/v4_local_database_manager.h b/components/safe_browsing/core/db/v4_local_database_manager.h
index 0a37f20b..b55897e 100644
--- a/components/safe_browsing/core/db/v4_local_database_manager.h
+++ b/components/safe_browsing/core/db/v4_local_database_manager.h
@@ -329,6 +329,16 @@
   // these stores.
   bool AreAllStoresAvailableNow(const StoresToCheck& stores_to_check) const;
 
+  // Return the number of entries in the store. If the database isn't enabled or
+  // the database is not found, return 0.
+  int64_t GetStoreEntryCount(const ListIdentifier& store,
+                             int bytes_per_entry) const;
+
+  // Return whether the size of the store is smaller than expected.
+  bool IsStoreTooSmall(const ListIdentifier& store,
+                       int bytes_per_entry,
+                       int min_entry_count) const;
+
   // Return true if we're enabled and have loaded real data for any of
   // these stores.
   bool AreAnyStoresAvailableNow(const StoresToCheck& stores_to_check) const;
diff --git a/components/safe_browsing/core/db/v4_local_database_manager_unittest.cc b/components/safe_browsing/core/db/v4_local_database_manager_unittest.cc
index b8d8d75a..5534f937 100644
--- a/components/safe_browsing/core/db/v4_local_database_manager_unittest.cc
+++ b/components/safe_browsing/core/db/v4_local_database_manager_unittest.cc
@@ -44,6 +44,8 @@
   return full_hashes[0];
 }
 
+const int kDefaultStoreFileSizeInBytes = 320000;
+
 // Use this if you want GetFullHashes() to always return prescribed results.
 class FakeGetHashProtocolManager : public V4GetHashProtocolManager {
  public:
@@ -108,7 +110,8 @@
       std::unique_ptr<StoreMap> store_map,
       const StoreAndHashPrefixes& store_and_hash_prefixes,
       NewDatabaseReadyCallback new_db_callback,
-      bool stores_available) {
+      bool stores_available,
+      int64_t store_file_size) {
     // Mimics V4Database::Create
     const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner =
         base::ThreadTaskRunnerHandle::Get();
@@ -117,7 +120,7 @@
         base::BindOnce(&FakeV4Database::CreateOnTaskRunner, db_task_runner,
                        std::move(store_map), store_and_hash_prefixes,
                        callback_task_runner, std::move(new_db_callback),
-                       stores_available));
+                       stores_available, store_file_size));
   }
 
   // V4Database implementation
@@ -136,6 +139,11 @@
     }
   }
 
+  // V4Database implementation
+  int64_t GetStoreSizeInBytes(const ListIdentifier& store) const override {
+    return store_file_size_;
+  }
+
   bool AreAllStoresAvailable(
       const StoresToCheck& stores_to_check) const override {
     return stores_available_;
@@ -153,11 +161,12 @@
       const StoreAndHashPrefixes& store_and_hash_prefixes,
       const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner,
       NewDatabaseReadyCallback new_db_callback,
-      bool stores_available) {
+      bool stores_available,
+      int64_t store_file_size) {
     // Mimics the semantics of V4Database::CreateOnTaskRunner
-    std::unique_ptr<FakeV4Database> fake_v4_database(
-        new FakeV4Database(db_task_runner, std::move(store_map),
-                           store_and_hash_prefixes, stores_available));
+    std::unique_ptr<FakeV4Database> fake_v4_database(new FakeV4Database(
+        db_task_runner, std::move(store_map), store_and_hash_prefixes,
+        stores_available, store_file_size));
     callback_task_runner->PostTask(FROM_HERE,
                                    base::BindOnce(std::move(new_db_callback),
                                                   std::move(fake_v4_database)));
@@ -166,13 +175,16 @@
   FakeV4Database(const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
                  std::unique_ptr<StoreMap> store_map,
                  const StoreAndHashPrefixes& store_and_hash_prefixes,
-                 bool stores_available)
+                 bool stores_available,
+                 int64_t store_file_size)
       : V4Database(db_task_runner, std::move(store_map)),
         store_and_hash_prefixes_(store_and_hash_prefixes),
-        stores_available_(stores_available) {}
+        stores_available_(stores_available),
+        store_file_size_(store_file_size) {}
 
   const StoreAndHashPrefixes store_and_hash_prefixes_;
   const bool stores_available_;
+  int64_t store_file_size_;
 };
 
 // TODO(nparker): This might be simpler with a mock and EXPECT calls.
@@ -371,8 +383,10 @@
     v4_local_database_manager_->PopulateArtificialDatabase();
   }
 
-  void ReplaceV4Database(const StoreAndHashPrefixes& store_and_hash_prefixes,
-                         bool stores_available = false) {
+  void ReplaceV4Database(
+      const StoreAndHashPrefixes& store_and_hash_prefixes,
+      bool stores_available = false,
+      int64_t store_file_size = kDefaultStoreFileSizeInBytes) {
     // Disable the V4LocalDatabaseManager first so that if the callback to
     // verify checksum has been scheduled, then it doesn't do anything when it
     // is called back.
@@ -387,9 +401,9 @@
     NewDatabaseReadyCallback db_ready_callback =
         base::BindOnce(&V4LocalDatabaseManager::DatabaseReadyForChecks,
                        base::Unretained(v4_local_database_manager_.get()));
-    FakeV4Database::Create(task_runner_, std::make_unique<StoreMap>(),
-                           store_and_hash_prefixes,
-                           std::move(db_ready_callback), stores_available);
+    FakeV4Database::Create(
+        task_runner_, std::make_unique<StoreMap>(), store_and_hash_prefixes,
+        std::move(db_ready_callback), stores_available, store_file_size);
     WaitForTasksOnTaskRunner();
   }
 
@@ -664,7 +678,8 @@
   StoreAndHashPrefixes store_and_hash_prefixes;
   store_and_hash_prefixes.emplace_back(GetUrlHighConfidenceAllowlistId(),
                                        safe_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true,
+                    /* store_file_size= */ 10000);
 
   // Setup the allowlist client to verify the callback.
   TestAllowlistClient client(
@@ -706,7 +721,8 @@
   StoreAndHashPrefixes store_and_hash_prefixes;
   store_and_hash_prefixes.emplace_back(GetUrlHighConfidenceAllowlistId(),
                                        safe_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true,
+                    /* store_file_size= */ 100000);
 
   // Setup the allowlist client to verify the callback.
   TestAllowlistClient client(
@@ -745,7 +761,8 @@
   StoreAndHashPrefixes store_and_hash_prefixes;
   store_and_hash_prefixes.emplace_back(GetUrlHighConfidenceAllowlistId(),
                                        safe_full_hash);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true,
+                    /* store_file_size= */ 100000);
 
   // Setup the allowlist client to verify the callback isn't called.
   TestAllowlistClient client(
@@ -777,7 +794,8 @@
   // Add a full hash that won't match the URL we check.
   StoreAndHashPrefixes store_and_hash_prefixes;
   store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), safe_full_hash);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true,
+                    /* store_file_size= */ 100000);
 
   // Setup the allowlist client to verify the callback isn't called.
   TestAllowlistClient client(
@@ -804,7 +822,39 @@
 
   // Setup local database as unavailable.
   StoreAndHashPrefixes store_and_hash_prefixes;
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ false);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ false,
+                    /* store_file_size= */ 100000);
+
+  // Setup the allowlist client to verify the callback isn't called.
+  TestAllowlistClient client(
+      /* match_expected= */ false,
+      /* expected_sb_threat_type= */ SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST);
+
+  const GURL url_check("https://example.com/safe");
+  EXPECT_EQ(AsyncMatch::MATCH,
+            v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist(
+                url_check, &client));
+
+  WaitForTasksOnTaskRunner();
+  EXPECT_FALSE(client.callback_called());
+}
+
+// When allowlist is available but the size is too small, all URLS should be
+// considered MATCH.
+TEST_F(V4LocalDatabaseManagerTest, TestCheckUrlForHCAllowlistSmallSize) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures({safe_browsing::kRealTimeUrlLookupEnabled}, {});
+
+  // Setup to receive full-hash misses. We won't make URL requests.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  // Setup the size of the allowlist to be smaller than the threshold. (10
+  // entries)
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true,
+                    /* store_file_size= */ 32 * 10);
 
   // Setup the allowlist client to verify the callback isn't called.
   TestAllowlistClient client(
diff --git a/components/safe_browsing/core/db/v4_store.h b/components/safe_browsing/core/db/v4_store.h
index 86e1f46..670d01f 100644
--- a/components/safe_browsing/core/db/v4_store.h
+++ b/components/safe_browsing/core/db/v4_store.h
@@ -193,6 +193,8 @@
 
   const base::FilePath& store_path() const { return store_path_; }
 
+  int64_t file_size() const { return file_size_; }
+
   void ApplyUpdate(std::unique_ptr<ListUpdateResponse> response,
                    const scoped_refptr<base::SingleThreadTaskRunner>& runner,
                    UpdatedStoreReadyCallback callback);
diff --git a/components/safe_browsing/core/db/v4_test_util.cc b/components/safe_browsing/core/db/v4_test_util.cc
index 00ec5e1..8f208b61 100644
--- a/components/safe_browsing/core/db/v4_test_util.cc
+++ b/components/safe_browsing/core/db/v4_test_util.cc
@@ -20,6 +20,7 @@
 const char kClient[] = "unittest";
 const char kAppVer[] = "1.0";
 const char kKeyParam[] = "test_key_param";
+const int kDefaultStoreFileSizeInBytes = 320000;
 
 }  // namespace
 
@@ -74,6 +75,10 @@
   test_store->MarkPrefixAsBad(prefix);
 }
 
+int64_t TestV4Database::GetStoreSizeInBytes(const ListIdentifier& store) const {
+  return kDefaultStoreFileSizeInBytes;
+}
+
 TestV4StoreFactory::TestV4StoreFactory() = default;
 
 TestV4StoreFactory::~TestV4StoreFactory() = default;
diff --git a/components/safe_browsing/core/db/v4_test_util.h b/components/safe_browsing/core/db/v4_test_util.h
index d59145c..dcf025b7 100644
--- a/components/safe_browsing/core/db/v4_test_util.h
+++ b/components/safe_browsing/core/db/v4_test_util.h
@@ -59,6 +59,9 @@
                  std::unique_ptr<StoreMap> store_map);
 
   void MarkPrefixAsBad(ListIdentifier list_id, HashPrefix prefix);
+
+  // V4Database implementation
+  int64_t GetStoreSizeInBytes(const ListIdentifier& store) const override;
 };
 
 class TestV4DatabaseFactory : public V4DatabaseFactory {
diff --git a/components/search_engines/template_url.cc b/components/search_engines/template_url.cc
index c683c02..9ff237c 100644
--- a/components/search_engines/template_url.cc
+++ b/components/search_engines/template_url.cc
@@ -1365,6 +1365,8 @@
         !engine->safe_for_autoreplace(),
         // Prefer engines created by Play API.
         engine->created_from_play_api(),
+        // Favor prepopulated engines over other auto-generated engines.
+        engine->prepopulate_id() > 0,
         // More recently modified engines or created engines win.
         engine->last_modified(), engine->date_created(),
         // TODO(tommycli): This should be a tie-breaker than provides a total
diff --git a/components/search_engines/template_url_service.h b/components/search_engines/template_url_service.h
index 8f43e43e..8c8213c 100644
--- a/components/search_engines/template_url_service.h
+++ b/components/search_engines/template_url_service.h
@@ -459,7 +459,7 @@
   FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest, AddOmniboxExtensionKeyword);
   FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest, ExtensionsWithSameKeywords);
   FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest,
-                           CheckEnginesWithSameKeywords);
+                           KeywordConflictNonReplaceableEngines);
   FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest, LastVisitedTimeUpdate);
   FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest,
                            RepairPrepopulatedSearchEngines);
diff --git a/components/signin/internal/identity_manager/primary_account_manager.cc b/components/signin/internal/identity_manager/primary_account_manager.cc
index 7b95c04..5c3b9c12 100644
--- a/components/signin/internal/identity_manager/primary_account_manager.cc
+++ b/components/signin/internal/identity_manager/primary_account_manager.cc
@@ -47,8 +47,6 @@
 
 // static
 void PrimaryAccountManager::RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  registry->RegisterStringPref(prefs::kGoogleServicesHostedDomain,
-                               std::string());
   registry->RegisterStringPref(prefs::kGoogleServicesLastAccountId,
                                std::string());
   registry->RegisterStringPref(prefs::kGoogleServicesLastUsername,
@@ -336,7 +334,6 @@
   }
 
   PrimaryAccountChangeEvent::State previous_state = GetPrimaryAccountState();
-  client_->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain);
 
   // Revoke all tokens before sending signed_out notification, because there
   // may be components that don't listen for token service events when the
diff --git a/components/signin/public/base/signin_metrics.cc b/components/signin/public/base/signin_metrics.cc
index adcfc19..842139c7 100644
--- a/components/signin/public/base/signin_metrics.cc
+++ b/components/signin/public/base/signin_metrics.cc
@@ -893,10 +893,13 @@
       base::RecordAction(
           base::UserMetricsAction("Signin_Impression_FromKaleidoscope"));
       break;
+    case AccessPoint::ACCESS_POINT_USER_MANAGER:
+      base::RecordAction(
+          base::UserMetricsAction("Signin_Impression_FromUserManager"));
+      break;
     case AccessPoint::ACCESS_POINT_CONTENT_AREA:
     case AccessPoint::ACCESS_POINT_EXTENSIONS:
     case AccessPoint::ACCESS_POINT_SUPERVISED_USER:
-    case AccessPoint::ACCESS_POINT_USER_MANAGER:
     case AccessPoint::ACCESS_POINT_UNKNOWN:
     case AccessPoint::ACCESS_POINT_MACHINE_LOGON:
     case AccessPoint::ACCESS_POINT_SYNC_ERROR_CARD:
diff --git a/components/signin/public/base/signin_pref_names.cc b/components/signin/public/base/signin_pref_names.cc
index 3860caf..bba71b0 100644
--- a/components/signin/public/base/signin_pref_names.cc
+++ b/components/signin/public/base/signin_pref_names.cc
@@ -49,10 +49,6 @@
 const char kGoogleServicesConsentedToSync[] =
     "google.services.consented_to_sync";
 
-// The profile's hosted domain; empty if unset; kNoHostedDomainFound if there
-// is none.
-const char kGoogleServicesHostedDomain[] = "google.services.hosted_domain";
-
 // Similar to kGoogleServicesLastUsername, this is the corresponding version of
 // kGoogleServicesAccountId that is not cleared on signout.
 const char kGoogleServicesLastAccountId[] = "google.services.last_account_id";
diff --git a/components/signin/public/base/signin_pref_names.h b/components/signin/public/base/signin_pref_names.h
index 59015f7..3230e853 100644
--- a/components/signin/public/base/signin_pref_names.h
+++ b/components/signin/public/base/signin_pref_names.h
@@ -20,7 +20,6 @@
 extern const char kGaiaCookiePeriodicReportTime[];
 extern const char kGoogleServicesAccountId[];
 extern const char kGoogleServicesConsentedToSync[];
-extern const char kGoogleServicesHostedDomain[];
 extern const char kGoogleServicesLastAccountId[];
 extern const char kGoogleServicesLastUsername[];
 extern const char kGoogleServicesSigninScopedDeviceId[];
diff --git a/components/site_engagement/content/BUILD.gn b/components/site_engagement/content/BUILD.gn
index 8e82a6d..2f3dcf24 100644
--- a/components/site_engagement/content/BUILD.gn
+++ b/components/site_engagement/content/BUILD.gn
@@ -7,18 +7,42 @@
     "engagement_type.h",
     "site_engagement_metrics.cc",
     "site_engagement_metrics.h",
+    "site_engagement_observer.cc",
+    "site_engagement_observer.h",
     "site_engagement_score.cc",
     "site_engagement_score.h",
+    "site_engagement_service.cc",
+    "site_engagement_service.h",
   ]
+
   deps = [
     "//base",
+    "//components/browsing_data/core",
     "//components/content_settings/core/browser",
     "//components/content_settings/core/common",
+    "//components/permissions",
+    "//components/prefs",
     "//components/security_state/core",
+    "//components/site_engagement/core",
     "//components/site_engagement/core/mojom:mojo_bindings",
+    "//components/user_prefs",
     "//components/variations",
+    "//content/public/browser",
     "//third_party/blink/public/mojom:mojom_platform_headers",
+    "//url",
   ]
+
+  if (is_android) {
+    sources += [
+      "android/site_engagement_service_android.cc",
+      "android/site_engagement_service_android.h",
+    ]
+
+    deps += [
+      "//components/embedder_support/android:browser_context",
+      "//components/site_engagement/content/android:jni_headers",
+    ]
+  }
 }
 
 source_set("unit_tests") {
diff --git a/components/site_engagement/content/DEPS b/components/site_engagement/content/DEPS
index 7f2ff9e..4c2117ef 100644
--- a/components/site_engagement/content/DEPS
+++ b/components/site_engagement/content/DEPS
@@ -1,5 +1,12 @@
 include_rules = [
+  "+components/browsing_data",
   "+components/content_settings/core",
+  "+components/keyed_service",
+  "+components/permissions",
+  "+components/prefs",
+  "+components/user_prefs",
   "+components/variations",
+  "+content/public/browser",
   "+third_party/blink/public/mojom",
+  "+ui/base",
 ]
diff --git a/components/site_engagement/content/android/BUILD.gn b/components/site_engagement/content/android/BUILD.gn
new file mode 100644
index 0000000..76930b1
--- /dev/null
+++ b/components/site_engagement/content/android/BUILD.gn
@@ -0,0 +1,19 @@
+# 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("//build/config/android/rules.gni")
+
+android_library("java") {
+  sources = [ "java/src/org/chromium/components/site_engagement/SiteEngagementService.java" ]
+  deps = [
+    "//base:base_java",
+    "//base:jni_java",
+    "//components/embedder_support/android:browser_context_java",
+  ]
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+}
+
+generate_jni("jni_headers") {
+  sources = [ "java/src/org/chromium/components/site_engagement/SiteEngagementService.java" ]
+}
diff --git a/components/site_engagement/content/android/DEPS b/components/site_engagement/content/android/DEPS
new file mode 100644
index 0000000..735adf49
--- /dev/null
+++ b/components/site_engagement/content/android/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/embedder_support/android",
+]
diff --git a/chrome/browser/engagement/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java b/components/site_engagement/content/android/java/src/org/chromium/components/site_engagement/SiteEngagementService.java
similarity index 98%
rename from chrome/browser/engagement/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java
rename to components/site_engagement/content/android/java/src/org/chromium/components/site_engagement/SiteEngagementService.java
index b01b334..a95b87f 100644
--- a/chrome/browser/engagement/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java
+++ b/components/site_engagement/content/android/java/src/org/chromium/components/site_engagement/SiteEngagementService.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.engagement;
+package org.chromium.components.site_engagement;
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
diff --git a/chrome/browser/engagement/site_engagement_service_android.cc b/components/site_engagement/content/android/site_engagement_service_android.cc
similarity index 90%
rename from chrome/browser/engagement/site_engagement_service_android.cc
rename to components/site_engagement/content/android/site_engagement_service_android.cc
index e28b1c9..3d954ca 100644
--- a/chrome/browser/engagement/site_engagement_service_android.cc
+++ b/components/site_engagement/content/android/site_engagement_service_android.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/engagement/site_engagement_service_android.h"
+#include "components/site_engagement/content/android/site_engagement_service_android.h"
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
-#include "chrome/browser/engagement/android/jni_headers/SiteEngagementService_jni.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
 #include "components/embedder_support/android/browser_context/browser_context_handle.h"
+#include "components/site_engagement/content/android/jni_headers/SiteEngagementService_jni.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 #include "url/gurl.h"
 
 namespace site_engagement {
diff --git a/chrome/browser/engagement/site_engagement_service_android.h b/components/site_engagement/content/android/site_engagement_service_android.h
similarity index 86%
rename from chrome/browser/engagement/site_engagement_service_android.h
rename to components/site_engagement/content/android/site_engagement_service_android.h
index 491ef8e7..653594e6 100644
--- a/chrome/browser/engagement/site_engagement_service_android.h
+++ b/components/site_engagement/content/android/site_engagement_service_android.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_ANDROID_H_
-#define CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_ANDROID_H_
+#ifndef COMPONENTS_SITE_ENGAGEMENT_CONTENT_ANDROID_SITE_ENGAGEMENT_SERVICE_ANDROID_H_
+#define COMPONENTS_SITE_ENGAGEMENT_CONTENT_ANDROID_SITE_ENGAGEMENT_SERVICE_ANDROID_H_
 
 #include "base/android/scoped_java_ref.h"
 
@@ -49,4 +49,4 @@
 
 }  // namespace site_engagement
 
-#endif  // CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_ANDROID_H_
+#endif  // COMPONENTS_SITE_ENGAGEMENT_CONTENT_ANDROID_SITE_ENGAGEMENT_SERVICE_ANDROID_H_
diff --git a/chrome/browser/engagement/site_engagement_observer.cc b/components/site_engagement/content/site_engagement_observer.cc
similarity index 85%
rename from chrome/browser/engagement/site_engagement_observer.cc
rename to components/site_engagement/content/site_engagement_observer.cc
index c8ae53e..2e2a4e6b 100644
--- a/chrome/browser/engagement/site_engagement_observer.cc
+++ b/components/site_engagement/content/site_engagement_observer.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/engagement/site_engagement_observer.h"
+#include "components/site_engagement/content/site_engagement_observer.h"
 
-#include "chrome/browser/engagement/site_engagement_service.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 
 namespace site_engagement {
 
diff --git a/chrome/browser/engagement/site_engagement_observer.h b/components/site_engagement/content/site_engagement_observer.h
similarity index 88%
rename from chrome/browser/engagement/site_engagement_observer.h
rename to components/site_engagement/content/site_engagement_observer.h
index a5ff3c44..f4a1024 100644
--- a/chrome/browser/engagement/site_engagement_observer.h
+++ b/components/site_engagement/content/site_engagement_observer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_OBSERVER_H_
-#define CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_OBSERVER_H_
+#ifndef COMPONENTS_SITE_ENGAGEMENT_CONTENT_SITE_ENGAGEMENT_OBSERVER_H_
+#define COMPONENTS_SITE_ENGAGEMENT_CONTENT_SITE_ENGAGEMENT_OBSERVER_H_
 
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
@@ -56,4 +56,4 @@
 
 }  // namespace site_engagement
 
-#endif  // CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_OBSERVER_H_
+#endif  // COMPONENTS_SITE_ENGAGEMENT_CONTENT_SITE_ENGAGEMENT_OBSERVER_H_
diff --git a/chrome/browser/engagement/site_engagement_service.cc b/components/site_engagement/content/site_engagement_service.cc
similarity index 97%
rename from chrome/browser/engagement/site_engagement_service.cc
rename to components/site_engagement/content/site_engagement_service.cc
index e4fd43a..84b3583 100644
--- a/chrome/browser/engagement/site_engagement_service.cc
+++ b/components/site_engagement/content/site_engagement_service.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/engagement/site_engagement_service.h"
+#include "components/site_engagement/content/site_engagement_service.h"
 
 #include <stddef.h>
 
@@ -18,8 +18,6 @@
 #include "base/time/default_clock.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "chrome/browser/engagement/site_engagement_observer.h"
-#include "chrome/common/pref_names.h"
 #include "components/browsing_data/core/browsing_data_utils.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
@@ -27,7 +25,9 @@
 #include "components/prefs/pref_service.h"
 #include "components/site_engagement/content/engagement_type.h"
 #include "components/site_engagement/content/site_engagement_metrics.h"
+#include "components/site_engagement/content/site_engagement_observer.h"
 #include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/core/pref_names.h"
 #include "components/user_prefs/user_prefs.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -36,7 +36,7 @@
 #include "url/gurl.h"
 
 #if defined(OS_ANDROID)
-#include "chrome/browser/engagement/site_engagement_service_android.h"
+#include "components/site_engagement/content/android/site_engagement_service_android.h"
 #endif
 
 namespace site_engagement {
@@ -232,8 +232,8 @@
     observer.Observe(nullptr);
 }
 
-blink::mojom::EngagementLevel
-SiteEngagementService::GetEngagementLevel(const GURL& url) const {
+blink::mojom::EngagementLevel SiteEngagementService::GetEngagementLevel(
+    const GURL& url) const {
   if (IsLastEngagementStale())
     CleanupEngagementScores(true);
 
diff --git a/chrome/browser/engagement/site_engagement_service.h b/components/site_engagement/content/site_engagement_service.h
similarity index 97%
rename from chrome/browser/engagement/site_engagement_service.h
rename to components/site_engagement/content/site_engagement_service.h
index 143a7fb0..6dd38f34 100644
--- a/chrome/browser/engagement/site_engagement_service.h
+++ b/components/site_engagement/content/site_engagement_service.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_H_
-#define CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_H_
+#ifndef COMPONENTS_SITE_ENGAGEMENT_CONTENT_SITE_ENGAGEMENT_SERVICE_H_
+#define COMPONENTS_SITE_ENGAGEMENT_CONTENT_SITE_ENGAGEMENT_SERVICE_H_
 
 #include <memory>
 #include <vector>
@@ -31,7 +31,7 @@
 namespace content {
 class BrowserContext;
 class WebContents;
-}
+}  // namespace content
 
 namespace web_app {
 class WebAppEngagementBrowserTest;
@@ -348,4 +348,4 @@
 
 }  // namespace site_engagement
 
-#endif  // CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_H_
+#endif  // COMPONENTS_SITE_ENGAGEMENT_CONTENT_SITE_ENGAGEMENT_SERVICE_H_
diff --git a/components/site_engagement/core/BUILD.gn b/components/site_engagement/core/BUILD.gn
new file mode 100644
index 0000000..dbf7d35
--- /dev/null
+++ b/components/site_engagement/core/BUILD.gn
@@ -0,0 +1,10 @@
+# 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.
+
+static_library("core") {
+  sources = [
+    "pref_names.cc",
+    "pref_names.h",
+  ]
+}
diff --git a/components/site_engagement/core/pref_names.cc b/components/site_engagement/core/pref_names.cc
new file mode 100644
index 0000000..3767483
--- /dev/null
+++ b/components/site_engagement/core/pref_names.cc
@@ -0,0 +1,17 @@
+// 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 "components/site_engagement/core/pref_names.h"
+
+namespace site_engagement {
+namespace prefs {
+
+// The last time that the site engagement service recorded an engagement event
+// for this profile for any URL. Recorded only during shutdown. Used to prevent
+// the service from decaying engagement when a user does not use the browser at
+// all for an extended period of time.
+const char kSiteEngagementLastUpdateTime[] = "profile.last_engagement_time";
+
+}  // namespace prefs
+}  // namespace site_engagement
diff --git a/components/site_engagement/core/pref_names.h b/components/site_engagement/core/pref_names.h
new file mode 100644
index 0000000..97a3f99
--- /dev/null
+++ b/components/site_engagement/core/pref_names.h
@@ -0,0 +1,16 @@
+// 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.
+
+#ifndef COMPONENTS_SITE_ENGAGEMENT_CORE_PREF_NAMES_H_
+#define COMPONENTS_SITE_ENGAGEMENT_CORE_PREF_NAMES_H_
+
+namespace site_engagement {
+namespace prefs {
+
+extern const char kSiteEngagementLastUpdateTime[];
+
+}  // namespace prefs
+}  // namespace site_engagement
+
+#endif  // COMPONENTS_SITE_ENGAGEMENT_CORE_PREF_NAMES_H_
diff --git a/components/subresource_filter/content/browser/async_document_subresource_filter.cc b/components/subresource_filter/content/browser/async_document_subresource_filter.cc
index c26ed088..8bd3b367 100644
--- a/components/subresource_filter/content/browser/async_document_subresource_filter.cc
+++ b/components/subresource_filter/content/browser/async_document_subresource_filter.cc
@@ -162,6 +162,20 @@
       std::move(result_callback));
 }
 
+void AsyncDocumentSubresourceFilter::GetLoadPolicyForSubdocumentURLs(
+    const std::vector<GURL>& urls,
+    MultiLoadPolicyCallback result_callback) {
+  DCHECK(sequence_checker_.CalledOnValidSequence());
+
+  // TODO(pkalinnikov): Think about avoiding copying of |urls| if they are
+  // too big and won't be allowed anyway (e.g. data: URI).
+  base::PostTaskAndReplyWithResult(
+      task_runner_, FROM_HERE,
+      base::BindOnce(&AsyncDocumentSubresourceFilter::Core::GetLoadPolicies,
+                     base::Unretained(core_.get()), urls),
+      std::move(result_callback));
+}
+
 void AsyncDocumentSubresourceFilter::ReportDisallowedLoad() {
   if (!first_disallowed_load_callback_.is_null())
     std::move(first_disallowed_load_callback_).Run();
@@ -206,6 +220,19 @@
   DCHECK(sequence_checker_.CalledOnValidSequence());
 }
 
+std::vector<LoadPolicy> AsyncDocumentSubresourceFilter::Core::GetLoadPolicies(
+    const std::vector<GURL>& urls) {
+  std::vector<LoadPolicy> policies;
+  for (const auto& url : urls) {
+    auto policy =
+        filter() ? filter()->GetLoadPolicy(
+                       url, url_pattern_index::proto::ELEMENT_TYPE_SUBDOCUMENT)
+                 : LoadPolicy::ALLOW;
+    policies.push_back(policy);
+  }
+  return policies;
+}
+
 void AsyncDocumentSubresourceFilter::Core::SetActivationState(
     const mojom::ActivationState& state) {
   DCHECK(filter_);
diff --git a/components/subresource_filter/content/browser/async_document_subresource_filter.h b/components/subresource_filter/content/browser/async_document_subresource_filter.h
index 35585c9..7580945e 100644
--- a/components/subresource_filter/content/browser/async_document_subresource_filter.h
+++ b/components/subresource_filter/content/browser/async_document_subresource_filter.h
@@ -6,6 +6,8 @@
 #define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_ASYNC_DOCUMENT_SUBRESOURCE_FILTER_H_
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/macros.h"
@@ -46,6 +48,8 @@
 class AsyncDocumentSubresourceFilter {
  public:
   using LoadPolicyCallback = base::OnceCallback<void(LoadPolicy)>;
+  using MultiLoadPolicyCallback =
+      base::OnceCallback<void(std::vector<LoadPolicy>)>;
 
   class Core;
 
@@ -134,6 +138,13 @@
   void GetLoadPolicyForSubdocument(const GURL& subdocument_url,
                                    LoadPolicyCallback result_callback);
 
+  // Computes LoadPolicy for each URL in `urls` and returns the vector of
+  // policies back to the calling thread via `result_callback`. If
+  // MemoryMappedRuleset is not present or malformed, then
+  // LoadPolicy::Allow is returned for each of these URLs.
+  void GetLoadPolicyForSubdocumentURLs(const std::vector<GURL>& urls,
+                                       MultiLoadPolicyCallback result_callback);
+
   // Invokes |first_disallowed_load_callback|, if necessary, and posts a task to
   // call DocumentSubresourceFilter::reportDisallowedCallback() on the
   // |task_runner|.
@@ -197,6 +208,8 @@
     return filter_ ? &filter_.value() : nullptr;
   }
 
+  std::vector<LoadPolicy> GetLoadPolicies(const std::vector<GURL>& urls);
+
  private:
   friend class AsyncDocumentSubresourceFilter;
 
diff --git a/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc b/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
index abda0841..4ae2ab4 100644
--- a/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
+++ b/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
@@ -62,6 +62,11 @@
     }
   }
 
+  void RunBlockingTasks() {
+    if (blocking_task_runner_->HasPendingTask())
+      blocking_task_runner_->RunPendingTasks();
+  }
+
   VerifiedRulesetDealer::Handle* dealer_handle() {
     return dealer_handle_.get();
   }
@@ -134,6 +139,67 @@
   DISALLOW_COPY_AND_ASSIGN(LoadPolicyCallbackReceiver);
 };
 
+class MultiLoadPolicyCallbackReceiver {
+ public:
+  MultiLoadPolicyCallbackReceiver() = default;
+  MultiLoadPolicyCallbackReceiver(
+      const MultiLoadPolicyCallbackReceiver& other) = delete;
+  MultiLoadPolicyCallbackReceiver& operator=(
+      const MultiLoadPolicyCallbackReceiver& other) = delete;
+  ~MultiLoadPolicyCallbackReceiver() = default;
+
+  AsyncDocumentSubresourceFilter::MultiLoadPolicyCallback GetCallback() {
+    return base::BindOnce(&MultiLoadPolicyCallbackReceiver::Callback,
+                          base::Unretained(this));
+  }
+
+  int explicitly_allow_count() const { return explicitly_allow_count_; }
+
+  int allow_count() const { return allow_count_; }
+
+  int would_disallow_count() const { return would_disallow_count_; }
+
+  int disallow_count() const { return disallow_count_; }
+
+  void SetQuitClosure(base::OnceClosure quit_closure) {
+    DCHECK(quit_closure);
+    quit_closure_ = std::move(quit_closure);
+  }
+
+ private:
+  void Callback(std::vector<LoadPolicy> policies) {
+    for (const auto& load_policy : policies) {
+      switch (load_policy) {
+        case LoadPolicy::EXPLICITLY_ALLOW:
+          explicitly_allow_count_++;
+          break;
+        case LoadPolicy::ALLOW:
+          allow_count_++;
+          break;
+        case LoadPolicy::WOULD_DISALLOW:
+          would_disallow_count_++;
+          break;
+        case LoadPolicy::DISALLOW:
+          disallow_count_++;
+      }
+    }
+
+    Quit();
+  }
+
+  void Quit() {
+    DCHECK(!quit_closure_.is_null());
+    std::move(quit_closure_).Run();
+  }
+
+  base::OnceClosure quit_closure_;
+
+  int explicitly_allow_count_ = 0;
+  int allow_count_ = 0;
+  int would_disallow_count_ = 0;
+  int disallow_count_ = 0;
+};
+
 }  // namespace
 
 TEST_F(AsyncDocumentSubresourceFilterTest, ActivationStateIsReported) {
@@ -237,6 +303,118 @@
   load_policy_2.ExpectReceivedOnce(LoadPolicy::DISALLOW);
 }
 
+TEST_F(AsyncDocumentSubresourceFilterTest, GetLoadPolicyForSubdocumentURLs) {
+  const struct {
+    mojom::ActivationLevel activation_level;
+    std::vector<GURL> urls;
+    int explicitly_allow_count;
+    int allow_count;
+    int would_disallow_count;
+    int disallow_count;
+  } kTestCases[] = {{mojom::ActivationLevel::kEnabled,
+                     {},
+                     0 /* explicitly_allow_count */,
+                     0 /* allow_count */,
+                     0 /* would_disallow_count */,
+                     0 /* disallow_count */},
+                    {mojom::ActivationLevel::kDryRun,
+                     {},
+                     0 /* explicitly_allow_count */,
+                     0 /* allow_count */,
+                     0 /* would_disallow_count */,
+                     0 /* disallow_count */},
+                    {mojom::ActivationLevel::kEnabled,
+                     {GURL("http://alias1.com/allowed.html"),
+                      GURL("http://alias2.com/disallowed.html")},
+                     0 /* explicitly_allow_count */,
+                     1 /* allow_count */,
+                     0 /* would_disallow_count */,
+                     1 /* disallow_count */},
+                    {mojom::ActivationLevel::kDryRun,
+                     {GURL("http://alias1.com/allowed.html"),
+                      GURL("http://alias2.com/disallowed.html")},
+                     0 /* explicitly_allow_count */,
+                     1 /* allow_count */,
+                     1 /* would_disallow_count */,
+                     0 /* disallow_count */},
+                    {mojom::ActivationLevel::kEnabled,
+                     {GURL("http://example.alias1.com/allowed.html"),
+                      GURL("http://example.alias2.com/allowed.html")},
+                     0 /* explicitly_allow_count */,
+                     2 /* allow_count */,
+                     0 /* would_disallow_count */,
+                     0 /* disallow_count */},
+                    {mojom::ActivationLevel::kDryRun,
+                     {GURL("http://example.alias1.com/allowed.html"),
+                      GURL("http://example.alias2.com/allowed.html")},
+                     0 /* explicitly_allow_count */,
+                     2 /* allow_count */,
+                     0 /* would_disallow_count */,
+                     0 /* disallow_count */},
+                    {mojom::ActivationLevel::kEnabled,
+                     {GURL("http://example.alias1.com/disallowed.html"),
+                      GURL("http://example.alias2.com/disallowed.html")},
+                     0 /* explicitly_allow_count */,
+                     0 /* allow_count */,
+                     0 /* would_disallow_count */,
+                     2 /* disallow_count */},
+                    {mojom::ActivationLevel::kDryRun,
+                     {GURL("http://example.alias1.com/disallowed.html"),
+                      GURL("http://example.alias2.com/disallowed.html")},
+                     0 /* explicitly_allow_count */,
+                     0 /* allow_count */,
+                     2 /* would_disallow_count */,
+                     0 /* disallow_count */},
+                    {mojom::ActivationLevel::kEnabled,
+                     {GURL("http://test.alias1.com/disallowed.html"),
+                      GURL("http://test.alias2.com/allowed.html"),
+                      GURL("http://test.alias3.com/disallowed.html"),
+                      GURL("http://test.alias4.com/disallowed.html")},
+                     0 /* explicitly_allow_count */,
+                     1 /* allow_count */,
+                     0 /* would_disallow_count */,
+                     3 /* disallow_count */},
+                    {mojom::ActivationLevel::kDryRun,
+                     {GURL("http://test.alias1.com/disallowed.html"),
+                      GURL("http://test.alias2.com/allowed.html"),
+                      GURL("http://test.alias3.com/disallowed.html"),
+                      GURL("http://test.alias4.com/disallowed.html")},
+                     0 /* explicitly_allow_count */,
+                     1 /* allow_count */,
+                     3 /* would_disallow_count */,
+                     0 /* disallow_count */}};
+
+  for (const auto& test : kTestCases) {
+    dealer_handle()->TryOpenAndSetRulesetFile(
+        ruleset().path, /*expected_checksum=*/0, base::DoNothing());
+    auto ruleset_handle = CreateRulesetHandle();
+
+    AsyncDocumentSubresourceFilter::InitializationParams params(
+        GURL("http://example.com"), test.activation_level,
+        false /* measure_performance */);
+
+    testing::TestActivationStateCallbackReceiver activation_state;
+    auto filter = std::make_unique<AsyncDocumentSubresourceFilter>(
+        ruleset_handle.get(), std::move(params),
+        activation_state.GetCallback());
+
+    base::RunLoop run_loop;
+    MultiLoadPolicyCallbackReceiver load_policy;
+    load_policy.SetQuitClosure(run_loop.QuitClosure());
+    filter->GetLoadPolicyForSubdocumentURLs(test.urls,
+                                            load_policy.GetCallback());
+
+    RunBlockingTasks();
+    run_loop.Run();
+
+    EXPECT_EQ(test.explicitly_allow_count,
+              load_policy.explicitly_allow_count());
+    EXPECT_EQ(test.allow_count, load_policy.allow_count());
+    EXPECT_EQ(test.would_disallow_count, load_policy.would_disallow_count());
+    EXPECT_EQ(test.disallow_count, load_policy.disallow_count());
+  }
+}
+
 TEST_F(AsyncDocumentSubresourceFilterTest, FirstDisallowedLoadIsReported) {
   dealer_handle()->TryOpenAndSetRulesetFile(
       ruleset().path, /*expected_checksum=*/0, base::DoNothing());
diff --git a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.cc b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.cc
index 0e0d5fd..7bf9866b 100644
--- a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.cc
+++ b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.cc
@@ -21,14 +21,54 @@
 #include "content/public/browser/web_contents.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 
+namespace features {
+
+// Enables or disables performing SubresourceFilter checks from the Browser
+// against any aliases for the requested URL found from DNS CNAME records.
+const base::Feature kSendCnameAliasesToSubresourceFilterFromBrowser{
+    "SendCnameAliasesToSubresourceFilterFromBrowser",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace features
+
 namespace subresource_filter {
 
+namespace {
+
+void LogCnameAliasMetrics(const CnameAliasMetricInfo& info) {
+  bool has_aliases = info.list_length > 0;
+
+  UMA_HISTOGRAM_BOOLEAN("SubresourceFilter.CnameAlias.Browser.HadAliases",
+                        has_aliases);
+
+  if (has_aliases) {
+    UMA_HISTOGRAM_COUNTS_1000("SubresourceFilter.CnameAlias.Browser.ListLength",
+                              info.list_length);
+    UMA_HISTOGRAM_COUNTS_1000(
+        "SubresourceFilter.CnameAlias.Browser.WasAdTaggedBasedOnAliasCount",
+        info.was_ad_tagged_based_on_alias_count);
+    UMA_HISTOGRAM_COUNTS_1000(
+        "SubresourceFilter.CnameAlias.Browser.WasBlockedBasedOnAliasCount",
+        info.was_blocked_based_on_alias_count);
+    UMA_HISTOGRAM_COUNTS_1000(
+        "SubresourceFilter.CnameAlias.Browser.InvalidCount",
+        info.invalid_count);
+    UMA_HISTOGRAM_COUNTS_1000(
+        "SubresourceFilter.CnameAlias.Browser.RedundantCount",
+        info.redundant_count);
+  }
+}
+
+}  // namespace
+
 SubframeNavigationFilteringThrottle::SubframeNavigationFilteringThrottle(
     content::NavigationHandle* handle,
     AsyncDocumentSubresourceFilter* parent_frame_filter,
     Delegate* delegate)
     : content::NavigationThrottle(handle),
       parent_frame_filter_(parent_frame_filter),
+      alias_check_enabled_(base::FeatureList::IsEnabled(
+          ::features::kSendCnameAliasesToSubresourceFilterFromBrowser)),
       delegate_(delegate) {
   DCHECK(!handle->IsInMainFrame());
   DCHECK(parent_frame_filter_);
@@ -57,6 +97,9 @@
           base::TimeDelta::FromSeconds(10), 50);
       break;
   }
+
+  if (alias_check_enabled_)
+    LogCnameAliasMetrics(alias_info_);
 }
 
 content::NavigationThrottle::ThrottleCheckResult
@@ -73,13 +116,48 @@
 SubframeNavigationFilteringThrottle::WillProcessResponse() {
   DCHECK_NE(load_policy_, LoadPolicy::DISALLOW);
 
-  // Load policy notifications should go out by WillProcessResponse,
-  // defer if we are still performing any ruleset checks. If we are here,
-  // and there are outstanding load policy calculations, we are in dry run
-  // mode.
+  if (alias_check_enabled_) {
+    alias_info_.list_length = navigation_handle()->GetDnsAliases().size();
+
+    std::vector<GURL> alias_urls;
+    const GURL& base_url = navigation_handle()->GetURL();
+
+    for (const auto& alias : navigation_handle()->GetDnsAliases()) {
+      if (alias == navigation_handle()->GetURL().host_piece()) {
+        alias_info_.redundant_count++;
+        continue;
+      }
+
+      GURL::Replacements replacements;
+      replacements.SetHostStr(alias);
+      GURL alias_url = base_url.ReplaceComponents(replacements);
+
+      if (!alias_url.is_valid()) {
+        alias_info_.invalid_count++;
+        continue;
+      }
+
+      alias_urls.push_back(alias_url);
+    }
+
+    if (!alias_urls.empty()) {
+      pending_load_policy_calculations_++;
+      parent_frame_filter_->GetLoadPolicyForSubdocumentURLs(
+          alias_urls, base::BindOnce(&SubframeNavigationFilteringThrottle::
+                                         OnCalculatedLoadPoliciesFromAliasUrls,
+                                     weak_ptr_factory_.GetWeakPtr()));
+    }
+  }
+
+  // Load policy notifications should go out by WillProcessResponse, unless
+  // we received CNAME aliases in the response and alias checking is enabled.
+  // Defer if we are still performing any ruleset checks. If we are here,
+  // and there are outstanding load policy calculations, we are either in dry
+  // run mode or checking aliases.
   if (pending_load_policy_calculations_ > 0) {
-    DCHECK((parent_frame_filter_->activation_state().activation_level ==
-            mojom::ActivationLevel::kDryRun));
+    DCHECK(parent_frame_filter_->activation_state().activation_level ==
+               mojom::ActivationLevel::kDryRun ||
+           alias_info_.list_length > 0);
     DeferStart(DeferStage::kWillProcessResponse);
     return DEFER;
   }
@@ -144,45 +222,43 @@
   if (defer_stage_ == DeferStage::kNotDeferring)
     return;
 
-  // When we are deferred, callback is not responsible for handling navigation
-  // if there are still outstanding load policy calculations.
-  if (pending_load_policy_calculations_ > 0) {
-    // We defer waiting for each load policy calculations when the embedder
-    // document has activation enabled.
-    DCHECK(parent_frame_filter_->activation_state().activation_level !=
-           mojom::ActivationLevel::kEnabled);
-    return;
-  }
-
-  // If we are deferred and there are no pending load policy calculations,
-  // handle the deferred navigation.
   DCHECK(defer_stage_ == DeferStage::kWillProcessResponse ||
          defer_stage_ == DeferStage::kWillStartOrRedirectRequest);
-  DCHECK(!last_defer_timestamp_.is_null());
-  bool deferring_response = defer_stage_ == DeferStage::kWillProcessResponse;
-  total_defer_time_ += base::TimeTicks::Now() - last_defer_timestamp_;
-  defer_stage_ = DeferStage::kNotDeferring;
-  if (deferring_response) {
-    NotifyLoadPolicy();
-    Resume();
+
+  // If we have an activation enabled and `load_policy_` is DISALLOW, we need
+  // to cancel the navigation.
+  if (parent_frame_filter_->activation_state().activation_level ==
+          mojom::ActivationLevel::kEnabled &&
+      load_policy_ == LoadPolicy::DISALLOW) {
+    CancelNavigation();
     return;
   }
 
-  // Otherwise, we deferred at start/redirect time. Either cancel navigation
-  // or resume here according to load policy.
-  if (load_policy_ == LoadPolicy::DISALLOW) {
-    HandleDisallowedLoad();
-
-    // Because the navigation will be canceled, this is the last LoadPolicy that
-    // will be calculated.
-    NotifyLoadPolicy();
-    CancelDeferredNavigation(BLOCK_REQUEST_AND_COLLAPSE);
+  // If there are still pending load calculations, then don't resume.
+  if (pending_load_policy_calculations_ > 0)
     return;
+
+  ResumeNavigation();
+}
+
+void SubframeNavigationFilteringThrottle::OnCalculatedLoadPoliciesFromAliasUrls(
+    std::vector<LoadPolicy> policies) {
+  // We deferred to check aliases in WillProcessResponse.
+  DCHECK(defer_stage_ == DeferStage::kWillProcessResponse);
+  DCHECK(!policies.empty());
+
+  LoadPolicy most_restricive_alias_policy = LoadPolicy::EXPLICITLY_ALLOW;
+
+  for (LoadPolicy policy : policies) {
+    most_restricive_alias_policy =
+        MoreRestrictiveLoadPolicy(most_restricive_alias_policy, policy);
+    if (policy == LoadPolicy::WOULD_DISALLOW)
+      alias_info_.was_ad_tagged_based_on_alias_count++;
+    else if (policy == LoadPolicy::DISALLOW)
+      alias_info_.was_blocked_based_on_alias_count++;
   }
 
-  // We will calculate another LoadPolicy for this navigation, so do not notify
-  // the manager yet.
-  Resume();
+  OnCalculatedLoadPolicy(most_restricive_alias_policy);
 }
 
 void SubframeNavigationFilteringThrottle::DeferStart(DeferStage stage) {
@@ -213,4 +289,40 @@
       navigation_handle(), load_policy_, is_ad_subframe);
 }
 
+void SubframeNavigationFilteringThrottle::UpdateDeferInfo() {
+  DCHECK(defer_stage_ != DeferStage::kNotDeferring);
+  DCHECK(!last_defer_timestamp_.is_null());
+  total_defer_time_ += base::TimeTicks::Now() - last_defer_timestamp_;
+  defer_stage_ = DeferStage::kNotDeferring;
+}
+
+void SubframeNavigationFilteringThrottle::CancelNavigation() {
+  bool defer_stage_was_will_process_response =
+      defer_stage_ == DeferStage::kWillProcessResponse;
+
+  UpdateDeferInfo();
+  HandleDisallowedLoad();
+  NotifyLoadPolicy();
+
+  if (defer_stage_was_will_process_response)
+    CancelDeferredNavigation(CANCEL);
+  else
+    CancelDeferredNavigation(BLOCK_REQUEST_AND_COLLAPSE);
+}
+
+void SubframeNavigationFilteringThrottle::ResumeNavigation() {
+  // There are no more pending load calculations. We can toggle back to not
+  // being deferred.
+  bool defer_stage_was_will_process_response =
+      defer_stage_ == DeferStage::kWillProcessResponse;
+  UpdateDeferInfo();
+
+  // If the defer stage was WillProcessResponse, then this is the last
+  // LoadPolicy that we will calculate.
+  if (defer_stage_was_will_process_response)
+    NotifyLoadPolicy();
+
+  Resume();
+}
+
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h
index 94e2f7b..77abfff 100644
--- a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h
+++ b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBFRAME_NAVIGATION_FILTERING_THROTTLE_H_
 #define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBFRAME_NAVIGATION_FILTERING_THROTTLE_H_
 
+#include "base/feature_list.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
@@ -12,6 +13,10 @@
 #include "components/subresource_filter/core/common/load_policy.h"
 #include "content/public/browser/navigation_throttle.h"
 
+namespace features {
+extern const base::Feature kSendCnameAliasesToSubresourceFilterFromBrowser;
+}  // namespace features
+
 namespace content {
 class NavigationHandle;
 class RenderFrameHost;
@@ -21,6 +26,16 @@
 
 class AsyncDocumentSubresourceFilter;
 
+// Struct for keeping variables used in recording CNAME alias metrics bundled
+// together.
+struct CnameAliasMetricInfo {
+  int list_length = 0;
+  int was_ad_tagged_based_on_alias_count = 0;
+  int was_blocked_based_on_alias_count = 0;
+  int invalid_count = 0;
+  int redundant_count = 0;
+};
+
 // NavigationThrottle responsible for filtering subframe document loads, which
 // are considered subresource loads of their parent frame, hence are subject to
 // subresource filtering using the parent frame's
@@ -77,11 +92,16 @@
   MaybeDeferToCalculateLoadPolicy();
 
   void OnCalculatedLoadPolicy(LoadPolicy policy);
+  void OnCalculatedLoadPoliciesFromAliasUrls(std::vector<LoadPolicy> policies);
   void HandleDisallowedLoad();
 
   void NotifyLoadPolicy() const;
 
   void DeferStart(DeferStage stage);
+  void UpdateDeferInfo();
+
+  void CancelNavigation();
+  void ResumeNavigation();
 
   // Must outlive this class.
   AsyncDocumentSubresourceFilter* parent_frame_filter_;
@@ -91,6 +111,9 @@
   base::TimeTicks last_defer_timestamp_;
   base::TimeDelta total_defer_time_;
 
+  const bool alias_check_enabled_;
+  CnameAliasMetricInfo alias_info_;
+
   // Set to the least restrictive load policy by default.
   LoadPolicy load_policy_ = LoadPolicy::EXPLICITLY_ALLOW;
 
diff --git a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc
index 686aa9d..5b9f768 100644
--- a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter_test_utils.h"
@@ -31,6 +32,19 @@
 
 namespace subresource_filter {
 
+const char kCnameAliasHadAliasesHistogram[] =
+    "SubresourceFilter.CnameAlias.Browser.HadAliases";
+const char kCnameAliasIsInvalidCountHistogram[] =
+    "SubresourceFilter.CnameAlias.Browser.InvalidCount";
+const char kCnameAliasIsRedundantCountHistogram[] =
+    "SubresourceFilter.CnameAlias.Browser.RedundantCount";
+const char kCnameAliasListLengthHistogram[] =
+    "SubresourceFilter.CnameAlias.Browser.ListLength";
+const char kCnameAliasWasAdTaggedCountHistogram[] =
+    "SubresourceFilter.CnameAlias.Browser.WasAdTaggedBasedOnAliasCount";
+const char kCnameAliasWasBlockedCountHistogram[] =
+    "SubresourceFilter.CnameAlias.Browser.WasBlockedBasedOnAliasCount";
+
 class MockDelegate : public SubframeNavigationFilteringThrottle::Delegate {
  public:
   MockDelegate() = default;
@@ -91,6 +105,51 @@
         test_ruleset_creator_.CreateRulesetToDisallowURLsWithPathSuffix(
             "disallowed.html", &test_ruleset_pair_));
 
+    FinishInitializingDocumentSubresourceFilter(document_url, parent_level);
+  }
+
+  void InitializeDocumentSubresourceFilterWithSubstringRules(
+      const GURL& document_url,
+      std::vector<base::StringPiece> urls_to_block,
+      mojom::ActivationLevel parent_level = mojom::ActivationLevel::kEnabled) {
+    ASSERT_NO_FATAL_FAILURE(
+        test_ruleset_creator_.CreateRulesetToDisallowURLWithSubstrings(
+            urls_to_block, &test_ruleset_pair_));
+
+    FinishInitializingDocumentSubresourceFilter(document_url, parent_level);
+  }
+
+  void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
+
+  void CreateTestSubframeAndInitNavigation(const GURL& first_url,
+                                           content::RenderFrameHost* parent) {
+    content::RenderFrameHost* render_frame =
+        content::RenderFrameHostTester::For(parent)->AppendChild(
+            base::StringPrintf("subframe-%s", first_url.spec().c_str()));
+    navigation_simulator_ =
+        content::NavigationSimulator::CreateRendererInitiated(first_url,
+                                                              render_frame);
+  }
+
+  const std::vector<std::string>& GetConsoleMessages() {
+    return content::RenderFrameHostTester::For(main_rfh())
+        ->GetConsoleMessages();
+  }
+
+  std::string GetFilterConsoleMessage(const GURL& filtered_url) {
+    return base::StringPrintf(kDisallowSubframeConsoleMessageFormat,
+                              filtered_url.possibly_invalid_spec().c_str());
+  }
+
+  void SetResponseDnsAliasesForNavigation(std::vector<std::string> aliases) {
+    DCHECK(navigation_simulator_);
+    navigation_simulator_->SetResponseDnsAliases(std::move(aliases));
+  }
+
+ private:
+  void FinishInitializingDocumentSubresourceFilter(
+      const GURL& document_url,
+      mojom::ActivationLevel parent_level) {
     // Make the blocking task runner run on the current task runner for the
     // tests, to ensure that the NavigationSimulator properly runs all necessary
     // tasks while waiting for throttle checks to finish.
@@ -116,29 +175,6 @@
     activation_state.ExpectReceivedOnce(parent_activation_state);
   }
 
-  void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
-
-  void CreateTestSubframeAndInitNavigation(const GURL& first_url,
-                                           content::RenderFrameHost* parent) {
-    content::RenderFrameHost* render_frame =
-        content::RenderFrameHostTester::For(parent)->AppendChild(
-            base::StringPrintf("subframe-%s", first_url.spec().c_str()));
-    navigation_simulator_ =
-        content::NavigationSimulator::CreateRendererInitiated(first_url,
-                                                              render_frame);
-  }
-
-  const std::vector<std::string>& GetConsoleMessages() {
-    return content::RenderFrameHostTester::For(main_rfh())
-        ->GetConsoleMessages();
-  }
-
-  std::string GetFilterConsoleMessage(const GURL& filtered_url) {
-    return base::StringPrintf(kDisallowSubframeConsoleMessageFormat,
-                              filtered_url.possibly_invalid_spec().c_str());
-  }
-
- private:
   testing::TestRulesetCreator test_ruleset_creator_;
   testing::TestRulesetPair test_ruleset_pair_;
 
@@ -326,4 +362,116 @@
   histogram_tester.ExpectTotalCount(kFilterDelayAllowed, 1);
 }
 
+class SubframeNavigationFilteringThrottleDnsAliasTest
+    : public SubframeNavigationFilteringThrottleTest {
+ public:
+  SubframeNavigationFilteringThrottleDnsAliasTest() {
+    feature_list_.InitAndEnableFeature(
+        ::features::kSendCnameAliasesToSubresourceFilterFromBrowser);
+  }
+
+  ~SubframeNavigationFilteringThrottleDnsAliasTest() override = default;
+
+  void ExpectHistogramsMatching(CnameAliasMetricInfo info) {
+    bool has_aliases = info.list_length > 0;
+    histogram_tester_.ExpectUniqueSample(kCnameAliasHadAliasesHistogram,
+                                         has_aliases, 1);
+
+    if (has_aliases) {
+      histogram_tester_.ExpectUniqueSample(kCnameAliasListLengthHistogram,
+                                           info.list_length, 1);
+      histogram_tester_.ExpectUniqueSample(
+          kCnameAliasWasAdTaggedCountHistogram,
+          info.was_ad_tagged_based_on_alias_count, 1);
+      histogram_tester_.ExpectUniqueSample(
+          kCnameAliasWasBlockedCountHistogram,
+          info.was_blocked_based_on_alias_count, 1);
+      histogram_tester_.ExpectUniqueSample(kCnameAliasIsInvalidCountHistogram,
+                                           info.invalid_count, 1);
+      histogram_tester_.ExpectUniqueSample(kCnameAliasIsRedundantCountHistogram,
+                                           info.redundant_count, 1);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  base::HistogramTester histogram_tester_;
+};
+
+TEST_F(SubframeNavigationFilteringThrottleDnsAliasTest,
+       FilterOnWillProcessResponse) {
+  InitializeDocumentSubresourceFilterWithSubstringRules(
+      GURL("https://example.test"), {"disallowed.com", ".bad-ad/some"});
+
+  const GURL url("https://example.test/some_path.html");
+  CreateTestSubframeAndInitNavigation(url, main_rfh());
+
+  std::vector<std::string> dns_aliases({"alias1.com", "/", "example.test", "",
+                                        "disallowed.com", "allowed.com",
+                                        "test.bad-ad"});
+  SetResponseDnsAliasesForNavigation(std::move(dns_aliases));
+
+  EXPECT_EQ(content::NavigationThrottle::CANCEL,
+            SimulateCommitAndGetResult(navigation_simulator()));
+  EXPECT_TRUE(
+      base::Contains(GetConsoleMessages(), GetFilterConsoleMessage(url)));
+
+  CnameAliasMetricInfo info = {.list_length = 7,
+                               .was_ad_tagged_based_on_alias_count = 0,
+                               .was_blocked_based_on_alias_count = 2,
+                               .invalid_count = 2,
+                               .redundant_count = 1};
+
+  ExpectHistogramsMatching(info);
+}
+
+TEST_F(SubframeNavigationFilteringThrottleDnsAliasTest,
+       DryRunOnWillProcessResponse) {
+  InitializeDocumentSubresourceFilterWithSubstringRules(
+      GURL("https://example.test"), {"disallowed.com", "bad", "blocked"},
+      mojom::ActivationLevel::kDryRun);
+
+  const GURL url("https://example.test/some_path.html");
+  CreateTestSubframeAndInitNavigation(url, main_rfh());
+
+  std::vector<std::string> dns_aliases({"alias1.com", "", "test.disallowed.com",
+                                        "allowed.com", "blocked.com",
+                                        "bad.org"});
+  SetResponseDnsAliasesForNavigation(std::move(dns_aliases));
+
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
+  EXPECT_FALSE(
+      base::Contains(GetConsoleMessages(), GetFilterConsoleMessage(url)));
+
+  CnameAliasMetricInfo info = {.list_length = 6,
+                               .was_ad_tagged_based_on_alias_count = 3,
+                               .was_blocked_based_on_alias_count = 0,
+                               .invalid_count = 1,
+                               .redundant_count = 0};
+
+  ExpectHistogramsMatching(info);
+}
+
+TEST_F(SubframeNavigationFilteringThrottleDnsAliasTest, EnabledNoAliases) {
+  InitializeDocumentSubresourceFilterWithSubstringRules(
+      GURL("https://example.test"), {"disallowed.com"},
+      mojom::ActivationLevel::kEnabled);
+
+  const GURL url("https://example.test/some_path.html");
+  CreateTestSubframeAndInitNavigation(url, main_rfh());
+
+  std::vector<std::string> dns_aliases;
+  SetResponseDnsAliasesForNavigation(std::move(dns_aliases));
+
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
+  EXPECT_FALSE(
+      base::Contains(GetConsoleMessages(), GetFilterConsoleMessage(url)));
+
+  CnameAliasMetricInfo info;
+
+  ExpectHistogramsMatching(info);
+}
+
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/core/common/test_ruleset_creator.cc b/components/subresource_filter/core/common/test_ruleset_creator.cc
index 9fab0093..1fef23f 100644
--- a/components/subresource_filter/core/common/test_ruleset_creator.cc
+++ b/components/subresource_filter/core/common/test_ruleset_creator.cc
@@ -136,6 +136,16 @@
       CreateUnindexedRulesetWithRules({suffix_rule}, test_unindexed_ruleset));
 }
 
+void TestRulesetCreator::CreateRulesetToDisallowURLWithSubstrings(
+    std::vector<base::StringPiece> substrings,
+    TestRulesetPair* test_ruleset_pair) {
+  DCHECK(test_ruleset_pair);
+  std::vector<proto::UrlRule> url_rules;
+  for (const auto& substring : substrings)
+    url_rules.push_back(CreateSubstringRule(substring));
+  CreateRulesetWithRules(url_rules, test_ruleset_pair);
+}
+
 void TestRulesetCreator::CreateRulesetToDisallowURLsWithManySuffixes(
     base::StringPiece suffix,
     int num_of_suffixes,
diff --git a/components/subresource_filter/core/common/test_ruleset_creator.h b/components/subresource_filter/core/common/test_ruleset_creator.h
index 8cc493e..23f59db 100644
--- a/components/subresource_filter/core/common/test_ruleset_creator.h
+++ b/components/subresource_filter/core/common/test_ruleset_creator.h
@@ -77,6 +77,14 @@
       base::StringPiece suffix,
       TestRuleset* test_unindexed_ruleset);
 
+  // Creates both the indexed and unindexed versions of a testing ruleset that
+  // consists of filtering rules that disallow subresource loads from URLs
+  // containing any of the given `substrings`. Enclose call in
+  // ASSERT_NO_FATAL_FAILURE to detect errors.
+  void CreateRulesetToDisallowURLWithSubstrings(
+      std::vector<base::StringPiece> substrings,
+      TestRulesetPair* test_ruleset_pair);
+
   // Similar to CreateRulesetToDisallowURLsWithPathSuffix, but the resulting
   // ruleset consists of |num_of_suffixes| rules, each of them disallowing URLs
   // with suffixes of the form |suffix|_k, 0 <= k < |num_of_suffixes|.
diff --git a/components/subresource_filter/core/common/test_ruleset_utils.cc b/components/subresource_filter/core/common/test_ruleset_utils.cc
index 8e2c2b7c..ddf32f0 100644
--- a/components/subresource_filter/core/common/test_ruleset_utils.cc
+++ b/components/subresource_filter/core/common/test_ruleset_utils.cc
@@ -11,6 +11,20 @@
 
 namespace proto = url_pattern_index::proto;
 
+proto::UrlRule CreateSubstringRule(base::StringPiece substring) {
+  proto::UrlRule rule;
+
+  rule.set_semantics(proto::RULE_SEMANTICS_BLACKLIST);
+  rule.set_source_type(proto::SOURCE_TYPE_ANY);
+  rule.set_element_types(proto::ELEMENT_TYPE_ALL);
+  rule.set_url_pattern_type(proto::URL_PATTERN_TYPE_SUBSTRING);
+  rule.set_anchor_left(proto::ANCHOR_TYPE_NONE);
+  rule.set_anchor_right(proto::ANCHOR_TYPE_NONE);
+  rule.set_url_pattern(substring.as_string());
+
+  return rule;
+}
+
 proto::UrlRule CreateSuffixRule(base::StringPiece suffix) {
   proto::UrlRule rule;
   rule.set_semantics(proto::RULE_SEMANTICS_BLACKLIST);
diff --git a/components/subresource_filter/core/common/test_ruleset_utils.h b/components/subresource_filter/core/common/test_ruleset_utils.h
index 00349fa..2fbfc96 100644
--- a/components/subresource_filter/core/common/test_ruleset_utils.h
+++ b/components/subresource_filter/core/common/test_ruleset_utils.h
@@ -15,6 +15,11 @@
 namespace subresource_filter {
 namespace testing {
 
+// Creates a blocklisted URL rule which targets subresources of any type with
+// a URL containing the given `substring`.
+url_pattern_index::proto::UrlRule CreateSubstringRule(
+    base::StringPiece substring);
+
 // Creates a blocklisted URL rule which targets subresources of any type such
 // that the resource URL ends with |suffix|.
 url_pattern_index::proto::UrlRule CreateSuffixRule(base::StringPiece suffix);
diff --git a/components/user_manager/fake_user_manager.cc b/components/user_manager/fake_user_manager.cc
index 64177bc8..d77d9c56 100644
--- a/components/user_manager/fake_user_manager.cc
+++ b/components/user_manager/fake_user_manager.cc
@@ -380,7 +380,8 @@
   return account_id == StubAccountId();
 }
 
-bool FakeUserManager::IsSupervisedAccountId(const AccountId& account_id) const {
+bool FakeUserManager::IsDeprecatedSupervisedAccountId(
+    const AccountId& account_id) const {
   return false;
 }
 
diff --git a/components/user_manager/fake_user_manager.h b/components/user_manager/fake_user_manager.h
index 974e584..adcd40e 100644
--- a/components/user_manager/fake_user_manager.h
+++ b/components/user_manager/fake_user_manager.h
@@ -123,7 +123,8 @@
                               const std::string& gaia_id,
                               AccountId* out_account_id) const override;
   void AsyncRemoveCryptohome(const AccountId& account_id) const override;
-  bool IsSupervisedAccountId(const AccountId& account_id) const override;
+  bool IsDeprecatedSupervisedAccountId(
+      const AccountId& account_id) const override;
   const gfx::ImageSkia& GetResourceImagekiaNamed(int id) const override;
   base::string16 GetResourceStringUTF16(int string_id) const override;
   void ScheduleResolveLocale(const std::string& locale,
diff --git a/components/user_manager/user.cc b/components/user_manager/user.cc
index b6d9d68..22164827 100644
--- a/components/user_manager/user.cc
+++ b/components/user_manager/user.cc
@@ -209,10 +209,9 @@
   return GetType() == user_manager::USER_TYPE_ACTIVE_DIRECTORY;
 }
 
-bool User::IsSupervised() const {
+bool User::IsChildOrDeprecatedSupervised() const {
   UserType type = GetType();
-  return  type == USER_TYPE_SUPERVISED ||
-          type == USER_TYPE_CHILD;
+  return type == USER_TYPE_SUPERVISED_DEPRECATED || type == USER_TYPE_CHILD;
 }
 
 bool User::IsChild() const {
@@ -263,7 +262,7 @@
       return true;
     case user_manager::USER_TYPE_GUEST:
     case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
-    case user_manager::USER_TYPE_SUPERVISED:
+    case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
     case user_manager::USER_TYPE_KIOSK_APP:
     case user_manager::USER_TYPE_ARC_KIOSK_APP:
     case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
@@ -507,7 +506,7 @@
 }
 
 UserType SupervisedUser::GetType() const {
-  return user_manager::USER_TYPE_SUPERVISED;
+  return user_manager::USER_TYPE_SUPERVISED_DEPRECATED;
 }
 
 std::string SupervisedUser::display_email() const {
diff --git a/components/user_manager/user.h b/components/user_manager/user.h
index 7b4e6c0..82c4d5f 100644
--- a/components/user_manager/user.h
+++ b/components/user_manager/user.h
@@ -99,8 +99,9 @@
   // Returns true if it's Active Directory user.
   virtual bool IsActiveDirectoryUser() const;
 
-  // Returns true if user is supervised.
-  virtual bool IsSupervised() const;
+  // Returns true if user is child or deprecated legacy supervised.
+  // TODO(crbug/1155729): Remove and replace with IsChild().
+  virtual bool IsChildOrDeprecatedSupervised() const;
 
   // Returns true if user is child.
   virtual bool IsChild() const;
diff --git a/components/user_manager/user_manager.cc b/components/user_manager/user_manager.cc
index caa9a29..f1384c989 100644
--- a/components/user_manager/user_manager.cc
+++ b/components/user_manager/user_manager.cc
@@ -129,8 +129,8 @@
   if (is_child)
     return USER_TYPE_CHILD;
 
-  if (IsSupervisedAccountId(account_id))
-    return USER_TYPE_SUPERVISED;
+  if (IsDeprecatedSupervisedAccountId(account_id))
+    return USER_TYPE_SUPERVISED_DEPRECATED;
 
   if (account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY)
     return USER_TYPE_ACTIVE_DIRECTORY;
diff --git a/components/user_manager/user_manager.h b/components/user_manager/user_manager.h
index 2e1e434..82a9bbf 100644
--- a/components/user_manager/user_manager.h
+++ b/components/user_manager/user_manager.h
@@ -327,7 +327,7 @@
 
   // Returns true if |user| is allowed depending on device policies.
   // Accepted user types: USER_TYPE_REGULAR, USER_TYPE_GUEST,
-  // USER_TYPE_SUPERVISED, USER_TYPE_CHILD.
+  // USER_TYPE_SUPERVISED_DEPRECATED, USER_TYPE_CHILD.
   virtual bool IsUserAllowed(const User& user) const = 0;
 
   // Returns "Local State" PrefService instance.
@@ -355,9 +355,10 @@
   // Returns true if |account_id| is Stub user.
   virtual bool IsStubAccountId(const AccountId& account_id) const = 0;
 
-  // Returns true if |account_id| is supervised.
-  // TODO(crbug.com/866790): Check it is not used anymore and remove it.
-  virtual bool IsSupervisedAccountId(const AccountId& account_id) const = 0;
+  // Returns true if |account_id| is deprecated supervised.
+  // TODO(crbug.com/1155729): Check it is not used anymore and remove it.
+  virtual bool IsDeprecatedSupervisedAccountId(
+      const AccountId& account_id) const = 0;
 
   virtual bool IsDeviceLocalAccountMarkedForRemoval(
       const AccountId& account_id) const = 0;
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc
index 71feab5..87e59b2f 100644
--- a/components/user_manager/user_manager_base.cc
+++ b/components/user_manager/user_manager_base.cc
@@ -64,8 +64,8 @@
 // session restore.
 const char kLastActiveUser[] = "LastActiveUser";
 
-// Histogram for tracking the number of legacy supervised user cryptohomes
-// remaining in the wild.
+// Histogram for tracking the number of deprecated legacy supervised user
+// cryptohomes remaining in the wild.
 const char kHideLegacySupervisedUserHistogramName[] =
     "ChromeOS.LegacySupervisedUsers.HiddenFromLoginScreen";
 
@@ -200,7 +200,7 @@
             user ? user : User::CreatePublicAccountUser(account_id));
         break;
 
-      case USER_TYPE_SUPERVISED:
+      case USER_TYPE_SUPERVISED_DEPRECATED:
         NOTREACHED() << "Supervised users are not supported anymore";
         break;
 
@@ -812,7 +812,7 @@
                 &regular_users_set);
   for (std::vector<AccountId>::const_iterator it = regular_users.begin();
        it != regular_users.end(); ++it) {
-    if (IsSupervisedAccountId(*it)) {
+    if (IsDeprecatedSupervisedAccountId(*it)) {
       base::UmaHistogramBoolean(kHideLegacySupervisedUserHistogramName, true);
       continue;
     }
diff --git a/components/user_manager/user_type.h b/components/user_manager/user_type.h
index 85afb8b..ac1677a 100644
--- a/components/user_manager/user_type.h
+++ b/components/user_manager/user_type.h
@@ -33,7 +33,7 @@
   // Legacy supervised user, being deprecated. Logs in only with local
   // authentication. No Gaia account. Never ephemeral. Could have a sync token
   // to fetch some basic account info.
-  USER_TYPE_SUPERVISED = 4,
+  USER_TYPE_SUPERVISED_DEPRECATED = 4,
 
   // Kiosk users used to launch application in a single app mode. Logs in
   // without authentications. No Gaia user account. Uses device robot account.
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index e4196d75..29a73e7 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -26,6 +26,7 @@
 #include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
 #include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
 #include "components/viz/common/quads/draw_quad.h"
+#include "components/viz/common/quads/solid_color_draw_quad.h"
 #include "components/viz/service/display/bsp_tree.h"
 #include "components/viz/service/display/bsp_walk_action.h"
 #include "components/viz/service/display/output_surface.h"
@@ -688,6 +689,16 @@
     SetScissorStateForQuad(quad, render_pass_scissor_in_draw_space,
                            render_pass_requires_scissor);
 
+    if (OverlayCandidate::RequiresOverlay(&quad)) {
+      // We cannot composite this quad properly, replace it with solid black.
+      SolidColorDrawQuad solid_black;
+      solid_black.SetAll(quad.shared_quad_state, quad.rect, quad.rect,
+                         /*needs_blending=*/false, SK_ColorBLACK,
+                         /*force_anti_aliasing_off=*/true);
+      DoDrawQuad(&solid_black, nullptr);
+      continue;
+    }
+
     DoDrawQuad(&quad, nullptr);
   }
   FlushPolygons(&poly_list, render_pass_scissor_in_draw_space,
diff --git a/components/viz/service/display/overlay_candidate.cc b/components/viz/service/display/overlay_candidate.cc
index 8f2a987..a282da4 100644
--- a/components/viz/service/display/overlay_candidate.cc
+++ b/components/viz/service/display/overlay_candidate.cc
@@ -305,6 +305,8 @@
     OverlayCandidate* candidate) {
   if (!resource_provider->IsOverlayCandidate(resource_id))
     return false;
+  if (quad->visible_rect.IsEmpty())
+    return false;
 
   candidate->format = resource_provider->GetBufferFormat(resource_id);
   candidate->color_space = resource_provider->GetColorSpace(resource_id);
diff --git a/components/viz/service/display/overlay_processor_using_strategy.cc b/components/viz/service/display/overlay_processor_using_strategy.cc
index 6f12bf7c..1c0080a 100644
--- a/components/viz/service/display/overlay_processor_using_strategy.cc
+++ b/components/viz/service/display/overlay_processor_using_strategy.cc
@@ -326,9 +326,9 @@
         // DRM/CDM HW overlay required:
         // This comparison is for correctness over performance reasons. Some
         // candidates must be an HW overlay to function. If both require an HW
-        // overlay we sort on the remaining criteria below.
-        if (a.candidate.requires_overlay ^ b.candidate.requires_overlay) {
-          return a.candidate.requires_overlay;
+        // overlay we leave them in order so the topmost one gets the overlay.
+        if (a.candidate.requires_overlay || b.candidate.requires_overlay) {
+          return a.candidate.requires_overlay && !b.candidate.requires_overlay;
         }
 
         // Opaque Power Metric:
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index d6409537..dbe9518 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -485,6 +485,10 @@
   RunAomTest(FILE_PATH_LITERAL("aom-live-region.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityAomModalDialog) {
+  RunAomTest(FILE_PATH_LITERAL("aom-modal-dialog.html"));
+}
+
 // TODO(crbug.com/983709): Flaky.
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
                        DISABLED_AccessibilityAriaActivedescendant) {
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index 5d2e3a9..c9f95a6d 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -1533,19 +1533,6 @@
       return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
     }
 
-    // Allow "no access" schemes to commit even though |url_origin| and
-    // |origin| tuples don't match. We have to allow this because Blink's
-    // SecurityOrigin::CreateWithReferenceOrigin() and url::Origin::Resolve()
-    // handle "no access" URLs differently. CreateWithReferenceOrigin() treats
-    // "no access" like data: URLs and returns an opaque origin with |origin|
-    // as a precursor. Resolve() returns a non-opaque origin consisting of the
-    // scheme and host portions of the original URL.
-    //
-    // TODO(1020201): Make CreateWithReferenceOrigin() & Resolve() consistent
-    // with each other and then remove this exception.
-    if (base::Contains(url::GetNoAccessSchemes(), url_info.url.scheme()))
-      return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
-
     return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
   }
 
diff --git a/content/browser/direct_sockets/direct_sockets_browsertest.cc b/content/browser/direct_sockets/direct_sockets_browsertest.cc
index 26ddcf2..074e0f9 100644
--- a/content/browser/direct_sockets/direct_sockets_browsertest.cc
+++ b/content/browser/direct_sockets/direct_sockets_browsertest.cc
@@ -26,6 +26,7 @@
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
+#include "net/net_buildflags.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
@@ -196,6 +197,25 @@
         ->GetNetworkContext();
   }
 
+  std::string CreateMDNSHostName() {
+    DCHECK(!mdns_responder_.is_bound());
+    GetNetworkContext()->CreateMdnsResponder(
+        mdns_responder_.BindNewPipeAndPassReceiver());
+
+    std::string name;
+    base::RunLoop run_loop;
+    mdns_responder_->CreateNameForAddress(
+        net::IPAddress::IPv4Localhost(),
+        base::BindLambdaForTesting(
+            [&name, &run_loop](const std::string& name_out,
+                               bool announcement_scheduled) {
+              name = name_out;
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+    return name;
+  }
+
   // Returns the port listening for TCP connections.
   uint16_t StartTcpServer() {
     net::IPEndPoint local_addr;
@@ -233,6 +253,7 @@
   }
 
   base::test::ScopedFeatureList feature_list_;
+  mojo::Remote<network::mojom::MdnsResponder> mdns_responder_;
   mojo::Remote<network::mojom::TCPServerSocket> tcp_server_socket_;
 };
 
@@ -280,6 +301,26 @@
   EXPECT_EQ(expected_result, EvalJs(shell(), script));
 }
 
+IN_PROC_BROWSER_TEST_F(DirectSocketsBrowserTest, OpenTcp_MDNS) {
+  EXPECT_TRUE(NavigateToURL(shell(), GetTestPageURL()));
+
+  const uint16_t listening_port = StartTcpServer();
+  const std::string name = CreateMDNSHostName();
+  EXPECT_TRUE(base::EndsWith(name, ".local"));
+
+  const std::string script =
+      base::StringPrintf("openTcp({remoteAddress: '%s', remotePort: %d})",
+                         name.c_str(), listening_port);
+
+#if BUILDFLAG(ENABLE_MDNS)
+  EXPECT_THAT(EvalJs(shell(), script).ExtractString(),
+              StartsWith("openTcp succeeded"));
+#else
+  EXPECT_EQ("openTcp failed: NotAllowedError: Permission denied",
+            EvalJs(shell(), script));
+#endif  // BUILDFLAG(ENABLE_MDNS)
+}
+
 IN_PROC_BROWSER_TEST_F(DirectSocketsBrowserTest, OpenTcp_CannotEvadeCors) {
   EXPECT_TRUE(NavigateToURL(shell(), GetTestPageURL()));
 
diff --git a/content/browser/direct_sockets/direct_sockets_service_impl.cc b/content/browser/direct_sockets/direct_sockets_service_impl.cc
index ae746b2..b1176ab 100644
--- a/content/browser/direct_sockets/direct_sockets_service_impl.cc
+++ b/content/browser/direct_sockets/direct_sockets_service_impl.cc
@@ -16,6 +16,7 @@
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/base/address_list.h"
+#include "net/net_buildflags.h"
 #include "services/network/public/cpp/resolve_host_client_base.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 
@@ -52,6 +53,12 @@
   return local_addr;
 }
 
+// True if |hostname| ends with either ".local" or ".local.".
+bool ResemblesMulticastDNSName(const std::string& hostname) {
+  return base::EndsWith(hostname, ".local") ||
+         base::EndsWith(hostname, ".local.");
+}
+
 }  // namespace
 
 DirectSocketsServiceImpl::DirectSocketsServiceImpl(RenderFrameHost& frame_host)
@@ -104,16 +111,22 @@
   void Start() {
     DCHECK(network_context_);
     DCHECK(!receiver_.is_bound());
+    DCHECK(!resolver_.is_bound());
 
     mojo::PendingRemote<network::mojom::HostResolver> pending_host_resolver;
-    mojo::Remote<network::mojom::HostResolver> resolver;
     network_context_->CreateHostResolver(
         base::nullopt, pending_host_resolver.InitWithNewPipeAndPassReceiver());
-    resolver.Bind(std::move(pending_host_resolver));
+    resolver_.Bind(std::move(pending_host_resolver));
 
-    resolver->ResolveHost(
+    network::mojom::ResolveHostParametersPtr parameters =
+        network::mojom::ResolveHostParameters::New();
+#if BUILDFLAG(ENABLE_MDNS)
+    if (ResemblesMulticastDNSName(*options_->remote_hostname))
+      parameters->source = net::HostResolverSource::MULTICAST_DNS;
+#endif  // !BUILDFLAG(ENABLE_MDNS)
+    resolver_->ResolveHost(
         net::HostPortPair(*options_->remote_hostname, options_->remote_port),
-        net::NetworkIsolationKey::CreateTransient(), nullptr,
+        net::NetworkIsolationKey::CreateTransient(), std::move(parameters),
         receiver_.BindNewPipeAndPassRemote());
     receiver_.set_disconnect_handler(
         base::BindOnce(&ResolveHostAndOpenSocket::OnComplete,
@@ -206,6 +219,7 @@
   OpenUdpSocketCallback udp_callback_;
 
   mojo::Receiver<network::mojom::ResolveHostClient> receiver_{this};
+  mojo::Remote<network::mojom::HostResolver> resolver_;
 };
 
 void DirectSocketsServiceImpl::OpenTcpSocket(
@@ -320,7 +334,6 @@
   if (!options.remote_hostname)
     return net::ERR_NAME_NOT_RESOLVED;
 
-  // TODO(crbug.com/1124255): Support mDNS.
   return net::OK;
 }
 
diff --git a/content/browser/federated_learning/floc_service_impl.cc b/content/browser/federated_learning/floc_service_impl.cc
index 8aabcac8..b2d17bf 100644
--- a/content/browser/federated_learning/floc_service_impl.cc
+++ b/content/browser/federated_learning/floc_service_impl.cc
@@ -6,7 +6,6 @@
 
 #include "base/bind.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
-#include "content/public/browser/browser_context.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/common/content_client.h"
@@ -33,12 +32,10 @@
 }
 
 void FlocServiceImpl::GetInterestCohort(GetInterestCohortCallback callback) {
-  BrowserContext* browser_context = render_frame_host_->GetBrowserContext();
-  DCHECK(browser_context);
-
   std::string interest_cohort =
       GetContentClient()->browser()->GetInterestCohortForJsApi(
-          browser_context, render_frame_host_->GetLastCommittedURL(),
+          WebContents::FromRenderFrameHost(render_frame_host_),
+          render_frame_host_->GetLastCommittedURL(),
           render_frame_host_->GetIsolationInfoForSubresources()
               .top_frame_origin());
 
diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc
index 2f94c42..714868e2 100644
--- a/content/browser/loader/navigation_url_loader_impl_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc
@@ -98,7 +98,8 @@
         std::move(client), /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params,
         /*coep_reporter=*/nullptr, 0, /* request_id */
-        0 /* keepalive_request_size */, resource_scheduler_client_,
+        0 /* keepalive_request_size */,
+        false /* require_network_isolation_key */, resource_scheduler_client_,
         nullptr /* keepalive_statistics_recorder */,
         nullptr /* network_usage_accumulator */, nullptr /* header_client */,
         nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc
index 1d5f203..6bb7c81 100644
--- a/content/browser/media/session/media_session_impl.cc
+++ b/content/browser/media/session/media_session_impl.cc
@@ -154,6 +154,19 @@
   return size.width() >= min_size || size.height() >= min_size;
 }
 
+bool IsSizesAtLeast(const std::vector<gfx::Size>& sizes, int min_size) {
+  // If we haven't found an image based on size then we should check if there
+  // are any images that have no size data or have an "any" size which is
+  // denoted by a single empty gfx::Size value.
+  if (sizes.size() == 0 || (sizes.size() == 1 && sizes[0].IsEmpty()))
+    return true;
+
+  bool check_size = false;
+  for (auto& size : sizes)
+    check_size = check_size || IsSizeAtLeast(size, min_size);
+  return check_size;
+}
+
 base::string16 SanitizeMediaTitle(const base::string16 title) {
   base::string16 out;
   base::TrimString(title, base::ASCIIToUTF16(" "), &out);
@@ -1108,12 +1121,7 @@
     }
   }
 
-  // Check that |image.sizes| contains a size that is above the minimum size.
-  bool check_size = false;
-  for (auto& size : image.sizes)
-    check_size = check_size || IsSizeAtLeast(size, minimum_size_px);
-
-  if (!found || !check_size) {
+  if (!found || !IsSizesAtLeast(image.sizes, minimum_size_px)) {
     std::move(callback).Run(SkBitmap());
     return;
   }
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index 71d657e..7854ada4 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -3314,19 +3314,10 @@
   url::ScopedSchemeRegistryForTests scoped_registry_;
 };
 
-// TODO(1021779): Figure out why this fails on the kitkat-dbg builder
-// and re-enable for all platforms.
-#if defined(OS_ANDROID)
-#define DISABLE_ON_ANDROID(x) DISABLED_##x
-#else
-#define DISABLE_ON_ANDROID(x) x
-#endif
-
 // Tests navigating to a URL that gets rewritten to a "no access" URL. This
 // mimics the behavior of navigating to special URLs like chrome://newtab and
 // chrome://history which get rewritten to "no access" chrome-native:// URLs.
-IN_PROC_BROWSER_TEST_F(NavigationUrlRewriteBrowserTest,
-                       DISABLE_ON_ANDROID(RewriteToNoAccess)) {
+IN_PROC_BROWSER_TEST_F(NavigationUrlRewriteBrowserTest, RewriteToNoAccess) {
   // Perform an initial navigation.
   {
     TestNavigationObserver observer(web_contents());
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 97fd6bda9..6872dce 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -341,8 +341,6 @@
   }
 
   root_window_ = root_window;
-  if (base::FeatureList::IsEnabled(features::kForce60HzRefreshRate))
-    root_window_->SetForce60HzRefreshRate();
   root_window_->SetLayer(root_layer ? root_layer : cc::Layer::Create());
   root_window_->GetLayer()->SetBounds(size_);
   root_window->AttachCompositor(this);
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 6c29eb1..30cd8e5 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -4,7 +4,9 @@
 
 #include "content/browser/renderer_host/navigation_request.h"
 
+#include <string>
 #include <utility>
+#include <vector>
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
@@ -684,12 +686,17 @@
 
   // Srcdoc subframes need to inherit their origin from their parent frame.
   if (navigation_request->GetURL().IsAboutSrcdoc()) {
-    // Srcdoc navigations in main frames are blocked before this function is
-    // called. This should guarantee existence of a parent here.
     RenderFrameHostImpl* parent =
         navigation_request->frame_tree_node()->parent();
-    DCHECK(parent);
-    return parent->GetLastCommittedOrigin();
+
+    // The `parent` may be missing if a renderer executes `location =
+    // "about:srcdoc` instead of embedding an <iframe srcdoc="..."></iframe>
+    // element.  Such case should use an error page with an opaque, unique
+    // origin.
+    //
+    // See also NavigationBrowserTest.BlockedSrcDoc* tests.
+    DCHECK(parent || navigation_request->GetNetErrorCode() != net::OK);
+    return parent ? parent->GetLastCommittedOrigin() : url::Origin();
   }
 
   // In cases not covered above, URLLoaderFactory should be associated with the
@@ -4510,8 +4517,12 @@
 }
 
 url::Origin NavigationRequest::GetOriginForURLLoaderFactory() {
+  // The origin to commit is not known until we get the final network response.
   DCHECK_GE(state_, WILL_PROCESS_RESPONSE);
 
+  if (IsSameDocument() || IsServedFromBackForwardCache())
+    return GetRenderFrameHost()->GetLastCommittedOrigin();
+
   // Calculate an approximation of the origin. The sandbox/csp are ignored.
   url::Origin origin = GetOriginForURLLoaderFactoryUnchecked(this);
 
@@ -4525,15 +4536,20 @@
   // This flag also prevents script from reading from or writing to the
   // document.cookie IDL attribute, and blocks access to localStorage.
   // ```
-  const bool use_opaque_origin = (sandbox_flags_to_commit_.value() &
-                                  network::mojom::WebSandboxFlags::kOrigin) ==
-                                 network::mojom::WebSandboxFlags::kOrigin;
+  bool use_opaque_origin = (sandbox_flags_to_commit_.value() &
+                            network::mojom::WebSandboxFlags::kOrigin) ==
+                           network::mojom::WebSandboxFlags::kOrigin;
+  // TODO(https://crbug.com/1158370): Move special-casing error pages into
+  // ComputeSandboxFlagsToCommit (and renderer-side origin calculations) so that
+  // the most strict sandbox flags are applied.
+  if (GetNetErrorCode() != net::OK)
+    use_opaque_origin = true;
   if (use_opaque_origin)
     origin = origin.DeriveNewOpaqueOrigin();
 
   // MHTML documents should commit as an opaque origin. They should not be able
   // to make network request on behalf of the real origin.
-  DCHECK(!IsMhtmlOrSubframe() || use_opaque_origin);
+  DCHECK(!IsMhtmlOrSubframe() || origin.opaque());
 
   // https://crbug.com/1041376) of the origin that will be committed because of
   // |this| NavigationRequest.
@@ -4984,6 +5000,11 @@
   return common_params().initiator_origin;
 }
 
+const std::vector<std::string>& NavigationRequest::GetDnsAliases() {
+  static const base::NoDestructor<std::vector<std::string>> emptyvector_result;
+  return response_head_ ? response_head_->dns_aliases : *emptyvector_result;
+}
+
 bool NavigationRequest::IsSameProcess() {
   return is_same_process_;
 }
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index 6f293b4c..c22481c 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -6,6 +6,8 @@
 #define CONTENT_BROWSER_RENDERER_HOST_NAVIGATION_REQUEST_H_
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/callback_forward.h"
@@ -323,6 +325,7 @@
       override;
   int GetInitiatorProcessID() override;
   const base::Optional<url::Origin>& GetInitiatorOrigin() override;
+  const std::vector<std::string>& GetDnsAliases() override;
   bool IsSameProcess() override;
   NavigationEntry* GetNavigationEntry() override;
   int GetNavigationEntryOffset() override;
diff --git a/content/browser/renderer_host/navigation_request_unittest.cc b/content/browser/renderer_host/navigation_request_unittest.cc
index a076ad2..8195aaa1 100644
--- a/content/browser/renderer_host/navigation_request_unittest.cc
+++ b/content/browser/renderer_host/navigation_request_unittest.cc
@@ -3,6 +3,10 @@
 // found in the LICENSE file.
 
 #include "content/browser/renderer_host/navigation_request.h"
+
+#include <string>
+#include <vector>
+
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/optional.h"
@@ -17,6 +21,7 @@
 #include "content/test/test_render_frame_host.h"
 #include "content/test/test_web_contents.h"
 #include "net/ssl/ssl_connection_status_flags.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 
 namespace content {
@@ -553,4 +558,54 @@
   }
 }
 
+TEST_F(NavigationRequestTest, DnsAliasesCanBeAccessed) {
+  // Create simulated NavigationRequest for the URL, which has aliases.
+  const GURL kUrl = GURL("http://chromium.org");
+  auto navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(kUrl, main_rfh());
+  std::vector<std::string> dns_aliases({"alias1", "alias2"});
+  navigation->SetResponseDnsAliases(std::move(dns_aliases));
+
+  // Start the navigation.
+  navigation->Start();
+  EXPECT_EQ(net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN,
+            navigation->GetNavigationHandle()->GetConnectionInfo());
+
+  // Commit the navigation.
+  navigation->set_http_connection_info(
+      net::HttpResponseInfo::CONNECTION_INFO_QUIC_35);
+  navigation->ReadyToCommit();
+  EXPECT_EQ(net::HttpResponseInfo::CONNECTION_INFO_QUIC_35,
+            navigation->GetNavigationHandle()->GetConnectionInfo());
+
+  // Verify that the aliases are accessible from the NavigationRequest.
+  EXPECT_THAT(navigation->GetNavigationHandle()->GetDnsAliases(),
+              testing::ElementsAre("alias1", "alias2"));
+}
+
+TEST_F(NavigationRequestTest, NoDnsAliases) {
+  // Create simulated NavigationRequest for the URL, which does not
+  // have aliases. (Note the empty alias list.)
+  const GURL kUrl = GURL("http://chromium.org");
+  auto navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(kUrl, main_rfh());
+  std::vector<std::string> dns_aliases;
+  navigation->SetResponseDnsAliases(std::move(dns_aliases));
+
+  // Start the navigation.
+  navigation->Start();
+  EXPECT_EQ(net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN,
+            navigation->GetNavigationHandle()->GetConnectionInfo());
+
+  // Commit the navigation.
+  navigation->set_http_connection_info(
+      net::HttpResponseInfo::CONNECTION_INFO_QUIC_35);
+  navigation->ReadyToCommit();
+  EXPECT_EQ(net::HttpResponseInfo::CONNECTION_INFO_QUIC_35,
+            navigation->GetNavigationHandle()->GetConnectionInfo());
+
+  // Verify that there are no aliases in the NavigationRequest.
+  EXPECT_TRUE(navigation->GetNavigationHandle()->GetDnsAliases().empty());
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 6855d3a..b1bab00 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -825,23 +825,25 @@
     NavigationRequest* navigation_request,
     const mojom::DidCommitProvisionalLoadParams& params) {
   DCHECK(navigation_request);
+  DCHECK(!navigation_request->IsSameDocument());
+  DCHECK(!navigation_request->IsServedFromBackForwardCache());
 
   // Ignore for now cases where the NavigationRequest is in an unexpectedly
-  // early state.  See also the NavigationRequestBrowserTest.VerifySameDocument
-  // test.
+  // early state. Triggered by the following tests:
+  // NavigationBrowserTest.OpenerNavigation_DownloadPolicy,
+  // WebContentsImplBrowserTest.NewNamedWindow.
   if (navigation_request->state() < NavigationRequest::WILL_PROCESS_RESPONSE)
     return;
 
-  // Ignore for now opaque |renderer_side_origin| origins.  This effectively
-  // ignores the following scenarios:
-  // - error frames (i.e. navigation_request->GetNetErrorCode() != net::OK;
-  //   see also the NavigationBrowserTest.FailedNavigation test)
-  // - sandboxed frames (see also https://crbug.com/1145139#c5)
-  // - comparison of precursor origins
+  // Check if both the renderer and browser expect an opaque origin. This
+  // effectively ignores the following:
+  // - precursor origins
   // - TODO(https://crbug.com/1041376): mismatched nonces (even if precursor
   //   origins would have matched)
   const url::Origin& renderer_side_origin = params.origin;
-  if (renderer_side_origin.opaque())
+  url::Origin browser_side_origin =
+      navigation_request->GetOriginForURLLoaderFactory();
+  if (renderer_side_origin.opaque() && browser_side_origin.opaque())
     return;
 
   // Ignore about:blank navigations, because browser-side calculated the origin
@@ -852,9 +854,8 @@
   if (navigation_request->GetURL().IsAboutBlank())
     return;
 
-  url::Origin browser_side_origin =
-      navigation_request->GetOriginForURLLoaderFactory();
-  DCHECK_EQ(browser_side_origin, renderer_side_origin);
+  DCHECK_EQ(browser_side_origin, renderer_side_origin)
+      << "; navigation_request->GetURL() = " << navigation_request->GetURL();
 }
 
 }  // namespace
@@ -7399,36 +7400,30 @@
   // |coep_reporter_receiver| is optional.
   DCHECK(out_trust_token_redemption_policy);
 
-  bool have_successful_request =
-      navigation_request && navigation_request->GetNetErrorCode() == net::OK;
-  bool have_failed_request =
-      navigation_request && navigation_request->GetNetErrorCode() != net::OK;
   CrossOriginEmbedderPolicyReporter* coep_reporter = nullptr;
 
-  if (have_successful_request) {
+  if (navigation_request) {
     // Return values based on the |navigation_request|.
     *out_main_world_origin = navigation_request->GetOriginForURLLoaderFactory();
-    *out_isolation_info = navigation_request->isolation_info_for_subresources();
     *out_client_security_state = navigation_request->BuildClientSecurityState();
-    coep_reporter = navigation_request->coep_reporter();
-    *out_trust_token_redemption_policy =
-        DetermineWhetherToForbidTrustTokenRedemption(
-            GetParent(), navigation_request->commit_params(),
-            *out_main_world_origin);
-  } else if (have_failed_request) {
-    // Some return values should always be the same for error pages.  Some
-    // return values may be based on the |navigation_request|.
 
-    // Error page will commit in an opaque origin.
-    // TODO(lukasza): https://crbug.com/888079: Use this origin when sending the
-    // commit IPC and setting |last_committed_origin_|.
-    url::Origin error_page_origin = url::Origin();
-    *out_main_world_origin = error_page_origin;
-    *out_isolation_info = net::IsolationInfo::CreateTransient();
-    *out_client_security_state = navigation_request->BuildClientSecurityState();
-    coep_reporter = nullptr;
-    *out_trust_token_redemption_policy =
-        network::mojom::TrustTokenRedemptionPolicy::kForbid;
+    // TODO(lukasza): Consider pushing the ok-vs-error differentiation into
+    // NavigationRequest methods (e.g. into |isolation_info_for_subresources|
+    // and/or |coep_reporter| methods).
+    if (navigation_request->GetNetErrorCode() == net::OK) {
+      *out_isolation_info =
+          navigation_request->isolation_info_for_subresources();
+      coep_reporter = navigation_request->coep_reporter();
+      *out_trust_token_redemption_policy =
+          DetermineWhetherToForbidTrustTokenRedemption(
+              GetParent(), navigation_request->commit_params(),
+              *out_main_world_origin);
+    } else {
+      *out_isolation_info = net::IsolationInfo::CreateTransient();
+      coep_reporter = nullptr;
+      *out_trust_token_redemption_policy =
+          network::mojom::TrustTokenRedemptionPolicy::kForbid;
+    }
   } else {
     // Use properties of the last committed navigation.
     *out_main_world_origin = last_committed_origin_;
@@ -8781,8 +8776,6 @@
 
   DCHECK(navigation_request);
   DCHECK(navigation_request->IsNavigationStarted());
-  VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch(
-      navigation_request.get(), *params);
   VerifyThatBrowserAndRendererCalculatedDidCommitParamsMatch(
       navigation_request.get(), *params, same_document_params.Clone());
 
@@ -8970,6 +8963,11 @@
     // This should be fixed and the DCHECK restored.
   }
 
+  // TODO(https://crbug.com/888079): The origin computed from the browser must
+  // match the one reported from the renderer process.
+  VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch(navigation_request,
+                                                             params);
+
   coep_reporter_ = navigation_request->TakeCoepReporter();
   if (coep_reporter_) {
     mojo::PendingRemote<blink::mojom::ReportingObserver> remote;
diff --git a/content/browser/renderer_host/render_frame_host_manager_unittest.cc b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
index 1331347..95a892b4 100644
--- a/content/browser/renderer_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/files/file_path.h"
 #include "base/hash/hash.h"
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -61,6 +62,7 @@
 #include "content/test/test_render_widget_host.h"
 #include "content/test/test_web_contents.h"
 #include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/frame/frame_policy.h"
 #include "third_party/blink/public/common/loader/previews_state.h"
@@ -2805,7 +2807,14 @@
   for (const auto& test_case : cases) {
     auto navigation = NavigationSimulatorImpl::CreateBrowserInitiated(
         GURL(test_case.url), contents());
-    navigation->set_origin(url::Origin::Create(GURL(test_case.origin)));
+    url::Origin origin = url::Origin::Create(GURL(test_case.origin));
+    navigation->set_origin(origin);
+    if (origin.opaque()) {
+      auto response_headers =
+          base::MakeRefCounted<net::HttpResponseHeaders>(std::string());
+      response_headers->SetHeader("Content-Security-Policy", "sandbox");
+      navigation->SetResponseHeaders(response_headers);
+    }
     navigation->ReadyToCommit();
 
     int expected_bad_msg_count =
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.cc b/content/browser/service_worker/service_worker_controllee_request_handler.cc
index 485cfc6c..94572f5 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.cc
@@ -118,15 +118,17 @@
     BrowserContext* browser_context,
     NavigationLoaderInterceptor::LoaderCallback loader_callback,
     NavigationLoaderInterceptor::FallbackCallback fallback_callback) {
-  // InitializeContainerHost() will update the host. This is important to do
-  // before falling back to network below, so service worker APIs still work
-  // even if the service worker is bypassed for request interception.
-  if (!InitializeContainerHost(tentative_resource_request)) {
+  if (!container_host_) {
     // We can't do anything other than to fall back to network.
     std::move(loader_callback).Run({});
     return;
   }
 
+  // Update the host. This is important to do before falling back to network
+  // below, so service worker APIs still work even if the service worker is
+  // bypassed for request interception.
+  InitializeContainerHost(tentative_resource_request);
+
   // Fall back to network if we were instructed to bypass the service worker for
   // request interception, or if the context is gone so we have to bypass
   // anyway.
@@ -173,14 +175,8 @@
           weak_factory_.GetWeakPtr()));
 }
 
-bool ServiceWorkerControlleeRequestHandler::InitializeContainerHost(
+void ServiceWorkerControlleeRequestHandler::InitializeContainerHost(
     const network::ResourceRequest& tentative_resource_request) {
-  ClearJob();
-
-  if (!container_host_) {
-    return false;
-  }
-
   // Update the container host with this request, clearing old controller state
   // if this is a redirect.
   container_host_->SetControllerRegistration(nullptr,
@@ -192,7 +188,6 @@
                                   ? tentative_resource_request.trusted_params
                                         ->isolation_info.top_frame_origin()
                                   : base::nullopt);
-  return true;
 }
 
 void ServiceWorkerControlleeRequestHandler::ContinueWithRegistration(
@@ -520,16 +515,6 @@
       weak_factory_.GetWeakPtr(), std::move(registration), version));
 }
 
-void ServiceWorkerControlleeRequestHandler::ClearJob() {
-  // Invalidate weak pointers to cancel RegisterStatusChangeCallback().
-  // Otherwise we may end up calling ForwardToServiceWorer()
-  // or FallbackToNetwork() twice on the same |loader()|.
-  // TODO(bashi): Consider not to reuse this handler when restarting the
-  // request after S13nServiceWorker is shipped.
-  weak_factory_.InvalidateWeakPtrs();
-  loader_wrapper_.reset();
-}
-
 void ServiceWorkerControlleeRequestHandler::CompleteWithoutLoader() {
   std::move(loader_callback_).Run({});
 }
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.h b/content/browser/service_worker/service_worker_controllee_request_handler.h
index e9b5c26..f245aa97 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.h
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.h
@@ -31,12 +31,15 @@
 class ServiceWorkerRegistration;
 class ServiceWorkerVersion;
 
-// Handles main resource requests for service worker clients (documents and
-// shared workers).
+// Handles a main resource request for service worker clients (documents and
+// shared workers). This manages state for a single request and does not
+// live across redirects. ServiceWorkerMainResourceLoaderInterceptor creates
+// one instance of this class for each request/redirect.
 //
-// TODO(crbug.com/1138155): Merge into
-// ServiceWorkerMainResourceLoaderInterceptor now that they are on the same
-// thread.
+// This class associates the ServiceWorkerContainerHost undergoing navigation
+// with a controller service worker, after looking up the registration and
+// activating the service worker if needed.  Once ready, it creates
+// ServiceWorkerMainResourceLoader to perform the resource load.
 class CONTENT_EXPORT ServiceWorkerControlleeRequestHandler final {
  public:
   // If |skip_service_worker| is true, service workers are bypassed for
@@ -49,19 +52,14 @@
       ServiceWorkerAccessedCallback service_worker_accessed_callback);
   ~ServiceWorkerControlleeRequestHandler();
 
-  // This could get called multiple times during the lifetime in redirect
-  // cases. (In fallback-to-network cases we basically forward the request
-  // to the request to the next request handler)
+  // This is called only once. On redirects, a new instance of this
+  // class is created.
   void MaybeCreateLoader(
       const network::ResourceRequest& tentative_request,
       BrowserContext* browser_context,
       NavigationLoaderInterceptor::LoaderCallback loader_callback,
       NavigationLoaderInterceptor::FallbackCallback fallback_callback);
 
-  // Does all initialization of |container_host_| for a request.
-  bool InitializeContainerHost(
-      const network::ResourceRequest& tentative_request);
-
   // Exposed for testing.
   ServiceWorkerMainResourceLoader* loader() {
     return loader_wrapper_ ? loader_wrapper_->get() : nullptr;
@@ -71,6 +69,10 @@
   FRIEND_TEST_ALL_PREFIXES(ServiceWorkerControlleeRequestHandlerTest,
                            ActivateWaitingVersion);
 
+  // Does all initialization of |container_host_| for a request.
+  void InitializeContainerHost(
+      const network::ResourceRequest& tentative_request);
+
   void ContinueWithRegistration(
       blink::ServiceWorkerStatusCode status,
       scoped_refptr<ServiceWorkerRegistration> registration);
@@ -88,10 +90,6 @@
       scoped_refptr<ServiceWorkerRegistration> registration,
       scoped_refptr<ServiceWorkerVersion> version);
 
-  // Sets |job_| to nullptr, and clears all extra response info associated with
-  // that job, except for timing information.
-  void ClearJob();
-
   void CompleteWithoutLoader();
 
   // Schedules a service worker update to occur shortly after the page and its
diff --git a/content/browser/service_worker/service_worker_main_resource_handle.h b/content/browser/service_worker/service_worker_main_resource_handle.h
index cd904fb1..f7877f62 100644
--- a/content/browser/service_worker/service_worker_main_resource_handle.h
+++ b/content/browser/service_worker/service_worker_main_resource_handle.h
@@ -9,7 +9,6 @@
 #include "base/memory/weak_ptr.h"
 #include "content/browser/service_worker/service_worker_accessed_callback.h"
 #include "content/browser/service_worker/service_worker_container_host.h"
-#include "content/browser/service_worker/service_worker_controllee_request_handler.h"
 #include "content/common/content_export.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/network/public/mojom/network_context.mojom.h"
@@ -115,15 +114,6 @@
     return parent_container_host_;
   }
 
-  void set_interceptor(
-      std::unique_ptr<ServiceWorkerControlleeRequestHandler> interceptor) {
-    interceptor_ = std::move(interceptor);
-  }
-
-  ServiceWorkerControlleeRequestHandler* interceptor() {
-    return interceptor_.get();
-  }
-
   const ServiceWorkerAccessedCallback& service_worker_accessed_callback() {
     return service_worker_accessed_callback_;
   }
@@ -144,8 +134,6 @@
   // Only used for workers with a blob URL.
   base::WeakPtr<ServiceWorkerContainerHost> parent_container_host_;
 
-  std::unique_ptr<ServiceWorkerControlleeRequestHandler> interceptor_;
-
   ServiceWorkerAccessedCallback service_worker_accessed_callback_;
 
   scoped_refptr<ServiceWorkerContextWrapper> context_wrapper_;
diff --git a/content/browser/service_worker/service_worker_main_resource_loader_interceptor.cc b/content/browser/service_worker/service_worker_main_resource_loader_interceptor.cc
index 9d31f41..fe85f03 100644
--- a/content/browser/service_worker/service_worker_main_resource_loader_interceptor.cc
+++ b/content/browser/service_worker/service_worker_main_resource_loader_interceptor.cc
@@ -16,7 +16,6 @@
 #include "content/browser/service_worker/service_worker_container_host.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
-#include "content/browser/service_worker/service_worker_controllee_request_handler.h"
 #include "content/browser/service_worker/service_worker_main_resource_handle.h"
 #include "content/browser/service_worker/service_worker_object_host.h"
 #include "content/public/common/content_client.h"
@@ -148,7 +147,7 @@
     // `container_info`.
     DCHECK(!handle_->container_host());
     base::WeakPtr<ServiceWorkerContainerHost> container_host;
-    bool inherit_container_host_only = false;
+    bool inherit_controller_only = false;
 
     if (request_destination_ == network::mojom::RequestDestination::kDocument ||
         request_destination_ == network::mojom::RequestDestination::kIframe) {
@@ -177,34 +176,29 @@
           tentative_resource_request.url.SchemeIsBlob()) {
         container_host->InheritControllerFrom(*parent_container_host,
                                               tentative_resource_request.url);
-        inherit_container_host_only = true;
+        inherit_controller_only = true;
       }
     }
     DCHECK(container_host);
     handle_->set_container_host(container_host);
 
-    // Also make the inner interceptor.
-    DCHECK(!handle_->interceptor());
-    handle_->set_interceptor(
-        std::make_unique<ServiceWorkerControlleeRequestHandler>(
-            context_core->AsWeakPtr(), container_host, request_destination_,
-            skip_service_worker_, handle_->service_worker_accessed_callback()));
-
-    // For the blob worker case, we only inherit the controller and do not
-    // let it intercept the requests. Blob URLs are not eligible to go through
-    // service worker interception. So just call the loader callback now.
-    // We don't use the interceptor but have to set it because we need
-    // ControllerServiceWorkerInfoPtr and ServiceWorkerObjectHost from the
-    // subresource loader params which is created by the interceptor.
-    if (inherit_container_host_only) {
+    // For the blob worker case, we only inherit the controller and do not let
+    // it intercept the main resource request. Blob URLs are not eligible to
+    // go through service worker interception. So just call the loader
+    // callback now.
+    if (inherit_controller_only) {
       std::move(loader_callback).Run(/*handler=*/{});
       return;
     }
   }
 
-  // Start the inner interceptor. It will invoke the loader callback
-  // or fallback callback.
-  handle_->interceptor()->MaybeCreateLoader(
+  // Create and start the handler for this request. It will invoke the loader
+  // callback or fallback callback.
+  request_handler_ = std::make_unique<ServiceWorkerControlleeRequestHandler>(
+      context_core->AsWeakPtr(), handle_->container_host(),
+      request_destination_, skip_service_worker_,
+      handle_->service_worker_accessed_callback());
+  request_handler_->MaybeCreateLoader(
       tentative_resource_request, browser_context, std::move(loader_callback),
       std::move(fallback_callback));
 }
diff --git a/content/browser/service_worker/service_worker_main_resource_loader_interceptor.h b/content/browser/service_worker/service_worker_main_resource_loader_interceptor.h
index 8997c46..67a2447d 100644
--- a/content/browser/service_worker/service_worker_main_resource_loader_interceptor.h
+++ b/content/browser/service_worker/service_worker_main_resource_loader_interceptor.h
@@ -13,6 +13,7 @@
 #include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/browser/navigation_subresource_loader_params.h"
+#include "content/browser/service_worker/service_worker_controllee_request_handler.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/service_worker_client_info.h"
@@ -126,6 +127,9 @@
   const int process_id_;
   const base::Optional<DedicatedOrSharedWorkerToken> worker_token_;
 
+  // Handles a single request. Set to a new instance on redirects.
+  std::unique_ptr<ServiceWorkerControlleeRequestHandler> request_handler_;
+
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerMainResourceLoaderInterceptor);
 };
 
diff --git a/content/browser/speech/tts_controller_impl.cc b/content/browser/speech/tts_controller_impl.cc
index 8715e8e..421ec2db 100644
--- a/content/browser/speech/tts_controller_impl.cc
+++ b/content/browser/speech/tts_controller_impl.cc
@@ -15,6 +15,7 @@
 #include "base/json/json_reader.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
+#include "base/strings/string_util.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -691,11 +692,13 @@
 
     // Prefer the utterance language.
     if (!voice.lang.empty() && !utterance->GetLang().empty()) {
-      // An exact language match is worth more than a partial match.
-      if (voice.lang == utterance->GetLang()) {
+      // An exact locale match is worth more than a partial match.
+      // Convert locales to lowercase to handle cases like "en-us" vs. "en-US".
+      if (base::EqualsCaseInsensitiveASCII(voice.lang, utterance->GetLang())) {
         score += 128;
-      } else if (l10n_util::GetLanguage(voice.lang) ==
-                 l10n_util::GetLanguage(utterance->GetLang())) {
+      } else if (base::EqualsCaseInsensitiveASCII(
+                     l10n_util::GetLanguage(voice.lang),
+                     l10n_util::GetLanguage(utterance->GetLang()))) {
         score += 64;
       }
     }
@@ -739,8 +742,9 @@
     if (!voice.lang.empty()) {
       if (voice.lang == app_lang) {
         score += 2;
-      } else if (l10n_util::GetLanguage(voice.lang) ==
-                 l10n_util::GetLanguage(app_lang)) {
+      } else if (base::EqualsCaseInsensitiveASCII(
+                     l10n_util::GetLanguage(voice.lang),
+                     l10n_util::GetLanguage(app_lang))) {
         score += 1;
       }
     }
diff --git a/content/browser/speech/tts_controller_unittest.cc b/content/browser/speech/tts_controller_unittest.cc
index 31dd5bc..2f1e8c0 100644
--- a/content/browser/speech/tts_controller_unittest.cc
+++ b/content/browser/speech/tts_controller_unittest.cc
@@ -510,6 +510,50 @@
     EXPECT_EQ(0, controller()->GetMatchingVoice(utterance.get(), voices));
 #endif
   }
+
+  {
+    // This block ensures that voices can be matched, even if their locale's
+    // casing doesn't exactly match that of the utterance e.g. "en-us" will
+    // match with "en-US".
+    std::vector<VoiceData> voices;
+    VoiceData voice0;
+    voice0.engine_id = "id0";
+    voice0.name = "English voice";
+    voice0.lang = "en-US";
+    voices.push_back(voice0);
+    VoiceData voice1;
+    voice1.engine_id = "id0";
+    voice1.name = "French voice";
+    voice1.lang = "fr";
+    voices.push_back(voice1);
+
+    std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create());
+    utterance->SetLang("en-us");
+    EXPECT_EQ(0, controller()->GetMatchingVoice(utterance.get(), voices));
+    utterance->SetLang("en-US");
+    EXPECT_EQ(0, controller()->GetMatchingVoice(utterance.get(), voices));
+    utterance->SetLang("EN-US");
+    EXPECT_EQ(0, controller()->GetMatchingVoice(utterance.get(), voices));
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    // Add another English voice.
+    VoiceData voice2;
+    voice2.engine_id = "id1";
+    voice2.name = "Another English voice";
+    voice2.lang = "en-us";
+    voices.push_back(voice2);
+
+    // Set voice2 as the preferred voice for English.
+    TtsControllerDelegate::PreferredVoiceIds preferred_voice_ids;
+    preferred_voice_ids.lang_voice_id.emplace(voice2.name, voice2.engine_id);
+    delegate()->SetPreferredVoiceIds(preferred_voice_ids);
+
+    // Ensure that voice2 is chosen over voice0, even though the locales don't
+    // match exactly. The utterance has a locale of "en-US", while voice2 has
+    // a locale of "en-us"; this shouldn't prevent voice2 from being used.
+    EXPECT_EQ(2, controller()->GetMatchingVoice(utterance.get(), voices));
+#endif
+  }
 }
 
 TEST_F(TtsControllerTest, StopsWhenWebContentsDestroyed) {
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index c366ae5..96b405fd 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -1045,7 +1045,7 @@
     NavigationDownloadPolicy* download_policy) {}
 
 std::string ContentBrowserClient::GetInterestCohortForJsApi(
-    content::BrowserContext* browser_context,
+    WebContents* web_contents,
     const GURL& url,
     const base::Optional<url::Origin>& top_frame_origin) {
   return std::string();
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 0cd721c..03f8c36 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1814,9 +1814,10 @@
       bool user_gesture,
       NavigationDownloadPolicy* download_policy);
 
-  // Returns the interest cohort associated with the |browser_context|.
+  // Returns the interest cohort associated with the browser context of
+  // |web_contents|.
   virtual std::string GetInterestCohortForJsApi(
-      content::BrowserContext* browser_context,
+      WebContents* web_contents,
       const GURL& url,
       const base::Optional<url::Origin>& top_frame_origin);
 
diff --git a/content/public/browser/navigation_handle.h b/content/public/browser/navigation_handle.h
index 867387d..160f243 100644
--- a/content/public/browser/navigation_handle.h
+++ b/content/public/browser/navigation_handle.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "content/common/content_export.h"
 #include "content/public/browser/navigation_handle_timing.h"
@@ -385,6 +386,11 @@
   // navigation for this NavigationHandle.
   virtual const base::Optional<url::Origin>& GetInitiatorOrigin() = 0;
 
+  // Retrieves any DNS aliases for the requested URL. The alias chain order
+  // is preserved in reverse, from canonical name (i.e. address record name)
+  // through to query name.
+  virtual const std::vector<std::string>& GetDnsAliases() = 0;
+
   // Whether the new document will be hosted in the same process as the current
   // document or not. Set only when the navigation commits.
   virtual bool IsSameProcess() = 0;
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 9884308..1801553c 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -877,10 +877,6 @@
     "BackgroundMediaRendererHasModerateBinding",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Force display to tick at ~60Hz refresh rate.
-const base::Feature kForce60HzRefreshRate{"Force60HzRefreshRate",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Screen Capture API support for Android
 const base::Feature kUserMediaScreenCapturing{
     "UserMediaScreenCapturing", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 9f7bf5a9..7d22686 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -201,7 +201,6 @@
 CONTENT_EXPORT extern const base::Feature kAndroidAutofillAccessibility;
 CONTENT_EXPORT extern const base::Feature
     kBackgroundMediaRendererHasModerateBinding;
-CONTENT_EXPORT extern const base::Feature kForce60HzRefreshRate;
 CONTENT_EXPORT extern const base::Feature kUserMediaScreenCapturing;
 CONTENT_EXPORT extern const base::Feature kWarmUpNetworkProcess;
 CONTENT_EXPORT extern const base::Feature kWebNfc;
diff --git a/content/public/test/mock_navigation_handle.h b/content/public/test/mock_navigation_handle.h
index df996d3..4440d5e 100644
--- a/content/public/test/mock_navigation_handle.h
+++ b/content/public/test/mock_navigation_handle.h
@@ -5,7 +5,11 @@
 #ifndef CONTENT_PUBLIC_TEST_MOCK_NAVIGATION_HANDLE_H_
 #define CONTENT_PUBLIC_TEST_MOCK_NAVIGATION_HANDLE_H_
 
+#include <string>
+#include <vector>
+
 #include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 #include "content/public/browser/global_request_id.h"
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/navigation_handle.h"
@@ -130,6 +134,11 @@
   const base::Optional<url::Origin>& GetInitiatorOrigin() override {
     return initiator_origin_;
   }
+  const std::vector<std::string>& GetDnsAliases() override {
+    static const base::NoDestructor<std::vector<std::string>>
+        emptyvector_result;
+    return *emptyvector_result;
+  }
   MOCK_METHOD(void,
               RegisterThrottleForTesting,
               (std::unique_ptr<NavigationThrottle>));
diff --git a/content/public/test/navigation_simulator.h b/content/public/test/navigation_simulator.h
index cf2acb5..2761c57 100644
--- a/content/public/test/navigation_simulator.h
+++ b/content/public/test/navigation_simulator.h
@@ -305,6 +305,12 @@
   // Commit().
   virtual void SetSSLInfo(const net::SSLInfo& ssl_info) = 0;
 
+  // Sets the DNS aliases to be received in the URLResponseHead. The aliases
+  // are what would be read from DNS CNAME records, and the alias chain should
+  // be preserved in reverse order, from canonical name (i.e. address record
+  // name) through to query name. This method should be called before Commit().
+  virtual void SetResponseDnsAliases(std::vector<std::string> aliases) = 0;
+
   // --------------------------------------------------------------------------
 
   // Gets the last throttle check result computed by the navigation throttles.
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index b13f677..af3294e 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -265,14 +265,9 @@
   if (!document.IsNull()) {
     StartOrStopLabelingImages(old_mode, mode);
 
-    // If there are any events in flight, |HandleAXEvent| will refuse to process
-    // our new event.
-    pending_events_.clear();
-    auto root_object = WebAXObject::FromWebDocument(document, false);
-    ax::mojom::Event event = root_object.IsLoaded()
-                                 ? ax::mojom::Event::kLoadComplete
-                                 : ax::mojom::Event::kLayoutComplete;
-    HandleAXEvent(ui::AXEvent(root_object.AxID(), event));
+    needs_initial_ax_tree_root_ = true;
+    event_schedule_mode_ = EventScheduleMode::kProcessEventsImmediately;
+    ScheduleSendPendingAccessibilityEvents();
   }
 }
 
@@ -476,11 +471,11 @@
   if (!document.IsNull()) {
     // Tree-only mode gets used by the automation extension API which requires a
     // load complete event to invoke listener callbacks.
-    auto root_object = WebAXObject::FromWebDocument(document, false);
-    ax::mojom::Event event = root_object.IsLoaded()
-                                 ? ax::mojom::Event::kLoadComplete
-                                 : ax::mojom::Event::kLayoutComplete;
-    HandleAXEvent(ui::AXEvent(root_object.AxID(), event));
+    // SendPendingAccessibilityEvents() will fire the load complete event
+    // if the page is loaded.
+    needs_initial_ax_tree_root_ = true;
+    event_schedule_mode_ = EventScheduleMode::kProcessEventsImmediately;
+    ScheduleSendPendingAccessibilityEvents();
   }
 }
 
@@ -797,10 +792,20 @@
     // complete for the entire document, in order to initialize the browser's
     // cached accessibility tree.
     needs_initial_ax_tree_root_ = false;
-    auto obj = WebAXObject::FromWebDocument(document);
+    auto root_obj = WebAXObject::FromWebDocument(document);
+    // Always fire layout complete for a new root object.
     pending_events_.insert(
         pending_events_.begin(),
-        ui::AXEvent(obj.AxID(), ax::mojom::Event::kLayoutComplete));
+        ui::AXEvent(root_obj.AxID(), ax::mojom::Event::kLayoutComplete));
+
+    // If loaded and has some content, insert load complete at the top, so that
+    // screen readers are informed a new document is ready.
+    if (root_obj.IsLoaded() && !document.Body().IsNull() &&
+        !document.Body().FirstChild().IsNull()) {
+      pending_events_.insert(
+          pending_events_.begin(),
+          ui::AXEvent(root_obj.AxID(), ax::mojom::Event::kLoadComplete));
+    }
   }
 
   if (pending_events_.empty() && dirty_objects_.empty()) {
@@ -834,8 +839,8 @@
   // location changes.
   bool need_to_send_location_changes = false;
 
-  // If there's a load complete message, we need to change the event schedule
-  // mode.
+  // Keep track of load complete messages. When a load completes, it's a good
+  // time to inject a stylesheet for image annotation debugging.
   bool had_load_complete_messages = false;
 
   ScopedFreezeBlinkAXTreeSource freeze(tree_source_.get());
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index c2efa9d1..352a04b 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1563,10 +1563,8 @@
       "../browser/site_per_process_mac_browsertest.mm",
       "../renderer/render_view_browsertest_mac.mm",
     ]
-    deps += [
-      "//content/shell:content_shell",
-      "//third_party/ocmock",
-    ]
+    deps += [ "//third_party/ocmock" ]
+    data_deps += [ "//content/shell:content_shell" ]
     data += [ "$root_out_dir/Content Shell.app/" ]
   }
 
diff --git a/content/test/data/accessibility/aom/aom-modal-dialog-expected-android.txt b/content/test/data/accessibility/aom/aom-modal-dialog-expected-android.txt
new file mode 100644
index 0000000..3d832796
--- /dev/null
+++ b/content/test/data/accessibility/aom/aom-modal-dialog-expected-android.txt
@@ -0,0 +1,6 @@
+android.webkit.WebView focusable focused scrollable
+++android.view.View
+++++android.widget.TextView name='Content outside modal dialog. '
+++++android.widget.Button role_description='button' clickable focusable name='Button outside modal dialog.'
+++android.app.Dialog role_description='dialog' name='Modal dialog.'
+++++android.widget.Button role_description='button' clickable name='Button inside modal dialog.'
diff --git a/content/test/data/accessibility/aom/aom-modal-dialog-expected-auralinux.txt b/content/test/data/accessibility/aom/aom-modal-dialog-expected-auralinux.txt
new file mode 100644
index 0000000..9b09d395
--- /dev/null
+++ b/content/test/data/accessibility/aom/aom-modal-dialog-expected-auralinux.txt
@@ -0,0 +1,6 @@
+[document web]
+++[section]
+++++[static] name='Content outside modal dialog. '
+++++[push button] name='Button outside modal dialog.'
+++[dialog] name='Modal dialog.' modal
+++++[push button] name='Button inside modal dialog.'
diff --git a/content/test/data/accessibility/aom/aom-modal-dialog-expected-blink.txt b/content/test/data/accessibility/aom/aom-modal-dialog-expected-blink.txt
new file mode 100644
index 0000000..032a717
--- /dev/null
+++ b/content/test/data/accessibility/aom/aom-modal-dialog-expected-blink.txt
@@ -0,0 +1,11 @@
+rootWebArea
+++genericContainer ignored
+++++genericContainer ignored
+++++++genericContainer
+++++++++staticText name='Content outside modal dialog. '
+++++++++++inlineTextBox name='Content outside modal dialog. '
+++++++++button name='Button outside modal dialog.'
+++++++++++staticText name='Button outside modal dialog.'
+++++++++++++inlineTextBox name='Button outside modal dialog.'
+++++++dialog name='Modal dialog.' modal=true
+++++++++button name='Button inside modal dialog.'
diff --git a/content/test/data/accessibility/aom/aom-modal-dialog-expected-mac.txt b/content/test/data/accessibility/aom/aom-modal-dialog-expected-mac.txt
new file mode 100644
index 0000000..90b0ce92
--- /dev/null
+++ b/content/test/data/accessibility/aom/aom-modal-dialog-expected-mac.txt
@@ -0,0 +1,6 @@
+AXWebArea
+++AXGroup
+++++AXStaticText AXValue='Content outside modal dialog. '
+++++AXButton AXTitle='Button outside modal dialog.'
+++AXGroup AXSubrole=AXApplicationDialog AXDescription='Modal dialog.'
+++++AXButton AXDescription='Button inside modal dialog.'
diff --git a/content/test/data/accessibility/aom/aom-modal-dialog-expected-uia-win.txt b/content/test/data/accessibility/aom/aom-modal-dialog-expected-uia-win.txt
new file mode 100644
index 0000000..cb7d5e84
--- /dev/null
+++ b/content/test/data/accessibility/aom/aom-modal-dialog-expected-uia-win.txt
@@ -0,0 +1,6 @@
+Document LocalizedControlType='document'
+++Group LocalizedControlType='group' IsControlElement=false
+++++Text LocalizedControlType='text' Name='Content outside modal dialog. '
+++++Button LocalizedControlType='button' Name='Button outside modal dialog.'
+++Pane LocalizedControlType='dialog' Name='Modal dialog.' Window.IsModal=true
+++++Button LocalizedControlType='button' Name='Button inside modal dialog.'
diff --git a/content/test/data/accessibility/aom/aom-modal-dialog-expected-uia-win7.txt b/content/test/data/accessibility/aom/aom-modal-dialog-expected-uia-win7.txt
new file mode 100644
index 0000000..d0369093
--- /dev/null
+++ b/content/test/data/accessibility/aom/aom-modal-dialog-expected-uia-win7.txt
@@ -0,0 +1,6 @@
+Document LocalizedControlType='document'
+++Group LocalizedControlType='group' IsControlElement=false
+++++Text LocalizedControlType='text' Name='Content outside modal dialog. '
+++++Button LocalizedControlType='button' Name='Button outside modal dialog.'
+++Pane LocalizedControlType='pane' Name='Modal dialog.' Window.IsModal=true
+++++Button LocalizedControlType='button' Name='Button inside modal dialog.'
diff --git a/content/test/data/accessibility/aom/aom-modal-dialog-expected-win.txt b/content/test/data/accessibility/aom/aom-modal-dialog-expected-win.txt
new file mode 100644
index 0000000..45773d2
--- /dev/null
+++ b/content/test/data/accessibility/aom/aom-modal-dialog-expected-win.txt
@@ -0,0 +1,6 @@
+ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
+++IA2_ROLE_SECTION
+++++ROLE_SYSTEM_STATICTEXT name='Content outside modal dialog. '
+++++ROLE_SYSTEM_PUSHBUTTON name='Button outside modal dialog.' FOCUSABLE
+++ROLE_SYSTEM_DIALOG name='Modal dialog.' IA2_STATE_MODAL
+++++ROLE_SYSTEM_PUSHBUTTON name='Button inside modal dialog.'
diff --git a/content/test/data/accessibility/aom/aom-modal-dialog.html b/content/test/data/accessibility/aom/aom-modal-dialog.html
new file mode 100644
index 0000000..622a9e8
--- /dev/null
+++ b/content/test/data/accessibility/aom/aom-modal-dialog.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!--
+@BLINK-ALLOW:modal*
+@WIN-ALLOW:IA2_STATE_MODAL
+@WIN-ALLOW:container*
+@UIA-WIN-ALLOW:Window.IsModal*
+@UIA-WIN-ALLOW:LocalizedControlType*
+@MAC-ALLOW:AXSubrole
+@AURALINUX-ALLOW:modal*
+
+TODO: For Mac and Android, we need to prune the content outside modal dialog.
+http://crbug.com/1165298
+-->
+<html>
+<body>
+  <div>
+   Content outside modal dialog.
+   <button>Button outside modal dialog.</button>
+  </div>
+  <script>
+    var button_node = new AccessibleNode();
+    button_node.role = "button";
+    button_node.label = "Button inside modal dialog.";
+
+    var dialog_node = new AccessibleNode();
+    dialog_node.role = "dialog";
+    dialog_node.modal = "true";
+    dialog_node.label = "Modal dialog."
+    document.body.accessibleNode.appendChild(dialog_node);
+    dialog_node.appendChild(button_node);
+  </script>
+</body>
+</html>
diff --git a/content/test/navigation_simulator_impl.cc b/content/test/navigation_simulator_impl.cc
index 16db4797..e70b71f 100644
--- a/content/test/navigation_simulator_impl.cc
+++ b/content/test/navigation_simulator_impl.cc
@@ -527,7 +527,7 @@
         ->PrepareForCommitDeprecatedForNavigationSimulator(
             remote_endpoint_, was_fetched_via_cache_,
             is_signed_exchange_inner_response_, http_connection_info_,
-            ssl_info_, response_headers_);
+            ssl_info_, response_headers_, response_dns_aliases_);
   }
 
   // Synchronous failure can cause the navigation to finish here.
@@ -949,6 +949,11 @@
   ssl_info_ = ssl_info;
 }
 
+void NavigationSimulatorImpl::SetResponseDnsAliases(
+    std::vector<std::string> aliases) {
+  response_dns_aliases_ = std::move(aliases);
+}
+
 NavigationThrottle::ThrottleCheckResult
 NavigationSimulatorImpl::GetLastThrottleCheckResult() {
   return last_throttle_check_result_.value();
diff --git a/content/test/navigation_simulator_impl.h b/content/test/navigation_simulator_impl.h
index d07c629..8591616 100644
--- a/content/test/navigation_simulator_impl.h
+++ b/content/test/navigation_simulator_impl.h
@@ -94,6 +94,7 @@
   void SetResolveErrorInfo(
       const net::ResolveErrorInfo& resolve_error_info) override;
   void SetSSLInfo(const net::SSLInfo& ssl_info) override;
+  void SetResponseDnsAliases(std::vector<std::string> aliases) override;
 
   NavigationThrottle::ThrottleCheckResult GetLastThrottleCheckResult() override;
   NavigationRequest* GetNavigationHandle() override;
@@ -296,6 +297,12 @@
   base::Optional<Impression> impression_;
   int64_t post_id_ = -1;
 
+  // Any DNS aliases, as read from CNAME records, for the request URL that
+  // would be in the network::mojom::URLResponseHead. The alias chain order
+  // is preserved in reverse, from canonical name (i.e. address record name)
+  // through to query name.
+  std::vector<std::string> response_dns_aliases_;
+
   bool auto_advance_ = true;
   bool drop_unload_ack_ = false;
   bool block_invoking_before_unload_completed_callback_ = false;
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index de9e3c15..34e1356 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -346,7 +346,7 @@
                            /* was_fetched_via_cache=*/false,
                            /* is_signed_exchange_inner_response=*/false,
                            net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN,
-                           base::nullopt, nullptr);
+                           base::nullopt, nullptr, {} /* dns_aliases */);
 }
 
 void TestRenderFrameHost::PrepareForCommitDeprecatedForNavigationSimulator(
@@ -355,10 +355,11 @@
     bool is_signed_exchange_inner_response,
     net::HttpResponseInfo::ConnectionInfo connection_info,
     base::Optional<net::SSLInfo> ssl_info,
-    scoped_refptr<net::HttpResponseHeaders> response_headers) {
+    scoped_refptr<net::HttpResponseHeaders> response_headers,
+    const std::vector<std::string>& dns_aliases) {
   PrepareForCommitInternal(remote_endpoint, was_fetched_via_cache,
                            is_signed_exchange_inner_response, connection_info,
-                           ssl_info, response_headers);
+                           ssl_info, response_headers, dns_aliases);
 }
 
 void TestRenderFrameHost::PrepareForCommitInternal(
@@ -367,7 +368,8 @@
     bool is_signed_exchange_inner_response,
     net::HttpResponseInfo::ConnectionInfo connection_info,
     base::Optional<net::SSLInfo> ssl_info,
-    scoped_refptr<net::HttpResponseHeaders> response_headers) {
+    scoped_refptr<net::HttpResponseHeaders> response_headers,
+    const std::vector<std::string>& dns_aliases) {
   NavigationRequest* request = frame_tree_node_->navigation_request();
   CHECK(request);
   bool have_to_make_network_request =
@@ -412,6 +414,7 @@
   response->headers = response_headers;
   response->parsed_headers =
       network::PopulateParsedHeaders(response->headers, request->GetURL());
+  response->dns_aliases = dns_aliases;
   // TODO(carlosk): Ideally, it should be possible someday to
   // fully commit the navigation at this call to CallOnResponseStarted.
   url_loader->CallOnResponseStarted(std::move(response));
diff --git a/content/test/test_render_frame_host.h b/content/test/test_render_frame_host.h
index e0bd9e5..170d554 100644
--- a/content/test/test_render_frame_host.h
+++ b/content/test/test_render_frame_host.h
@@ -147,7 +147,8 @@
       bool is_signed_exchange_inner_response,
       net::HttpResponseInfo::ConnectionInfo connection_info,
       base::Optional<net::SSLInfo> ssl_info,
-      scoped_refptr<net::HttpResponseHeaders> response_headers);
+      scoped_refptr<net::HttpResponseHeaders> response_headers,
+      const std::vector<std::string>& dns_aliases);
 
   // Used to simulate the commit of a navigation having been processed in the
   // renderer. If parameters required to commit are not provided, they will be
@@ -239,7 +240,8 @@
       bool is_signed_exchange_inner_response,
       net::HttpResponseInfo::ConnectionInfo connection_info,
       base::Optional<net::SSLInfo> ssl_info,
-      scoped_refptr<net::HttpResponseHeaders> response_headers);
+      scoped_refptr<net::HttpResponseHeaders> response_headers,
+      const std::vector<std::string>& dns_aliases);
 
   // Computes the page ID for a pending navigation in this RenderFrameHost;
   int32_t ComputeNextPageID();
diff --git a/device/bluetooth/public/mojom/adapter.mojom b/device/bluetooth/public/mojom/adapter.mojom
index b593b1e..9c8932a 100644
--- a/device/bluetooth/public/mojom/adapter.mojom
+++ b/device/bluetooth/public/mojom/adapter.mojom
@@ -92,8 +92,9 @@
 };
 
 // Represents an open connection to a remote device. Releasing it will destroy
-// the underlying connection, but callers should prefer to let a call to
-// Disconnect() to finish first.
+// the underlying connection, but if callers want to try again soon, they should
+// call Disconnect() first and wait for completion to ensure that the resource
+// has been completely released.
 // Note: Methods which are declared [Sync] are for use by
 // //chrome/services/sharing/nearby; all other usage of their synchronous
 // signatures is strongly discouraged.
@@ -107,7 +108,9 @@
 };
 
 // Represents a pending connecting from a remote device. Releasing it will
-// stop listening for an incoming connection.
+// stop listening for an incoming connection, but if callers want to start
+// listening again soon, they should call Disconnect() first and wait for
+// completion to ensure that the resource has been completely released.
 // Note: Methods which are declared [Sync] are for use by
 // //chrome/services/sharing/nearby; all other usage of their synchronous
 // signatures is strongly discouraged.
@@ -116,6 +119,13 @@
   // goes wrong, the returned |result| will be null.
   [Sync]
   Accept() => (AcceptConnectionResult? result);
+
+  // Use to gracefully close the underlying resource before destroying. The
+  // reply callback can be used to synchronize an attempt to re-initialize
+  // a server socket; attempting to listen on the same server socket on this
+  // device may fail with a busy error until the reply callback is invoked.
+  [Sync]
+  Disconnect() => ();
 };
 
 // Handles requests to either query Bluetooth adapter capabilities or state, or
diff --git a/device/bluetooth/server_socket.cc b/device/bluetooth/server_socket.cc
index f673e2e..af21933 100644
--- a/device/bluetooth/server_socket.cc
+++ b/device/bluetooth/server_socket.cc
@@ -36,6 +36,11 @@
                      weak_ptr_factory_.GetWeakPtr(), copyable_callback));
 }
 
+void ServerSocket::Disconnect(DisconnectCallback callback) {
+  DCHECK(server_socket_);
+  server_socket_->Disconnect(std::move(callback));
+}
+
 void ServerSocket::OnAccept(
     AcceptCallback callback,
     const device::BluetoothDevice* device,
diff --git a/device/bluetooth/server_socket.h b/device/bluetooth/server_socket.h
index e685936..8647a3bb 100644
--- a/device/bluetooth/server_socket.h
+++ b/device/bluetooth/server_socket.h
@@ -33,6 +33,7 @@
 
   // mojom::ServerSocket:
   void Accept(AcceptCallback callback) override;
+  void Disconnect(DisconnectCallback callback) override;
 
  private:
   void OnAccept(AcceptCallback callback,
diff --git a/device/bluetooth/server_socket_unittest.cc b/device/bluetooth/server_socket_unittest.cc
index 4f54124..506dbce 100644
--- a/device/bluetooth/server_socket_unittest.cc
+++ b/device/bluetooth/server_socket_unittest.cc
@@ -123,4 +123,9 @@
   EXPECT_FALSE(fake_bluetooth_server_socket_->HasAcceptArgs());
 }
 
+TEST_F(ServerSocketTest, TestDisconnect) {
+  server_socket_->Disconnect(base::DoNothing());
+  EXPECT_TRUE(fake_bluetooth_server_socket_->called_disconnect());
+}
+
 }  // namespace bluetooth
diff --git a/docs/accessibility/chromevox_on_desktop_linux.md b/docs/accessibility/chromevox_on_desktop_linux.md
index 9f016cd..828b963 100644
--- a/docs/accessibility/chromevox_on_desktop_linux.md
+++ b/docs/accessibility/chromevox_on_desktop_linux.md
@@ -85,9 +85,10 @@
 Pick the latest version and
 
 ```
+VERSION=1.49.3.7
 TMPDIR=$(mktemp -d)
-gsutil cp gs://chromeos-localmirror/distfiles/espeak-ng-20180801.tar.gz $TMPDIR
-tar -C $TMPDIR -xvf ~/espeak-ng/espeak-ng-20180801.tar.gz
+gsutil cp gs://chromeos-localmirror/distfiles/espeak-ng-$VERSION.tar.gz $TMPDIR
+tar -C $TMPDIR -xvf $TMPDIR/espeak-ng-$VERSION.tar.gz
 sudo mkdir -p /usr/share/chromeos-assets/speech_synthesis/espeak-ng/
 sudo chown -R $(whoami) /usr/share/chromeos-assets/
 cp -r $TMPDIR/espeak-ng/chrome-extension/* /usr/share/chromeos-assets/speech_synthesis/espeak-ng
diff --git a/docs/media/autoplay.md b/docs/media/autoplay.md
index 3d0594a7..04ee027 100644
--- a/docs/media/autoplay.md
+++ b/docs/media/autoplay.md
@@ -1,9 +1,8 @@
 # Autoplay of HTMLMediaElements
 
-Autoplay is the concept of playing media elements without user gesture. On
-desktop, autoplay is always allowed. On mobile, only muted video elements are
-allowed to autoplay. The autoplay logic follows
-the
+Autoplay is the concept of playing media elements without user gesture. The
+policy that defines when autoplay is allowed is defined [here](https://www.chromium.org/audio-video/autoplay).
+The autoplay logic follows the
 [HTML spec](https://html.spec.whatwg.org/multipage/embedded-content.html#media-elements).
 
 There are two ways of initiating autoplay:
diff --git a/extensions/browser/api/BUILD.gn b/extensions/browser/api/BUILD.gn
index 1fb8850a..7777e3d 100644
--- a/extensions/browser/api/BUILD.gn
+++ b/extensions/browser/api/BUILD.gn
@@ -134,12 +134,6 @@
       "media_perception_private/media_perception_private_api.h",
     ]
 
-    # TODO(crbug/1158984): as above, remove these deps.
-    public_deps += [
-      "//extensions/browser/api/vpn_provider",
-      "//extensions/browser/api/webcam_private",
-    ]
-
     deps += [
       "//chromeos",
       "//chromeos/dbus",
@@ -211,6 +205,8 @@
       "//extensions/browser/api/clipboard",
       "//extensions/browser/api/diagnostics",
       "//extensions/browser/api/virtual_keyboard",
+      "//extensions/browser/api/vpn_provider",
+      "//extensions/browser/api/webcam_private",
     ]
   }
 }
diff --git a/extensions/browser/api/vpn_provider/BUILD.gn b/extensions/browser/api/vpn_provider/BUILD.gn
index 6312734c..222e86b2 100644
--- a/extensions/browser/api/vpn_provider/BUILD.gn
+++ b/extensions/browser/api/vpn_provider/BUILD.gn
@@ -25,6 +25,8 @@
     "//chromeos/dbus",
     "//chromeos/login/login_state",
     "//chromeos/network",
+    "//components/keyed_service/content",
+    "//content/public/browser",
     "//extensions/common/api",
   ]
 
diff --git a/extensions/browser/api/webcam_private/BUILD.gn b/extensions/browser/api/webcam_private/BUILD.gn
index 1e24696..fb187db 100644
--- a/extensions/browser/api/webcam_private/BUILD.gn
+++ b/extensions/browser/api/webcam_private/BUILD.gn
@@ -25,6 +25,8 @@
 
   deps = [
     "//chromeos/dbus/ip_peripheral",
+    "//extensions/browser/api",
+    "//extensions/browser/api/serial",
     "//extensions/common/api",
     "//mojo/public/cpp/bindings",
     "//services/device/public/mojom",
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index e45815a..990d80d 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -137,11 +137,6 @@
 const char kShortName[] = "short_name";
 const char kSignature[] = "signature";
 const char kSockets[] = "sockets";
-const char kSpellcheck[] = "spellcheck";
-const char kSpellcheckDictionaryFormat[] = "dictionary_format";
-const char kSpellcheckDictionaryLanguage[] = "dictionary_language";
-const char kSpellcheckDictionaryLocale[] = "dictionary_locale";
-const char kSpellcheckDictionaryPath[] = "dictionary_path";
 const char kStorageManagedSchema[] = "storage.managed_schema";
 const char kSuggestedKey[] = "suggested_key";
 const char kSystemIndicator[] = "system_indicator";
@@ -594,16 +589,6 @@
     "Invalid value for 'short_name'.";
 const char kInvalidSignature[] =
     "Value 'signature' is missing or invalid.";
-const char kInvalidSpellcheck[] =
-    "Invalid value for 'spellcheck'.";
-const char kInvalidSpellcheckDictionaryFormat[] =
-    "Invalid value for spellcheck dictionary format.";
-const char kInvalidSpellcheckDictionaryLanguage[] =
-    "Invalid value for spellcheck dictionary language.";
-const char kInvalidSpellcheckDictionaryLocale[] =
-    "Invalid value for spellcheck dictionary locale.";
-const char kInvalidSpellcheckDictionaryPath[] =
-    "Invalid value for spellcheck dictionary path.";
 const char kInvalidStartupOverrideURL[] =
     "Invalid value for overriding startup URL: '[*]'.";
 const char kInvalidSystemIndicator[] =
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h
index fabc5c1d4..0a93f7c 100644
--- a/extensions/common/manifest_constants.h
+++ b/extensions/common/manifest_constants.h
@@ -139,11 +139,6 @@
 extern const char kShortName[];
 extern const char kSignature[];
 extern const char kSockets[];
-extern const char kSpellcheck[];
-extern const char kSpellcheckDictionaryFormat[];
-extern const char kSpellcheckDictionaryLanguage[];
-extern const char kSpellcheckDictionaryLocale[];
-extern const char kSpellcheckDictionaryPath[];
 extern const char kStorageManagedSchema[];
 extern const char kSuggestedKey[];
 extern const char kSystemIndicator[];
@@ -423,11 +418,6 @@
 extern const char kInvalidSearchEngineURL[];
 extern const char kInvalidShortName[];
 extern const char kInvalidSignature[];
-extern const char kInvalidSpellcheck[];
-extern const char kInvalidSpellcheckDictionaryFormat[];
-extern const char kInvalidSpellcheckDictionaryLanguage[];
-extern const char kInvalidSpellcheckDictionaryLocale[];
-extern const char kInvalidSpellcheckDictionaryPath[];
 extern const char kInvalidStartupOverrideURL[];
 extern const char kInvalidSystemIndicator[];
 extern const char kInvalidTheme[];
diff --git a/gpu/command_buffer/client/webgpu_implementation.cc b/gpu/command_buffer/client/webgpu_implementation.cc
index c12b382..9fa76a2 100644
--- a/gpu/command_buffer/client/webgpu_implementation.cc
+++ b/gpu/command_buffer/client/webgpu_implementation.cc
@@ -136,10 +136,10 @@
     TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
                  "WebGPUCommandSerializer::Flush", "bytes", c2s_put_offset_);
 
-    TRACE_EVENT_FLOW_BEGIN0(
-        TRACE_DISABLED_BY_DEFAULT("gpu.dawn"), "DawnCommands",
-        (static_cast<uint64_t>(c2s_buffer_.shm_id()) << 32) +
-            c2s_buffer_.offset());
+    TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
+                           "DawnCommands", TRACE_EVENT_FLAG_FLOW_OUT,
+                           (static_cast<uint64_t>(c2s_buffer_.shm_id()) << 32) +
+                               c2s_buffer_.offset());
 
     c2s_buffer_.Shrink(c2s_put_offset_);
     helper_->DawnCommands(device_client_id_, c2s_buffer_.shm_id(),
@@ -415,8 +415,9 @@
 #if BUILDFLAG(USE_DAWN)
 
   static uint32_t return_trace_id = 0;
-  TRACE_EVENT_FLOW_END0(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
-                        "DawnReturnCommands", return_trace_id++);
+  TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
+                         "DawnReturnCommands", TRACE_EVENT_FLAG_FLOW_IN,
+                         return_trace_id++);
 
   TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
                "WebGPUImplementation::OnGpuControlReturnData", "bytes",
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index 3fd0449..233a6ff 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -115,8 +115,9 @@
                  "WireServerCommandSerializer::Flush", "bytes", put_offset_);
 
     static uint32_t return_trace_id = 0;
-    TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
-                            "DawnReturnCommands", return_trace_id++);
+    TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
+                           "DawnReturnCommands", TRACE_EVENT_FLAG_FLOW_OUT,
+                           return_trace_id++);
 
     client_->HandleReturnData(base::make_span(buffer_.data(), put_offset_));
     put_offset_ = kDawnReturnCmdsOffset;
@@ -1026,8 +1027,9 @@
     return error::kOutOfBounds;
   }
 
-  TRACE_EVENT_FLOW_END0(
+  TRACE_EVENT_WITH_FLOW0(
       TRACE_DISABLED_BY_DEFAULT("gpu.dawn"), "DawnCommands",
+      TRACE_EVENT_FLAG_FLOW_IN,
       (static_cast<uint64_t>(commands_shm_id) << 32) + commands_shm_offset);
 
   TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
diff --git a/headless/test/data/protocol/emulation/screenshot-device-scale-factor-expected.txt b/headless/test/data/protocol/emulation/screenshot-device-scale-factor-expected.txt
new file mode 100644
index 0000000..bdd7a42
--- /dev/null
+++ b/headless/test/data/protocol/emulation/screenshot-device-scale-factor-expected.txt
@@ -0,0 +1,4 @@
+Tests screenshot with overridden device scale factor
+requested url: http://example.com/
+Screenshot size: 200 x 200
+rgba @(175,175) 0,0,255,255
\ No newline at end of file
diff --git a/headless/test/data/protocol/emulation/screenshot-device-scale-factor.js b/headless/test/data/protocol/emulation/screenshot-device-scale-factor.js
new file mode 100644
index 0000000..1e79d0b4
--- /dev/null
+++ b/headless/test/data/protocol/emulation/screenshot-device-scale-factor.js
@@ -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.
+
+(async function(testRunner) {
+  const {page, session, dp} = await testRunner.startWithFrameControl(
+      'Tests screenshot with overridden device scale factor');
+
+  await dp.Runtime.enable();
+  await dp.HeadlessExperimental.enable();
+
+  dp.Emulation.enable();
+  await dp.Emulation.setDeviceMetricsOverride({
+      deviceScaleFactor: 2,
+      width: 100,
+      height: 100,
+      screenHeight: 100,
+      screenWidth: 100,
+      mobile: true,
+      viewport: {x: 0, y: 0, width: 100, height: 100, scale: 1}
+  });
+
+  const RendererTestHelper =
+      await testRunner.loadScript('../helpers/renderer-test-helper.js');
+  const {httpInterceptor, frameNavigationHelper, virtualTimeController} =
+      await (new RendererTestHelper(testRunner, dp, page)).init();
+
+  httpInterceptor.addResponse(`http://example.com/`,
+      `<!doctype html>
+      <html>
+      <meta name=viewport content="width=device-width">
+      <style>
+        html, body { width: 100%; height: 100%; margin:0; padding:0; }
+        div {
+          width: 100%; height: 100%; margin:0; padding:0;
+          background-size: 100% 100%;
+          background-color: blue;
+        }
+      </style>
+      <body>
+        <div></div>
+      </body>
+      </html>
+  `);
+
+  let ctx = await new Promise(async fulfill => {
+    await virtualTimeController.grantInitialTime(500, 100,
+        null,
+        async () => fulfill(await virtualTimeController.captureScreenshot())
+    );
+    frameNavigationHelper.navigate('http://example.com/');
+  });
+  // We use a screen and viewport of 100x100 DIP, which is 200 physical pixels
+  // due to deviceScaleFactor of 2. Make sure the screenshot is not clipped.
+  let rgba = ctx.getImageData(175, 175, 1, 1).data;
+  testRunner.log(`rgba @(175,175) ${rgba}`);
+  testRunner.completeTest();
+})
\ No newline at end of file
diff --git a/headless/test/headless_compositor_browsertest.cc b/headless/test/headless_compositor_browsertest.cc
index c2563d2..9986738e 100644
--- a/headless/test/headless_compositor_browsertest.cc
+++ b/headless/test/headless_compositor_browsertest.cc
@@ -157,5 +157,7 @@
                          "sanity/renderer-opacity-animation.js")
 HEADLESS_COMPOSITOR_TEST(ScreenshotAfterMetricsOverride,
                          "sanity/screenshot-after-metrics-override.js")
+HEADLESS_COMPOSITOR_TEST(ScreenshotDeviceScaleFactor,
+                         "emulation/screenshot-device-scale-factor.js")
 
 }  // namespace headless
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 1218a18..1eb1bd9 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -8527,116 +8527,6 @@
       }
     }
     builders {
-      name: "Linux remote_run Builder"
-      swarming_host: "chromium-swarm.appspot.com"
-      swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/master"
-        cmd: "recipes"
-      }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 36000
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium.resultdb.result_sink"
-        value: 100
-      }
-      experiments {
-        key: "chromium.resultdb.result_sink.gtests_local"
-        value: 100
-      }
-      experiments {
-        key: "chromium.resultdb.result_sink.junit_tests"
-        value: 100
-      }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "luci-resultdb"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "luci-resultdb"
-          dataset: "chromium"
-          table: "gpu_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test/.+"
-            }
-          }
-        }
-      }
-    }
-    builders {
-      name: "Linux remote_run Tester"
-      swarming_host: "chromium-swarm.appspot.com"
-      swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/master"
-        cmd: "recipes"
-      }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 36000
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium.resultdb.result_sink"
-        value: 100
-      }
-      experiments {
-        key: "chromium.resultdb.result_sink.gtests_local"
-        value: 100
-      }
-      experiments {
-        key: "chromium.resultdb.result_sink.junit_tests"
-        value: 100
-      }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "luci-resultdb"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "luci-resultdb"
-          dataset: "chromium"
-          table: "gpu_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test/.+"
-            }
-          }
-        }
-      }
-    }
-    builders {
       name: "Lollipop Phone Tester"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index 2764aaef..849986f 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -5922,14 +5922,6 @@
     short_name: "tst"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Linux remote_run Builder"
-    category: "remote_run"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux remote_run Tester"
-    category: "remote_run"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Site Isolation Android"
     category: "site_isolation"
   }
diff --git a/infra/config/generated/luci-scheduler.cfg b/infra/config/generated/luci-scheduler.cfg
index 8365c1b..f5dfeb64 100644
--- a/infra/config/generated/luci-scheduler.cfg
+++ b/infra/config/generated/luci-scheduler.cfg
@@ -2079,30 +2079,6 @@
   }
 }
 job {
-  id: "Linux remote_run Builder"
-  realm: "ci"
-  acl_sets: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "Linux remote_run Builder"
-  }
-}
-job {
-  id: "Linux remote_run Tester"
-  realm: "ci"
-  acls {
-    role: TRIGGERER
-    granted_to: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-  }
-  acl_sets: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "Linux remote_run Tester"
-  }
-}
-job {
   id: "Lollipop Phone Tester"
   realm: "ci"
   acls {
@@ -6985,7 +6961,6 @@
   triggers: "Linux MSan Builder"
   triggers: "Linux TSan Builder"
   triggers: "Linux Viz"
-  triggers: "Linux remote_run Builder"
   triggers: "MSAN Release (chained origins)"
   triggers: "MSAN Release (no origins)"
   triggers: "Mac ASAN Release"
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index ff34b32..b7112793 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -217,7 +217,6 @@
             "infra",
             "linux",
             "recipe",
-            "remote_run",
             "site_isolation",
             "network",
             "viz",
@@ -2733,21 +2732,6 @@
 )
 
 ci.fyi_builder(
-    name = "Linux remote_run Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "remote_run",
-    ),
-)
-
-ci.fyi_builder(
-    name = "Linux remote_run Tester",
-    console_view_entry = consoles.console_view_entry(
-        category = "remote_run",
-    ),
-    triggered_by = ["Linux remote_run Builder"],
-)
-
-ci.fyi_builder(
     name = "Site Isolation Android",
     console_view_entry = consoles.console_view_entry(
         category = "site_isolation",
diff --git a/ios/chrome/app/main_application_delegate.mm b/ios/chrome/app/main_application_delegate.mm
index f994d9d..a830c3d 100644
--- a/ios/chrome/app/main_application_delegate.mm
+++ b/ios/chrome/app/main_application_delegate.mm
@@ -326,12 +326,10 @@
             base::RecordAction(
                 base::UserMetricsAction("IOSOpenByMainIntent"));
           }
-
-          [_appState
-              applicationWillEnterForeground:UIApplication.sharedApplication
-                             metricsMediator:_metricsMediator
-                                memoryHelper:_memoryHelper];
         });
+    [_appState applicationWillEnterForeground:UIApplication.sharedApplication
+                              metricsMediator:_metricsMediator
+                                 memoryHelper:_memoryHelper];
   }
 }
 
diff --git a/ios/chrome/app/strings/ios_chromium_strings.grd b/ios/chrome/app/strings/ios_chromium_strings.grd
index edd1fdf..9a4d2af 100644
--- a/ios/chrome/app/strings/ios_chromium_strings.grd
+++ b/ios/chrome/app/strings/ios_chromium_strings.grd
@@ -190,6 +190,12 @@
       <message name="IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_MESSAGE" desc="Message when the user taps the addition information button in the default browser promotion modal. [iOS only]">
           You can now use Chromium any time you tap links in messages, documents, and other apps.
       </message>
+      <message name="IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_INSTRUCTIONS_MESSAGE" desc="Description to user about how to reach the default browser setting in their device. [iOS only]">
+        To make Chromium your default:
+1. Open Settings
+2. Tap Default Browser App
+3. Select Chromium.
+      </message>
       <message name="IDS_IOS_DISCONNECT_DIALOG_TITLE" desc="The title of the disconnect dialog [Length: 30em].">
         Sign out of Chromium?
       </message>
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_INSTRUCTIONS_MESSAGE.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_INSTRUCTIONS_MESSAGE.png.sha1
new file mode 100644
index 0000000..84ed9a7
--- /dev/null
+++ b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_INSTRUCTIONS_MESSAGE.png.sha1
@@ -0,0 +1 @@
+d8c0993b2c1a5c82af50aba4aefa5caf4f2b7995
\ 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 b6e4558..ff5b080 100644
--- a/ios/chrome/app/strings/ios_google_chrome_strings.grd
+++ b/ios/chrome/app/strings/ios_google_chrome_strings.grd
@@ -190,6 +190,12 @@
       <message name="IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_MESSAGE" desc="Message when the user taps the addition information button in the default browser promotion modal. [iOS only]">
           You can now use Chrome any time you tap links in messages, documents, and other apps.
       </message>
+      <message name="IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_INSTRUCTIONS_MESSAGE" desc="Description to user about how to reach the default browser setting in their device. [iOS only]">
+          To make Chrome your default:
+  1. Open Settings
+  2. Tap Default Browser App
+  3. Select Chrome.
+      </message>
       <message name="IDS_IOS_DISCONNECT_DIALOG_TITLE" desc="The title of the disconnect dialog [Length: 30em].">
         Sign out of Chrome?
       </message>
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_INSTRUCTIONS_MESSAGE.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_INSTRUCTIONS_MESSAGE.png.sha1
new file mode 100644
index 0000000..84ed9a7
--- /dev/null
+++ b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_INSTRUCTIONS_MESSAGE.png.sha1
@@ -0,0 +1 @@
+d8c0993b2c1a5c82af50aba4aefa5caf4f2b7995
\ 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 a352744..3a2edfff 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -688,6 +688,9 @@
       <message name="IDS_IOS_DEFAULT_BROWSER_MAIN_BUTTON_TEXT" desc="Text of the button that will take the user to the Settings page to configure the default browser [iOS only]">
         Open Settings
       </message>
+      <message name="IDS_IOS_DEFAULT_BROWSER_REMIND_ME_LATER_BUTTON_TEXT" desc="Text of button that will show the default browser modal promo UI again at a later time.  [iOS only]">
+        Remind Me Later
+      </message>
       <message name="IDS_IOS_DEFAULT_BROWSER_SECONDARY_BUTTON_TEXT" desc="Text of secondary button that will dismiss the default browser modal promo UI [iOS only]">
         No Thanks
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_BROWSER_REMIND_ME_LATER_BUTTON_TEXT.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_BROWSER_REMIND_ME_LATER_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..1b54c6dc
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_BROWSER_REMIND_ME_LATER_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+2be6f7f0d2d54ea282942be1fac13e1ca8e6244a
\ No newline at end of file
diff --git a/ios/chrome/browser/ios_chrome_main_parts.mm b/ios/chrome/browser/ios_chrome_main_parts.mm
index 66ef6f54..6cb15f4 100644
--- a/ios/chrome/browser/ios_chrome_main_parts.mm
+++ b/ios/chrome/browser/ios_chrome_main_parts.mm
@@ -79,12 +79,6 @@
 
 namespace {
 
-// If enabled local state file will have NSURLFileProtectionNone protection
-// level set for NSURLFileProtectionKey key. The purpose of this feature is to
-// understand if file protection interferes with "clean exit beacon" pref.
-const base::Feature kRemoveProtectionFromPrefFile{
-    "RemoveProtectionFromPrefFile", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Sets |level| value for NSURLFileProtectionKey key for the URL with given
 // |local_state_path|.
 void SetProtectionLevel(const base::FilePath& file_path, id level) {
@@ -213,15 +207,11 @@
   metrics::EnableExpiryChecker(::kExpiredHistogramsHashes,
                                ::kNumExpiredHistograms);
 
+  // TODO(crbug.com/1164533): Remove code below some time after February 2021.
   NSString* const kRemoveProtectionFromPrefFileKey =
       @"RemoveProtectionFromPrefKey";
-  if (base::FeatureList::IsEnabled(kRemoveProtectionFromPrefFile)) {
-    SetProtectionLevel(local_state_path, NSURLFileProtectionNone);
-    [NSUserDefaults.standardUserDefaults
-        setBool:YES
-         forKey:kRemoveProtectionFromPrefFileKey];
-  } else if ([NSUserDefaults.standardUserDefaults
-                 boolForKey:kRemoveProtectionFromPrefFileKey]) {
+  if ([NSUserDefaults.standardUserDefaults
+          boolForKey:kRemoveProtectionFromPrefFileKey]) {
     // Restore default protection level when user is no longer in the
     // experimental group.
     SetProtectionLevel(local_state_path,
diff --git a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm
index 50dd4c0..b23784b 100644
--- a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm
+++ b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm
@@ -61,10 +61,10 @@
   kMaxValue = SessionRestorationXte
 };
 
-// Values of the Stability.iOS.UTE.MobileSessionAppWillTerminateReceived
+// Values of the Stability.iOS.UTE.MobileSessionAppWillTerminateWasReceived
 // histogram. These values are persisted to logs. Entries should not be
 // renumbered and numeric values should never be reused.
-enum class MobileSessionAppWillTerminateReceived {
+enum class MobileSessionAppWillTerminateWasReceived {
   // ApplicationWillTerminate notification was not received for this XTE.
   WasNotReceivedForXte = 0,
   // ApplicationWillTerminate notification was not received for this UTE.
@@ -143,18 +143,18 @@
 }
 
 // Returns value to record for
-// Stability.iOS.UTE.MobileSessionAppWillTerminateReceived histogram.
-MobileSessionAppWillTerminateReceived GetMobileSessionAppWillTerminateReceived(
-    bool has_possible_explanation) {
+// Stability.iOS.UTE.MobileSessionAppWillTerminateWasReceived histogram.
+MobileSessionAppWillTerminateWasReceived
+GetMobileSessionAppWillTerminateWasReceived(bool has_possible_explanation) {
   if (!PreviousSessionInfo.sharedInstance.applicationWillTerminateWasReceived) {
     return has_possible_explanation
-               ? MobileSessionAppWillTerminateReceived::WasNotReceivedForXte
-               : MobileSessionAppWillTerminateReceived::WasNotReceivedForUte;
+               ? MobileSessionAppWillTerminateWasReceived::WasNotReceivedForXte
+               : MobileSessionAppWillTerminateWasReceived::WasNotReceivedForUte;
   }
 
   return has_possible_explanation
-             ? MobileSessionAppWillTerminateReceived::WasReceivedForXte
-             : MobileSessionAppWillTerminateReceived::WasReceivedForUte;
+             ? MobileSessionAppWillTerminateWasReceived::WasReceivedForXte
+             : MobileSessionAppWillTerminateWasReceived::WasReceivedForUte;
 }
 
 // Logs |type| in the shutdown type histogram.
@@ -351,8 +351,8 @@
 
     UMA_STABILITY_HISTOGRAM_ENUMERATION(
         "Stability.iOS.UTE.MobileSessionAppWillTerminateWasReceived",
-        GetMobileSessionAppWillTerminateReceived(possible_explanation),
-        MobileSessionAppWillTerminateReceived::kMaxValue);
+        GetMobileSessionAppWillTerminateWasReceived(possible_explanation),
+        MobileSessionAppWillTerminateWasReceived::kMaxValue);
 
     if (!possible_explanation && EnableSyntheticCrashReportsForUte() &&
         GetApplicationContext()->GetLocalState()->GetBoolean(
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index c5352e8..4639414 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -3639,6 +3639,8 @@
 
 - (void)webState:(web::WebState*)webState
     contextMenuConfigurationForParams:(const web::ContextMenuParams&)params
+                      previewProvider:
+                          (UIContextMenuContentPreviewProvider)previewProvider
                     completionHandler:
                         (void (^)(UIContextMenuConfiguration*))completionHandler
     API_AVAILABLE(ios(13.0)) {
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index fe73efe..ea407ba 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -784,12 +784,16 @@
     // the categorization of potentially interested users or if the user is
     // signed in. Do not show if it is determined that Chrome is already the
     // default browser or if the user has already seen the promo UI.
+    // If the user was in the experiment group that showed the Remind Me Later
+    // button and tapped on it, then show the promo again if now is the right
+    // time.
     BOOL isEligibleUser = IsLikelyInterestedDefaultBrowserUser() ||
                           ios::GetChromeBrowserProvider()
                               ->GetChromeIdentityService()
                               ->HasIdentities();
-    if (!IsChromeLikelyDefaultBrowser() &&
-        !HasUserInteractedWithFullscreenPromoBefore() && isEligibleUser) {
+    if ((!IsChromeLikelyDefaultBrowser() &&
+         !HasUserInteractedWithFullscreenPromoBefore() && isEligibleUser) ||
+        ShouldShowRemindMeLaterDefaultBrowserFullscreenPromo()) {
       self.sceneState.appState.shouldShowDefaultBrowserPromo = YES;
     }
   }
diff --git a/ios/chrome/browser/ui/whats_new/BUILD.gn b/ios/chrome/browser/ui/whats_new/BUILD.gn
index bf94748a..0592c596 100644
--- a/ios/chrome/browser/ui/whats_new/BUILD.gn
+++ b/ios/chrome/browser/ui/whats_new/BUILD.gn
@@ -8,7 +8,10 @@
     "default_browser_utils.h",
     "default_browser_utils.mm",
   ]
-  deps = [ "//base" ]
+  deps = [
+    "//base",
+    "//ios/chrome/browser/ui:feature_flags",
+  ]
 }
 
 source_set("whats_new") {
diff --git a/ios/chrome/browser/ui/whats_new/default_browser_promo_coordinator.mm b/ios/chrome/browser/ui/whats_new/default_browser_promo_coordinator.mm
index b4e70a0..9fba13a0 100644
--- a/ios/chrome/browser/ui/whats_new/default_browser_promo_coordinator.mm
+++ b/ios/chrome/browser/ui/whats_new/default_browser_promo_coordinator.mm
@@ -4,9 +4,11 @@
 
 #import "ios/chrome/browser/ui/whats_new/default_browser_promo_coordinator.h"
 
-#include "base/metrics/histogram_macros.h"
+#include "base/feature_list.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
+#include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/ui/whats_new/default_browser_promo_view_controller.h"
 #import "ios/chrome/browser/ui/whats_new/default_browser_utils.h"
 #import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_action_handler.h"
@@ -24,7 +26,8 @@
 enum IOSDefaultBrowserFullscreenPromoAction {
   ACTION_BUTTON = 0,
   CANCEL = 1,
-  kMaxValue = CANCEL,
+  REMIND_ME_LATER = 2,
+  kMaxValue = REMIND_ME_LATER,
 };
 
 }  // namespace
@@ -92,7 +95,17 @@
 }
 
 - (void)confirmationAlertPrimaryAction {
-  UMA_HISTOGRAM_ENUMERATION("IOS.DefaultBrowserFullscreenPromo", ACTION_BUTTON);
+  if (IsInRemindMeLaterGroup()) {
+    if (self.defaultBrowerPromoViewController.tertiaryActionAvailable) {
+      [self logDefaultBrowserFullscreenPromoRemindMeHistogramForAction:
+                ACTION_BUTTON];
+    } else {
+      [self logDefaultBrowserFullscreenRemindMeSecondPromoHistogramForAction:
+                ACTION_BUTTON];
+    }
+  } else {
+    [self logDefaultBrowserFullscreenPromoHistogramForAction:ACTION_BUTTON];
+  }
   base::RecordAction(base::UserMetricsAction(
       "IOS.DefaultBrowserFullscreenPromo.PrimaryActionTapped"));
   LogUserInteractionWithFullscreenPromo();
@@ -105,7 +118,34 @@
 }
 
 - (void)confirmationAlertSecondaryAction {
-  UMA_HISTOGRAM_ENUMERATION("IOS.DefaultBrowserFullscreenPromo", CANCEL);
+  if (IsInRemindMeLaterGroup()) {
+    if (self.defaultBrowerPromoViewController.tertiaryActionAvailable) {
+      // When the "Remind Me Later" button is visible, it is the secondary
+      // button, while the "No Thanks" button is the tertiary button.
+      [self logDefaultBrowserFullscreenPromoRemindMeHistogramForAction:
+                REMIND_ME_LATER];
+      base::RecordAction(base::UserMetricsAction(
+          "IOS.DefaultBrowserFullscreenPromo.RemindMeTapped"));
+      LogRemindMeLaterPromoActionInteraction();
+    } else {
+      [self logDefaultBrowserFullscreenRemindMeSecondPromoHistogramForAction:
+                CANCEL];
+      base::RecordAction(base::UserMetricsAction(
+          "IOS.DefaultBrowserFullscreenPromo.Dismissed"));
+      LogUserInteractionWithFullscreenPromo();
+    }
+  } else {
+    [self logDefaultBrowserFullscreenPromoHistogramForAction:CANCEL];
+    base::RecordAction(
+        base::UserMetricsAction("IOS.DefaultBrowserFullscreenPromo.Dismissed"));
+    LogUserInteractionWithFullscreenPromo();
+  }
+  [self.handler hidePromo];
+}
+
+- (void)confirmationAlertTertiaryAction {
+  DCHECK(IsInRemindMeLaterGroup());
+  [self logDefaultBrowserFullscreenPromoRemindMeHistogramForAction:CANCEL];
   base::RecordAction(
       base::UserMetricsAction("IOS.DefaultBrowserFullscreenPromo.Dismissed"));
   LogUserInteractionWithFullscreenPromo();
@@ -116,7 +156,10 @@
   base::RecordAction(base::UserMetricsAction(
       "IOS.DefaultBrowserFullscreen.PromoMoreInfoTapped"));
   NSString* message =
-      l10n_util::GetNSString(IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_MESSAGE);
+      IsInModifiedStringsGroup()
+          ? l10n_util::GetNSString(
+                IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_INSTRUCTIONS_MESSAGE)
+          : l10n_util::GetNSString(IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_MESSAGE);
   self.learnMoreViewController =
       [[PopoverLabelViewController alloc] initWithMessage:message];
 
@@ -131,4 +174,21 @@
                  completion:nil];
 }
 
+- (void)logDefaultBrowserFullscreenPromoHistogramForAction:
+    (IOSDefaultBrowserFullscreenPromoAction)action {
+  base::UmaHistogramEnumeration("IOS.DefaultBrowserFullscreenPromo", action);
+}
+
+- (void)logDefaultBrowserFullscreenPromoRemindMeHistogramForAction:
+    (IOSDefaultBrowserFullscreenPromoAction)action {
+  base::UmaHistogramEnumeration("IOS.DefaultBrowserFullscreenPromoRemindMe",
+                                action);
+}
+
+- (void)logDefaultBrowserFullscreenRemindMeSecondPromoHistogramForAction:
+    (IOSDefaultBrowserFullscreenPromoAction)action {
+  base::UmaHistogramEnumeration(
+      "IOS.DefaultBrowserFullscreenPromoRemindMeSecondPromo", action);
+}
+
 @end
diff --git a/ios/chrome/browser/ui/whats_new/default_browser_promo_view_controller.mm b/ios/chrome/browser/ui/whats_new/default_browser_promo_view_controller.mm
index a921213..f8b2c59e 100644
--- a/ios/chrome/browser/ui/whats_new/default_browser_promo_view_controller.mm
+++ b/ios/chrome/browser/ui/whats_new/default_browser_promo_view_controller.mm
@@ -6,6 +6,7 @@
 
 #include "base/feature_list.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
+#import "ios/chrome/browser/ui/whats_new/default_browser_utils.h"
 #include "ios/chrome/grit/ios_google_chrome_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
@@ -29,12 +30,17 @@
   self.showDismissBarButton = NO;
   self.titleString = l10n_util::GetNSString(IDS_IOS_DEFAULT_BROWSER_TITLE);
   self.subtitleString =
-      l10n_util::GetNSString(IDS_IOS_DEFAULT_BROWSER_DESCRIPTION);
+      IsInModifiedStringsGroup()
+          ? l10n_util::GetNSString(IDS_IOS_DEFAULT_BROWSER_LEARN_MORE_MESSAGE)
+          : l10n_util::GetNSString(IDS_IOS_DEFAULT_BROWSER_DESCRIPTION);
   self.primaryActionString =
       l10n_util::GetNSString(IDS_IOS_DEFAULT_BROWSER_MAIN_BUTTON_TEXT);
-  if (base::FeatureList::IsEnabled(kDefaultBrowserFullscreenPromoExperiment)) {
-    // TODO:(crubg.com/1155778): Add translation string.
-    self.secondaryActionString = @"Remind Me Later";
+  if (IsInRemindMeLaterGroup() &&
+      !ShouldShowRemindMeLaterDefaultBrowserFullscreenPromo()) {
+    // Show the Remind Me Later button if the user is in the correct experiment
+    // group and this isn't the second impression.
+    self.secondaryActionString = l10n_util::GetNSString(
+        IDS_IOS_DEFAULT_BROWSER_REMIND_ME_LATER_BUTTON_TEXT);
     self.tertiaryActionAvailable = YES;
     self.tertiaryActionString =
         l10n_util::GetNSString(IDS_IOS_DEFAULT_BROWSER_SECONDARY_BUTTON_TEXT);
@@ -43,7 +49,6 @@
         l10n_util::GetNSString(IDS_IOS_DEFAULT_BROWSER_SECONDARY_BUTTON_TEXT);
   }
   self.dismissBarButtonSystemItem = UIBarButtonSystemItemCancel;
-
 #if defined(__IPHONE_13_4)
   if (@available(iOS 13.4, *)) {
     self.pointerInteractionEnabled = YES;
diff --git a/ios/chrome/browser/ui/whats_new/default_browser_utils.h b/ios/chrome/browser/ui/whats_new/default_browser_utils.h
index a9b6efc..fe0ddcfd 100644
--- a/ios/chrome/browser/ui/whats_new/default_browser_utils.h
+++ b/ios/chrome/browser/ui/whats_new/default_browser_utils.h
@@ -17,6 +17,22 @@
 // past expired logs that have happened too far in the past.
 void LogLikelyInterestedDefaultBrowserUserActivity();
 
+// Logs the timestamp of a user tap on the "Remind Me Later" button in the
+// Fullscreen Promo.
+void LogRemindMeLaterPromoActionInteraction();
+
+// Returns true if the user has tapped on the "Remind Me Later" button and the
+// delay time threshold has been met.
+bool ShouldShowRemindMeLaterDefaultBrowserFullscreenPromo();
+
+// Returns true if the user is in the group that will be shown the Remind Me
+// Later button in the fullscreen promo.
+bool IsInRemindMeLaterGroup();
+
+// Returns true if the user is in the group that will be shown a modified
+// description and "Learn More" text.
+bool IsInModifiedStringsGroup();
+
 // Returns true if the user has interacted with the Fullscreen Promo previously.
 // Returns false otherwise.
 bool HasUserInteractedWithFullscreenPromoBefore();
diff --git a/ios/chrome/browser/ui/whats_new/default_browser_utils.mm b/ios/chrome/browser/ui/whats_new/default_browser_utils.mm
index 0150559..41b95d8 100644
--- a/ios/chrome/browser/ui/whats_new/default_browser_utils.mm
+++ b/ios/chrome/browser/ui/whats_new/default_browser_utils.mm
@@ -4,7 +4,10 @@
 
 #import "ios/chrome/browser/ui/whats_new/default_browser_utils.h"
 
+#include "base/feature_list.h"
 #include "base/ios/ios_util.h"
+#include "base/metrics/field_trial.h"
+#include "ios/chrome/browser/ui/ui_feature_flags.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -18,12 +21,24 @@
 NSString* const kUserHasInteractedWithFullscreenPromo =
     @"userHasInteractedWithFullscreenPromo";
 
+NSString* const kRemindMeLaterPromoActionInteraction =
+    @"remindMeLaterPromoActionInteraction";
+
+const char kDefaultBrowserFullscreenPromoExperimentRemindMeGroupName[] =
+    "RemindMeLater";
+
+const char kDefaultBrowserFullscreenPromoExperimentChangeStringsGroupName[] =
+    "ChangeStrings";
+
 // Time threshold before activity timestamps should be removed. Currently set to
 // seven days.
 const NSTimeInterval kUserActivityTimestampExpiration = 7 * 24 * 60 * 60;
 // Time threshold for the last URL open before no URL opens likely indicates
 // Chrome is no longer the default browser.
 const NSTimeInterval kLatestURLOpenForDefaultBrowser = 7 * 24 * 60 * 60;
+// Delay for the user to be reshown the fullscreen promo when the user taps on
+// the "Remind Me Later" button. 50 hours.
+const NSTimeInterval kRemindMeLaterPresentationDelay = 50 * 60 * 60;
 }
 
 NSString* const kLastHTTPURLOpenTime = @"lastHTTPURLOpenTime";
@@ -58,6 +73,43 @@
                                             forKey:kLastSignificantUserEvent];
 }
 
+void LogRemindMeLaterPromoActionInteraction() {
+  DCHECK(IsInRemindMeLaterGroup());
+  [[NSUserDefaults standardUserDefaults]
+      setObject:[NSDate date]
+         forKey:kRemindMeLaterPromoActionInteraction];
+}
+
+bool ShouldShowRemindMeLaterDefaultBrowserFullscreenPromo() {
+  if (!IsInRemindMeLaterGroup()) {
+    return false;
+  }
+  NSDate* remindMeTimestamp = [[NSUserDefaults standardUserDefaults]
+      objectForKey:kRemindMeLaterPromoActionInteraction];
+  if (!remindMeTimestamp) {
+    return false;
+  }
+  NSDate* fiftyHoursAgoDate =
+      [NSDate dateWithTimeIntervalSinceNow:-kRemindMeLaterPresentationDelay];
+  return [remindMeTimestamp laterDate:fiftyHoursAgoDate] == fiftyHoursAgoDate;
+}
+
+bool IsInRemindMeLaterGroup() {
+  return base::FeatureList::IsEnabled(
+             kDefaultBrowserFullscreenPromoExperiment) &&
+         base::FieldTrialList::FindFullName(
+             kDefaultBrowserFullscreenPromoExperiment.name) ==
+             kDefaultBrowserFullscreenPromoExperimentRemindMeGroupName;
+}
+
+bool IsInModifiedStringsGroup() {
+  return base::FeatureList::IsEnabled(
+             kDefaultBrowserFullscreenPromoExperiment) &&
+         base::FieldTrialList::FindFullName(
+             kDefaultBrowserFullscreenPromoExperiment.name) ==
+             kDefaultBrowserFullscreenPromoExperimentChangeStringsGroupName;
+}
+
 bool HasUserInteractedWithFullscreenPromoBefore() {
   return [[NSUserDefaults standardUserDefaults]
       boolForKey:kUserHasInteractedWithFullscreenPromo];
@@ -67,6 +119,12 @@
   [[NSUserDefaults standardUserDefaults]
       setBool:YES
        forKey:kUserHasInteractedWithFullscreenPromo];
+
+  if (IsInRemindMeLaterGroup()) {
+    // Clear any possible Remind Me Later timestamp saved.
+    [[NSUserDefaults standardUserDefaults]
+        removeObjectForKey:kRemindMeLaterPromoActionInteraction];
+  }
 }
 
 bool IsChromeLikelyDefaultBrowser() {
diff --git a/ios/web/public/web_state_delegate.h b/ios/web/public/web_state_delegate.h
index 4bcbd58..8c7e1694 100644
--- a/ios/web/public/web_state_delegate.h
+++ b/ios/web/public/web_state_delegate.h
@@ -96,10 +96,12 @@
 
   // Called when iOS13+ context menu is triggered and now it is required to
   // provide a UIContextMenuConfiguration to |completion_handler| to generate
-  // the context menu.
+  // the context menu. |previewProvider| is used to show a custom ViewController
+  // to preview the page.
   virtual void ContextMenuConfiguration(
       WebState* source,
       const ContextMenuParams& params,
+      UIContextMenuContentPreviewProvider preview_provider,
       void (^completion_handler)(UIContextMenuConfiguration*))
       API_AVAILABLE(ios(13.0));
   // Called when iOS13+ context menu is ready to be showed.
diff --git a/ios/web/public/web_state_delegate_bridge.h b/ios/web/public/web_state_delegate_bridge.h
index cc15ef4..9e529a3 100644
--- a/ios/web/public/web_state_delegate_bridge.h
+++ b/ios/web/public/web_state_delegate_bridge.h
@@ -84,9 +84,12 @@
 
 // Called when iOS13+ context menu is triggered and now it is required to
 // provide a UIContextMenuConfiguration to |completion_handler| to generate the
-// context menu.
+// context menu. |previewProvider| is used to show a custom ViewController to
+// preview the page.
 - (void)webState:(web::WebState*)webState
     contextMenuConfigurationForParams:(const web::ContextMenuParams&)params
+                      previewProvider:
+                          (UIContextMenuContentPreviewProvider)previewProvider
                     completionHandler:
                         (void (^)(UIContextMenuConfiguration*))completionHandler
     API_AVAILABLE(ios(13.0));
@@ -147,6 +150,7 @@
   void ContextMenuConfiguration(
       WebState* source,
       const ContextMenuParams& params,
+      UIContextMenuContentPreviewProvider preview_provider,
       void (^completion_handler)(UIContextMenuConfiguration*))
       API_AVAILABLE(ios(13.0)) override;
   void ContextMenuDidEnd(WebState* source, const GURL& link_url)
diff --git a/ios/web/web_state/ui/crw_context_menu_controller.mm b/ios/web/web_state/ui/crw_context_menu_controller.mm
index 85e2c1f..930d43a7 100644
--- a/ios/web/web_state/ui/crw_context_menu_controller.mm
+++ b/ios/web/web_state/ui/crw_context_menu_controller.mm
@@ -146,13 +146,16 @@
   // delegate's methods.
   [interaction.view addSubview:self.highlightView];
   [interaction.view addSubview:self.dismissView];
+  self.highlightView.center = location;
+  self.dismissView.center = location;
 
   self.params.location = [self.webView convertPoint:location
                                            fromView:interaction.view];
 
   __block UIContextMenuConfiguration* configuration;
   self.webState->GetDelegate()->ContextMenuConfiguration(
-      self.webState, self.params, ^(UIContextMenuConfiguration* conf) {
+      self.webState, self.params, /*preview_provider=*/nil,
+      ^(UIContextMenuConfiguration* conf) {
         configuration = conf;
       });
 
diff --git a/ios/web/web_state/ui/crw_wk_ui_handler.mm b/ios/web/web_state/ui/crw_wk_ui_handler.mm
index adf24de..359a801 100644
--- a/ios/web/web_state/ui/crw_wk_ui_handler.mm
+++ b/ios/web/web_state/ui/crw_wk_ui_handler.mm
@@ -210,8 +210,8 @@
   web::ContextMenuParams params;
   params.link_url = net::GURLWithNSURL(elementInfo.linkURL);
 
-  delegate->ContextMenuConfiguration(self.webStateImpl, params,
-                                     completionHandler);
+  delegate->ContextMenuConfiguration(
+      self.webStateImpl, params, /*preview_provider=*/nil, completionHandler);
 }
 
 #endif  // End of >iOS13 deprecated block.
diff --git a/ios/web/web_state/web_state_context_menu_bridge_unittest.mm b/ios/web/web_state/web_state_context_menu_bridge_unittest.mm
index aa3e1bc..5bd7859 100644
--- a/ios/web/web_state/web_state_context_menu_bridge_unittest.mm
+++ b/ios/web/web_state/web_state_context_menu_bridge_unittest.mm
@@ -31,6 +31,8 @@
 
 - (void)webState:(web::WebState*)webState
     contextMenuConfigurationForParams:(const web::ContextMenuParams&)params
+                      previewProvider:
+                          (UIContextMenuContentPreviewProvider)previewProvider
                     completionHandler:
                         (void (^)(UIContextMenuConfiguration*))completionHandler
     API_AVAILABLE(ios(13.0)) {
diff --git a/ios/web/web_state/web_state_delegate.mm b/ios/web/web_state/web_state_delegate.mm
index 3a2efa0..9ef2836 100644
--- a/ios/web/web_state/web_state_delegate.mm
+++ b/ios/web/web_state/web_state_delegate.mm
@@ -87,6 +87,7 @@
 void WebStateDelegate::ContextMenuConfiguration(
     WebState* source,
     const ContextMenuParams& params,
+    UIContextMenuContentPreviewProvider preview_provider,
     void (^completion_handler)(UIContextMenuConfiguration*))
     API_AVAILABLE(ios(13.0)) {
   completion_handler(nil);
diff --git a/ios/web/web_state/web_state_delegate_bridge.mm b/ios/web/web_state/web_state_delegate_bridge.mm
index eeaf5639..df64357c 100644
--- a/ios/web/web_state/web_state_delegate_bridge.mm
+++ b/ios/web/web_state/web_state_delegate_bridge.mm
@@ -141,13 +141,16 @@
 void WebStateDelegateBridge::ContextMenuConfiguration(
     WebState* source,
     const ContextMenuParams& params,
+    UIContextMenuContentPreviewProvider preview_provider,
     void (^completion_handler)(UIContextMenuConfiguration*))
     API_AVAILABLE(ios(13.0)) {
   if ([delegate_ respondsToSelector:@selector
                  (webState:
-                     contextMenuConfigurationForParams:completionHandler:)]) {
+                     contextMenuConfigurationForParams:previewProvider
+                                                      :completionHandler:)]) {
     [delegate_ webState:source
         contextMenuConfigurationForParams:params
+                          previewProvider:preview_provider
                         completionHandler:completion_handler];
   } else {
     completion_handler(nil);
diff --git a/ios/web_view/internal/cwv_web_view.mm b/ios/web_view/internal/cwv_web_view.mm
index 5ca736ba..eeb140e 100644
--- a/ios/web_view/internal/cwv_web_view.mm
+++ b/ios/web_view/internal/cwv_web_view.mm
@@ -524,6 +524,8 @@
 
 - (void)webState:(web::WebState*)webState
     contextMenuConfigurationForParams:(const web::ContextMenuParams&)params
+                      previewProvider:
+                          (UIContextMenuContentPreviewProvider)previewProvider
                     completionHandler:
                         (void (^)(UIContextMenuConfiguration*))completionHandler
     API_AVAILABLE(ios(13.0)) {
diff --git a/ipc/ipc_sync_channel.cc b/ipc/ipc_sync_channel.cc
index 0b558d9..4ce87905 100644
--- a/ipc/ipc_sync_channel.cc
+++ b/ipc/ipc_sync_channel.cc
@@ -457,9 +457,9 @@
   }
 
   base::WaitableEvent* done_event = deserializers_.back().done_event;
-  TRACE_EVENT_FLOW_BEGIN0("toplevel.flow",
-                          "SyncChannel::SyncContext::TryToUnblockListener",
-                          done_event);
+  TRACE_EVENT_WITH_FLOW0("toplevel.flow",
+                         "SyncChannel::SyncContext::TryToUnblockListener",
+                         done_event, TRACE_EVENT_FLAG_FLOW_OUT);
 
   done_event->Signal();
 
@@ -522,9 +522,9 @@
   PendingSyncMessageQueue::iterator iter;
   DVLOG(1) << "Canceling pending sends";
   for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) {
-    TRACE_EVENT_FLOW_BEGIN0("toplevel.flow",
-                            "SyncChannel::SyncContext::CancelPendingSends",
-                            iter->done_event);
+    TRACE_EVENT_WITH_FLOW0("toplevel.flow",
+                           "SyncChannel::SyncContext::CancelPendingSends",
+                           iter->done_event, TRACE_EVENT_FLAG_FLOW_OUT);
     iter->done_event->Signal();
   }
 }
@@ -644,8 +644,8 @@
   scoped_refptr<mojo::SyncHandleRegistry> registry = sync_handle_registry_;
   WaitForReply(registry.get(), context.get(), pump_messages);
 
-  TRACE_EVENT_FLOW_END0("toplevel.flow", "SyncChannel::Send",
-                        context->GetSendDoneEvent());
+  TRACE_EVENT_WITH_FLOW0("toplevel.flow", "SyncChannel::Send",
+                         context->GetSendDoneEvent(), TRACE_EVENT_FLAG_FLOW_IN);
 
   return context->Pop();
 }
diff --git a/ipc/ipc_sync_message_filter.cc b/ipc/ipc_sync_message_filter.cc
index 929f3d2..747766f 100644
--- a/ipc/ipc_sync_message_filter.cc
+++ b/ipc/ipc_sync_message_filter.cc
@@ -84,8 +84,8 @@
     const bool* stop_flags[] = {&done, &shutdown};
     registry->Wait(stop_flags, 2);
     if (done) {
-      TRACE_EVENT_FLOW_END0("toplevel.flow", "SyncMessageFilter::Send",
-                            &done_event);
+      TRACE_EVENT_WITH_FLOW0("toplevel.flow", "SyncMessageFilter::Send",
+                             &done_event, TRACE_EVENT_FLAG_FLOW_IN);
     }
   }
 
@@ -132,9 +132,9 @@
         (*iter)->send_result =
             (*iter)->deserializer->SerializeOutputParameters(message);
       }
-      TRACE_EVENT_FLOW_BEGIN0("toplevel.flow",
-                              "SyncMessageFilter::OnMessageReceived",
-                              (*iter)->done_event);
+      TRACE_EVENT_WITH_FLOW0("toplevel.flow",
+                             "SyncMessageFilter::OnMessageReceived",
+                             (*iter)->done_event, TRACE_EVENT_FLAG_FLOW_OUT);
       (*iter)->done_event->Signal();
       return true;
     }
@@ -170,9 +170,9 @@
   lock_.AssertAcquired();
   for (PendingSyncMessages::iterator iter = pending_sync_messages_.begin();
        iter != pending_sync_messages_.end(); ++iter) {
-    TRACE_EVENT_FLOW_BEGIN0("toplevel.flow",
-                            "SyncMessageFilter::SignalAllEvents",
-                            (*iter)->done_event);
+    TRACE_EVENT_WITH_FLOW0("toplevel.flow",
+                           "SyncMessageFilter::SignalAllEvents",
+                           (*iter)->done_event, TRACE_EVENT_FLAG_FLOW_OUT);
     (*iter)->done_event->Signal();
   }
 }
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 7c394c4a..5305528 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -498,6 +498,10 @@
 const base::Feature kUseSodaForLiveCaption{"UseSodaForLiveCaption",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Live Caption runs system-wide on ChromeOS, as opposed to just in the browser.
+const base::Feature kLiveCaptionSystemWideOnChromeOS{
+    "LiveCaptionSystemWideOnChromeOS", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Prevents UrlProvisionFetcher from making a provisioning request. If
 // specified, any provisioning request made will not be sent to the provisioning
 // server, and the response will indicate a failure to communicate with the
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 3e11fe5c..9f17fdc1 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -145,6 +145,7 @@
 MEDIA_EXPORT extern const base::Feature kKaleidoscopeModule;
 MEDIA_EXPORT extern const base::Feature kKaleidoscopeModuleCacheOnly;
 MEDIA_EXPORT extern const base::Feature kLiveCaption;
+MEDIA_EXPORT extern const base::Feature kLiveCaptionSystemWideOnChromeOS;
 MEDIA_EXPORT extern const base::Feature kLowDelayVideoRenderingOnLiveStream;
 MEDIA_EXPORT extern const base::Feature kMediaCapabilitiesQueryGpuFactories;
 MEDIA_EXPORT extern const base::Feature kMediaCapabilitiesWithParameters;
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index b13be4d..d715ba3 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -1351,10 +1351,8 @@
   if (video_frame && video_frame->HasTextures()) {
     if (!raster_context_provider_)
       return;  // Unable to get/create a shared main thread context.
-    if (!raster_context_provider_->GrContext() &&
-        !raster_context_provider_->ContextCapabilities().supports_oop_raster) {
-      return;  // The context has been lost.
-    }
+    if (!raster_context_provider_->GrContext())
+      return;  // The context has been lost since and can't setup a GrContext.
   }
   if (out_metadata && video_frame) {
     // WebGL last-uploaded-frame-metadata API enabled. https://crbug.com/639174
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 20b9867..dcb6458 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -548,6 +548,7 @@
     sources += [
       "windows/d3d11_copying_texture_wrapper_unittest.cc",
       "windows/d3d11_decoder_configurator_unittest.cc",
+      "windows/d3d11_picture_buffer_unittest.cc",
       "windows/d3d11_texture_selector_unittest.cc",
       "windows/d3d11_texture_wrapper_unittest.cc",
       "windows/d3d11_video_decoder_unittest.cc",
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc
index 7a498ff..d9d3ba8 100644
--- a/media/gpu/v4l2/v4l2_device.cc
+++ b/media/gpu/v4l2/v4l2_device.cc
@@ -958,8 +958,7 @@
   struct v4l2_format format = BuildV4L2Format(type_, fourcc, size, buffer_size);
   if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0 ||
       format.fmt.pix_mp.pixelformat != fourcc) {
-    VPQLOGF(2) << "Failed to set format (format_fourcc=0x" << std::hex << fourcc
-               << ")";
+    VPQLOGF(2) << "Failed to set format fourcc: " << FourccToString(fourcc);
     return base::nullopt;
   }
 
@@ -973,8 +972,7 @@
   struct v4l2_format format = BuildV4L2Format(type_, fourcc, size, buffer_size);
   if (device_->Ioctl(VIDIOC_TRY_FMT, &format) != 0 ||
       format.fmt.pix_mp.pixelformat != fourcc) {
-    VPQLOGF(2) << "Tried format not supported (format_fourcc=0x" << std::hex
-               << fourcc << ")";
+    VPQLOGF(2) << "Failed to try format fourcc: " << FourccToString(fourcc);
     return base::nullopt;
   }
 
diff --git a/media/gpu/v4l2/v4l2_video_decoder.cc b/media/gpu/v4l2/v4l2_video_decoder.cc
index 6caf038..e08e331 100644
--- a/media/gpu/v4l2/v4l2_video_decoder.cc
+++ b/media/gpu/v4l2/v4l2_video_decoder.cc
@@ -152,35 +152,25 @@
     SetState(State::kUninitialized);
   }
 
-  // Open V4L2 device.
-  VideoCodecProfile profile = config.profile();
-  uint32_t input_format_fourcc_stateless =
-      V4L2Device::VideoCodecProfileToV4L2PixFmt(profile, true);
-  if (!input_format_fourcc_stateless ||
-      !device_->Open(V4L2Device::Type::kDecoder,
-                     input_format_fourcc_stateless)) {
-    VLOGF(1) << "Failed to open device for profile: " << profile
-             << " fourcc: " << FourccToString(input_format_fourcc_stateless);
-    input_format_fourcc_stateless = 0;
-  } else {
-    VLOGF(1) << "Found V4L2 device capable of stateless decoding for "
-             << FourccToString(input_format_fourcc_stateless);
+  const VideoCodecProfile profile = config.profile();
+  constexpr bool kStateful = false;
+  constexpr bool kStateless = true;
+  base::Optional<std::pair<bool, uint32_t>> api_and_format;
+  // Try both kStateful and kStateless APIs via |fourcc| and select the first
+  // combination where Open()ing the |device_| works.
+  for (const auto api : {kStateful, kStateless}) {
+    const auto fourcc = V4L2Device::VideoCodecProfileToV4L2PixFmt(profile, api);
+    constexpr uint32_t kInvalidV4L2PixFmt = 0;
+    if (fourcc == kInvalidV4L2PixFmt ||
+        !device_->Open(V4L2Device::Type::kDecoder, fourcc)) {
+      continue;
+    }
+    api_and_format = std::make_pair(api, fourcc);
+    break;
   }
 
-  uint32_t input_format_fourcc_stateful =
-      V4L2Device::VideoCodecProfileToV4L2PixFmt(profile, false);
-  if (!input_format_fourcc_stateful ||
-      !device_->Open(V4L2Device::Type::kDecoder,
-                     input_format_fourcc_stateful)) {
-    VLOGF(1) << "Failed to open device for profile: " << profile
-             << " fourcc: " << FourccToString(input_format_fourcc_stateful);
-    input_format_fourcc_stateful = 0;
-  } else {
-    VLOGF(1) << "Found V4L2 device capable of stateful decoding for "
-             << FourccToString(input_format_fourcc_stateful);
-  }
-
-  if (!input_format_fourcc_stateless && !input_format_fourcc_stateful) {
+  if (!api_and_format.has_value()) {
+    VLOGF(1) << "No V4L2 API found for profile: " << GetProfileName(profile);
     std::move(init_cb).Run(StatusCode::kV4l2NoDecoder);
     return;
   }
@@ -206,19 +196,19 @@
     return;
   }
 
-  uint32_t input_format_fourcc;
-  if (input_format_fourcc_stateful) {
+  const auto preferred_api_and_format = api_and_format.value();
+  const uint32_t input_format_fourcc = preferred_api_and_format.second;
+  if (preferred_api_and_format.first == kStateful) {
+    VLOGF(1) << "Using a stateful API for profile: " << GetProfileName(profile)
+             << " and fourcc: " << FourccToString(input_format_fourcc);
     backend_ = std::make_unique<V4L2StatefulVideoDecoderBackend>(
         this, device_, profile, decoder_task_runner_);
-    input_format_fourcc = input_format_fourcc_stateful;
-  } else if (input_format_fourcc_stateless) {
+  } else {
+    DCHECK_EQ(preferred_api_and_format.first, kStateless);
+    VLOGF(1) << "Using a stateless API for profile: " << GetProfileName(profile)
+             << " and fourcc: " << FourccToString(input_format_fourcc);
     backend_ = std::make_unique<V4L2StatelessVideoDecoderBackend>(
         this, device_, profile, decoder_task_runner_);
-    input_format_fourcc = input_format_fourcc_stateless;
-  } else {
-    VLOGF(1) << "No backend capable of taking this profile.";
-    std::move(init_cb).Run(StatusCode::kV4l2FailedResourceAllocation);
-    return;
   }
 
   if (!backend_->Initialize()) {
@@ -227,7 +217,6 @@
     return;
   }
 
-  // Setup input format.
   if (!SetupInputFormat(input_format_fourcc)) {
     VLOGF(1) << "Failed to setup input format.";
     std::move(init_cb).Run(StatusCode::kV4l2BadFormat);
diff --git a/media/gpu/vp9_decoder.cc b/media/gpu/vp9_decoder.cc
index eee5e501..bbe17de 100644
--- a/media/gpu/vp9_decoder.cc
+++ b/media/gpu/vp9_decoder.cc
@@ -120,6 +120,7 @@
 
 void VP9Decoder::Reset() {
   curr_frame_hdr_ = nullptr;
+  decrypt_config_.reset();
   pending_pic_.reset();
 
   ref_frames_.Clear();
@@ -146,12 +147,11 @@
     }
 
     // Read a new frame header if one is not awaiting decoding already.
-    std::unique_ptr<DecryptConfig> decrypt_config;
     if (!curr_frame_hdr_) {
       gfx::Size allocate_size;
       std::unique_ptr<Vp9FrameHeader> hdr(new Vp9FrameHeader());
       Vp9Parser::Result res =
-          parser_.ParseNextFrame(hdr.get(), &allocate_size, &decrypt_config);
+          parser_.ParseNextFrame(hdr.get(), &allocate_size, &decrypt_config_);
       switch (res) {
         case Vp9Parser::kOk:
           curr_frame_hdr_ = std::move(hdr);
@@ -183,6 +183,7 @@
         state_ = kDecoding;
       } else {
         curr_frame_hdr_.reset();
+        decrypt_config_.reset();
         continue;
       }
     }
@@ -215,6 +216,7 @@
       }
 
       curr_frame_hdr_.reset();
+      decrypt_config_.reset();
       continue;
     }
 
@@ -267,6 +269,7 @@
         }
 
         curr_frame_hdr_.reset();
+        decrypt_config_.reset();
         return kRanOutOfStreamData;
       }
 
@@ -293,7 +296,7 @@
     pic->set_visible_rect(new_render_rect);
     pic->set_bitstream_id(stream_id_);
 
-    pic->set_decrypt_config(std::move(decrypt_config));
+    pic->set_decrypt_config(std::move(decrypt_config_));
 
     // For VP9, container color spaces override video stream color spaces.
     if (container_color_space_.IsSpecified())
diff --git a/media/gpu/vp9_decoder.h b/media/gpu/vp9_decoder.h
index 31dd7ce..2698e98 100644
--- a/media/gpu/vp9_decoder.h
+++ b/media/gpu/vp9_decoder.h
@@ -157,8 +157,10 @@
   // Current stream buffer id; to be assigned to pictures decoded from it.
   int32_t stream_id_ = -1;
 
-  // Current frame header to be used in decoding the next picture.
+  // Current frame header and decrypt config to be used in decoding the next
+  // picture.
   std::unique_ptr<Vp9FrameHeader> curr_frame_hdr_;
+  std::unique_ptr<DecryptConfig> decrypt_config_;
   // Current frame size that is necessary to decode |curr_frame_hdr_|.
   gfx::Size curr_frame_size_;
 
diff --git a/media/gpu/windows/d3d11_picture_buffer.h b/media/gpu/windows/d3d11_picture_buffer.h
index 70d48fc7..8ceadaa3 100644
--- a/media/gpu/windows/d3d11_picture_buffer.h
+++ b/media/gpu/windows/d3d11_picture_buffer.h
@@ -80,12 +80,19 @@
   size_t picture_index() const { return picture_index_; }
 
   // Is this PictureBuffer backing a VideoFrame right now?
-  bool in_client_use() const { return in_client_use_; }
+  bool in_client_use() const { return in_client_use_ > 0; }
 
   // Is this PictureBuffer holding an image that's in use by the decoder?
   bool in_picture_use() const { return in_picture_use_; }
 
-  void set_in_client_use(bool use) { in_client_use_ = use; }
+  void add_client_use() {
+    in_client_use_++;
+    DCHECK_GT(in_client_use_, 0);
+  }
+  void remove_client_use() {
+    DCHECK_GT(in_client_use_, 0);
+    in_client_use_--;
+  }
   void set_in_picture_use(bool use) { in_picture_use_ = use; }
 
   const ComD3D11VideoDecoderOutputView& output_view() const {
@@ -108,7 +115,7 @@
   std::unique_ptr<Texture2DWrapper> texture_wrapper_;
   gfx::Size size_;
   bool in_picture_use_ = false;
-  bool in_client_use_ = false;
+  int in_client_use_ = 0;
   size_t picture_index_;
 
   ComD3D11VideoDecoderOutputView output_view_;
diff --git a/media/gpu/windows/d3d11_picture_buffer_unittest.cc b/media/gpu/windows/d3d11_picture_buffer_unittest.cc
new file mode 100644
index 0000000..546dee2
--- /dev/null
+++ b/media/gpu/windows/d3d11_picture_buffer_unittest.cc
@@ -0,0 +1,48 @@
+// 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 <utility>
+
+#include "base/callback_helpers.h"
+#include "base/test/task_environment.h"
+#include "media/gpu/windows/d3d11_picture_buffer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+class D3D11PictureBufferTest : public ::testing::Test {
+ public:
+  D3D11PictureBufferTest() {
+    picture_buffer_ = base::MakeRefCounted<D3D11PictureBuffer>(
+        task_environment_.GetMainThreadTaskRunner(), nullptr, 0, nullptr,
+        gfx::Size(), 0);
+  }
+
+  base::test::TaskEnvironment task_environment_;
+
+  scoped_refptr<D3D11PictureBuffer> picture_buffer_;
+};
+
+// The processor proxy wraps the VideoDevice/VideoContext and stores some of the
+// d3d11 types. Make sure that the arguments we give these methods are passed
+// through correctly.
+TEST_F(D3D11PictureBufferTest, InClientUse) {
+  EXPECT_FALSE(picture_buffer_->in_client_use());
+
+  // Add two client refs.
+  picture_buffer_->add_client_use();
+  EXPECT_TRUE(picture_buffer_->in_client_use());
+  picture_buffer_->add_client_use();
+  EXPECT_TRUE(picture_buffer_->in_client_use());
+
+  // Remove them.  Should still be in use by the client until the second one has
+  // been removed.
+  picture_buffer_->remove_client_use();
+  EXPECT_TRUE(picture_buffer_->in_client_use());
+  picture_buffer_->remove_client_use();
+  EXPECT_FALSE(picture_buffer_->in_client_use());
+}
+
+}  // namespace media
\ No newline at end of file
diff --git a/media/gpu/windows/d3d11_video_decoder.cc b/media/gpu/windows/d3d11_video_decoder.cc
index ac043a7..a98333e 100644
--- a/media/gpu/windows/d3d11_video_decoder.cc
+++ b/media/gpu/windows/d3d11_video_decoder.cc
@@ -470,7 +470,7 @@
   // We may decode into this buffer again.
   // Note that |buffer| might no longer be in |picture_buffers_| if we've
   // replaced them.  That's okay.
-  buffer->set_in_client_use(false);
+  buffer->remove_client_use();
 
   // Also re-start decoding in case it was waiting for more pictures.
   DoDecode();
@@ -818,7 +818,7 @@
   DCHECK(texture_selector_);
   TRACE_EVENT0("gpu", "D3D11VideoDecoder::OutputResult");
 
-  picture_buffer->set_in_client_use(true);
+  picture_buffer->add_client_use();
 
   // Note: The pixel format doesn't matter.
   gfx::Rect visible_rect = picture->visible_rect();
diff --git a/media/gpu/windows/d3d11_vp9_accelerator.cc b/media/gpu/windows/d3d11_vp9_accelerator.cc
index 4f8c29fb..cc0e40e 100644
--- a/media/gpu/windows/d3d11_vp9_accelerator.cc
+++ b/media/gpu/windows/d3d11_vp9_accelerator.cc
@@ -63,7 +63,7 @@
   D3D11PictureBuffer* picture_buffer = client_->GetPicture();
   if (!picture_buffer)
     return nullptr;
-  return base::MakeRefCounted<D3D11VP9Picture>(picture_buffer);
+  return base::MakeRefCounted<D3D11VP9Picture>(picture_buffer, client_);
 }
 
 bool D3D11VP9Accelerator::BeginFrame(const D3D11VP9Picture& pic) {
diff --git a/media/gpu/windows/d3d11_vp9_picture.cc b/media/gpu/windows/d3d11_vp9_picture.cc
index 5efa82b5..913fefec 100644
--- a/media/gpu/windows/d3d11_vp9_picture.cc
+++ b/media/gpu/windows/d3d11_vp9_picture.cc
@@ -6,8 +6,10 @@
 
 namespace media {
 
-D3D11VP9Picture::D3D11VP9Picture(D3D11PictureBuffer* picture_buffer)
+D3D11VP9Picture::D3D11VP9Picture(D3D11PictureBuffer* picture_buffer,
+                                 D3D11VideoDecoderClient* client)
     : picture_buffer_(picture_buffer),
+      client_(client),
       picture_index_(picture_buffer_->picture_index()) {
   picture_buffer_->set_in_picture_use(true);
 }
@@ -16,4 +18,11 @@
   picture_buffer_->set_in_picture_use(false);
 }
 
+scoped_refptr<VP9Picture> D3D11VP9Picture::CreateDuplicate() {
+  // We've already sent off the base frame for rendering, so we can just stamp
+  // |picture_buffer_| with the updated timestamp.
+  client_->UpdateTimestamp(picture_buffer_);
+  return this;
+}
+
 }  // namespace media
diff --git a/media/gpu/windows/d3d11_vp9_picture.h b/media/gpu/windows/d3d11_vp9_picture.h
index 27b14440..68b2998 100644
--- a/media/gpu/windows/d3d11_vp9_picture.h
+++ b/media/gpu/windows/d3d11_vp9_picture.h
@@ -8,6 +8,7 @@
 #include "media/gpu/vp9_picture.h"
 
 #include "media/gpu/windows/d3d11_picture_buffer.h"
+#include "media/gpu/windows/d3d11_video_decoder_client.h"
 
 namespace media {
 
@@ -15,7 +16,8 @@
 
 class D3D11VP9Picture : public VP9Picture {
  public:
-  explicit D3D11VP9Picture(D3D11PictureBuffer* picture_buffer);
+  explicit D3D11VP9Picture(D3D11PictureBuffer* picture_buffer,
+                           D3D11VideoDecoderClient* client);
 
   D3D11PictureBuffer* picture_buffer() const { return picture_buffer_; }
 
@@ -24,8 +26,11 @@
  protected:
   ~D3D11VP9Picture() override;
 
+  scoped_refptr<VP9Picture> CreateDuplicate() override;
+
  private:
   D3D11PictureBuffer* picture_buffer_;
+  D3D11VideoDecoderClient* client_;
   size_t picture_index_;
 };
 
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index fc0ff827..f4d4428 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -800,67 +800,28 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator);
 };
 
+// TODO(jochin): Add support for all OOP-R specific APIs (eg. GetMailbox() and
+// GetSkImageViaReadback())
 class VideoTextureBacking : public cc::TextureBacking {
  public:
   explicit VideoTextureBacking(
       sk_sp<SkImage> sk_image,
-      const gpu::Mailbox& mailbox,
-      bool wraps_video_frame_texture,
       scoped_refptr<viz::RasterContextProvider> raster_context_provider)
-      : sk_image_(std::move(sk_image)),
-        sk_image_info_(sk_image_->imageInfo()),
-        mailbox_(mailbox),
-        wraps_video_frame_texture_(wraps_video_frame_texture) {
+      : sk_image_(std::move(sk_image)) {
     raster_context_provider_ = std::move(raster_context_provider);
   }
 
-  explicit VideoTextureBacking(
-      const gpu::Mailbox& mailbox,
-      const SkImageInfo& info,
-      bool wraps_video_frame_texture,
-      scoped_refptr<viz::RasterContextProvider> raster_context_provider)
-      : sk_image_info_(info),
-        mailbox_(mailbox),
-        wraps_video_frame_texture_(wraps_video_frame_texture) {
-    raster_context_provider_ = std::move(raster_context_provider);
+  const SkImageInfo& GetSkImageInfo() override {
+    return sk_image_->imageInfo();
   }
-
-  ~VideoTextureBacking() override {
-    auto* ri = raster_context_provider_->RasterInterface();
-    if (!wraps_video_frame_texture_) {
-      gpu::SyncToken sync_token;
-      ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
-      auto* sii = raster_context_provider_->SharedImageInterface();
-      sii->DestroySharedImage(sync_token, mailbox_);
-    }
-  }
-
-  const SkImageInfo& GetSkImageInfo() override { return sk_image_info_; }
   gpu::Mailbox GetMailbox() const override { return mailbox_; }
   sk_sp<SkImage> GetAcceleratedSkImage() override { return sk_image_; }
-  bool wraps_video_frame_texture() const { return wraps_video_frame_texture_; }
-  const scoped_refptr<viz::RasterContextProvider>& raster_context_provider()
-      const {
-    return raster_context_provider_;
-  }
-
   sk_sp<SkImage> GetSkImageViaReadback() override {
-    if (sk_image_)
+    if (sk_image_) {
       return sk_image_->makeNonTextureImage();
-
-    sk_sp<SkData> image_pixels =
-        SkData::MakeUninitialized(sk_image_info_.computeMinByteSize());
-    uint8_t* writable_pixels =
-        static_cast<uint8_t*>(image_pixels->writable_data());
-    gpu::raster::RasterInterface* ri =
-        raster_context_provider_->RasterInterface();
-    ri->ReadbackImagePixels(mailbox_, sk_image_info_,
-                            sk_image_info_.minRowBytes(), 0, 0,
-                            writable_pixels);
-    return SkImage::MakeRasterData(sk_image_info_, std::move(image_pixels),
-                                   sk_image_info_.minRowBytes());
+    }
+    return nullptr;
   }
-
   bool readPixels(const SkImageInfo& dst_info,
                   void* dst_pixels,
                   size_t dst_row_bytes,
@@ -870,13 +831,8 @@
       return sk_image_->readPixels(dst_info, dst_pixels, dst_row_bytes, src_x,
                                    src_y);
     }
-    gpu::raster::RasterInterface* ri =
-        raster_context_provider_->RasterInterface();
-    ri->ReadbackImagePixels(mailbox_, dst_info, dst_info.minRowBytes(), src_x,
-                            src_y, dst_pixels);
-    return true;
+    return false;
   }
-
   void FlushPendingSkiaOps() override {
     if (!raster_context_provider_ || !sk_image_)
       return;
@@ -884,19 +840,9 @@
   }
 
  private:
-  sk_sp<SkImage> sk_image_;
-  SkImageInfo sk_image_info_;
-  scoped_refptr<viz::RasterContextProvider> raster_context_provider_;
-
-  // This can be either the source VideoFrame's texture (if
-  // |wraps_video_frame_texture_| is true) or a newly allocated shared image
-  // (if |wraps_video_frame_texture_| is false) if a copy or conversion was
-  // necessary.
+  const sk_sp<SkImage> sk_image_;
   const gpu::Mailbox mailbox_;
-
-  // Whether |mailbox_| directly points to a texture of the VideoFrame
-  // (if true), or to an allocated shared image (if false).
-  const bool wraps_video_frame_texture_;
+  scoped_refptr<viz::RasterContextProvider> raster_context_provider_;
 };
 
 PaintCanvasVideoRenderer::PaintCanvasVideoRenderer()
@@ -952,11 +898,11 @@
   DCHECK(image);
 
   base::Optional<ScopedSharedImageAccess> source_access;
-  if (video_frame->HasTextures() && cache_->source_texture) {
-    DCHECK(cache_->texture_backing);
+  if (video_frame->HasTextures()) {
+    DCHECK(!cache_->source_mailbox.IsZero());
+    DCHECK(cache_->source_texture);
     source_access.emplace(raster_context_provider->RasterInterface(),
-                          cache_->source_texture,
-                          cache_->texture_backing->GetMailbox());
+                          cache_->source_texture, cache_->source_mailbox);
   }
 
   cc::PaintFlags video_flags;
@@ -1048,9 +994,9 @@
                               raster_context_provider->ContextSupport());
   }
   // Because we are not retaining a reference to the VideoFrame, it would be
-  // invalid for the texture_backing to directly wrap its texture(s), as they
-  // will be recycled.
-  DCHECK(!CacheBackingWrapsTexture());
+  // invalid for the cache to directly wrap its texture(s), as they will be
+  // recycled.
+  DCHECK(!cache_ || !cache_->wraps_video_frame_texture);
 }
 
 void PaintCanvasVideoRenderer::Copy(
@@ -1380,8 +1326,7 @@
     if (!raster_context_provider)
       return false;
     GrDirectContext* gr_context = raster_context_provider->GrContext();
-    if (!gr_context &&
-        !raster_context_provider->ContextCapabilities().supports_oop_raster)
+    if (!gr_context)
       return false;
 // TODO(crbug.com/1108154): Expand this uploading path to macOS, linux
 // chromeOS after collecting perf data and resolve failure cases.
@@ -1407,7 +1352,7 @@
     }
 
     DCHECK(cache_);
-    DCHECK(cache_->texture_backing);
+    DCHECK(!cache_->source_mailbox.IsZero());
     gpu::raster::RasterInterface* canvas_ri =
         raster_context_provider->RasterInterface();
 
@@ -1417,10 +1362,10 @@
     canvas_ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
 
     uint32_t intermediate_texture = SynchronizeAndImportMailbox(
-        destination_gl, sync_token, cache_->texture_backing->GetMailbox());
+        destination_gl, sync_token, cache_->source_mailbox);
     {
       ScopedSharedImageAccess access(destination_gl, intermediate_texture,
-                                     cache_->texture_backing->GetMailbox());
+                                     cache_->source_mailbox);
       VideoFrameCopyTextureOrSubTexture(
           destination_gl, cache_->coded_size, cache_->visible_rect,
           intermediate_texture, target, texture, internal_format, format, type,
@@ -1438,7 +1383,7 @@
     // Because we are not retaining a reference to the VideoFrame, it would be
     // invalid to keep the cache around if it directly wraps the VideoFrame
     // texture(s), as they will be recycled.
-    if (cache_->texture_backing->wraps_video_frame_texture())
+    if (cache_->wraps_video_frame_texture)
       cache_.reset();
 
     // Synchronize |video_frame| with the read operations in UpdateLastImage(),
@@ -1453,7 +1398,8 @@
     WaitAndReplaceSyncTokenClient client(destination_gl);
     video_frame->UpdateReleaseSyncToken(&client);
   }
-  DCHECK(!CacheBackingWrapsTexture());
+  DCHECK(!cache_ || !cache_->wraps_video_frame_texture);
+
   return true;
 }
 
@@ -1486,7 +1432,6 @@
     return false;
   }
 
-  // TODO(nazabris): Support OOP-R code path here that does not have GrContext.
   if (!raster_context_provider || !raster_context_provider->GrContext())
     return false;
 
@@ -1546,7 +1491,6 @@
     return false;
   }
 
-  // TODO(nazabris): Support OOP-R code path here that does not have GrContext.
   if (!raster_context_provider || !raster_context_provider->GrContext())
     return false;
 
@@ -1584,7 +1528,7 @@
   WaitAndReplaceSyncTokenClient client(source_ri);
   video_frame->UpdateReleaseSyncToken(&client);
 
-  DCHECK(!CacheBackingWrapsTexture());
+  DCHECK(!cache_ || !cache_->wraps_video_frame_texture);
   return true;
 }
 
@@ -1758,17 +1702,33 @@
 
 PaintCanvasVideoRenderer::Cache::Cache(int frame_id) : frame_id(frame_id) {}
 
-PaintCanvasVideoRenderer::Cache::~Cache() = default;
+PaintCanvasVideoRenderer::Cache::~Cache() {
+  if (!raster_context_provider)
+    return;
+
+  DCHECK(!source_mailbox.IsZero());
+  DCHECK(source_texture);
+  auto* ri = raster_context_provider->RasterInterface();
+  if (!wraps_video_frame_texture) {
+    gpu::SyncToken sync_token;
+    ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+    auto* sii = raster_context_provider->SharedImageInterface();
+    sii->DestroySharedImage(sync_token, source_mailbox);
+  }
+}
 
 bool PaintCanvasVideoRenderer::Cache::Recycle() {
-  DCHECK(!texture_backing->wraps_video_frame_texture());
-
-  paint_image = cc::PaintImage();
-  if (!texture_backing->unique())
+  DCHECK(!wraps_video_frame_texture);
+  if (!paint_image.HasExclusiveTextureAccess())
     return false;
 
   // Flush any pending GPU work using this texture.
-  texture_backing->FlushPendingSkiaOps();
+  paint_image.FlushPendingSkiaOps();
+
+  paint_image = cc::PaintImage();
+  // We need a new texture ID because skia will destroy the previous one with
+  // the SkImage.
+  source_texture = 0;
   return true;
 }
 
@@ -1776,9 +1736,9 @@
     scoped_refptr<VideoFrame> video_frame,
     viz::RasterContextProvider* raster_context_provider,
     bool allow_wrap_texture) {
-  DCHECK(!CacheBackingWrapsTexture());
+  DCHECK(!cache_ || !cache_->wraps_video_frame_texture);
   if (!cache_ || video_frame->unique_id() != cache_->frame_id ||
-      !cache_->paint_image) {
+      cache_->source_mailbox.IsZero()) {
     auto paint_image_builder =
         cc::PaintImageBuilder::WithDefault()
             .set_id(renderer_stable_id_)
@@ -1792,30 +1752,24 @@
     // could cause problems since the pool of VideoFrames has a fixed size.
     if (video_frame->HasTextures()) {
       DCHECK(raster_context_provider);
-      bool supports_oop_raster =
-          raster_context_provider->ContextCapabilities().supports_oop_raster;
-      DCHECK(supports_oop_raster || raster_context_provider->GrContext());
+      DCHECK(raster_context_provider->GrContext());
       auto* ri = raster_context_provider->RasterInterface();
       DCHECK(ri);
-      bool wraps_video_frame_texture = false;
-      gpu::Mailbox mailbox;
 
       if (allow_wrap_texture && video_frame->NumTextures() == 1) {
         cache_.emplace(video_frame->unique_id());
         const gpu::MailboxHolder& holder =
             GetVideoFrameMailboxHolder(video_frame.get());
-        mailbox = holder.mailbox;
+        cache_->source_mailbox = holder.mailbox;
         ri->WaitSyncTokenCHROMIUM(holder.sync_token.GetConstData());
-        wraps_video_frame_texture = true;
+        cache_->wraps_video_frame_texture = true;
       } else {
-        if (cache_ && cache_->texture_backing &&
-            cache_->texture_backing->raster_context_provider() ==
-                raster_context_provider &&
+        if (cache_ &&
+            cache_->raster_context_provider == raster_context_provider &&
             cache_->coded_size == video_frame->coded_size() &&
             cache_->Recycle()) {
           // We can reuse the shared image from the previous cache.
           cache_->frame_id = video_frame->unique_id();
-          mailbox = cache_->texture_backing->GetMailbox();
         } else {
           cache_.emplace(video_frame->unique_id());
           auto* sii = raster_context_provider->SharedImageInterface();
@@ -1823,10 +1777,11 @@
           // cached shared image is created without it.
           uint32_t flags =
               gpu::SHARED_IMAGE_USAGE_GLES2 | gpu::SHARED_IMAGE_USAGE_RASTER;
-          if (supports_oop_raster) {
+          if (raster_context_provider->ContextCapabilities()
+                  .supports_oop_raster) {
             flags |= gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
           }
-          mailbox = sii->CreateSharedImage(
+          cache_->source_mailbox = sii->CreateSharedImage(
               viz::ResourceFormat::RGBA_8888, video_frame->coded_size(),
               gfx::ColorSpace(), kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
               flags, gpu::kNullSurfaceHandle);
@@ -1836,55 +1791,48 @@
         if (video_frame->NumTextures() == 1) {
           auto frame_mailbox =
               SynchronizeVideoFrameSingleMailbox(ri, video_frame.get());
-          ri->CopySubTexture(frame_mailbox, mailbox, GL_TEXTURE_2D, 0, 0, 0, 0,
-                             video_frame->coded_size().width(),
-                             video_frame->coded_size().height(), GL_FALSE,
-                             GL_FALSE);
+          ri->CopySubTexture(
+              frame_mailbox, cache_->source_mailbox, GL_TEXTURE_2D, 0, 0, 0, 0,
+              video_frame->coded_size().width(),
+              video_frame->coded_size().height(), GL_FALSE, GL_FALSE);
         } else {
-          gpu::MailboxHolder dest_holder{mailbox, gpu::SyncToken(),
-                                         GL_TEXTURE_2D};
+          gpu::MailboxHolder dest_holder{cache_->source_mailbox,
+                                         gpu::SyncToken(), GL_TEXTURE_2D};
           VideoFrameYUVConverter::ConvertYUVVideoFrameNoCaching(
               video_frame.get(), raster_context_provider, dest_holder);
         }
-        if (!supports_oop_raster)
-          raster_context_provider->GrContext()->flushAndSubmit();
+        raster_context_provider->GrContext()->flushAndSubmit();
       }
 
+      // TODO(jochin): Don't always generate SkImage here.
+      DCHECK(cache_->source_texture == 0);
+      cache_->source_texture =
+          ri->CreateAndConsumeForGpuRaster(cache_->source_mailbox);
+
+      // TODO(nazabris): Handle scoped access correctly. This follows the
+      // current pattern but is most likely bugged. Access should last for the
+      // lifetime of the SkImage.
+      ScopedSharedImageAccess(ri, cache_->source_texture,
+                              cache_->source_mailbox);
+      auto source_image =
+          WrapGLTexture(cache_->wraps_video_frame_texture
+                            ? video_frame->mailbox_holder(0).texture_target
+                            : GL_TEXTURE_2D,
+                        cache_->source_texture, video_frame->coded_size(),
+                        video_frame->ColorSpace(), raster_context_provider);
+      if (!source_image) {
+        // Couldn't create the SkImage.
+        cache_.reset();
+        return false;
+      }
+      cache_->raster_context_provider = raster_context_provider;
       cache_->coded_size = video_frame->coded_size();
       cache_->visible_rect = video_frame->visible_rect();
-      if (!cache_->texture_backing) {
-        if (supports_oop_raster) {
-          SkImageInfo sk_image_info = SkImageInfo::Make(
-              gfx::SizeToSkISize(cache_->coded_size), kRGBA_8888_SkColorType,
-              kPremul_SkAlphaType, video_frame->ColorSpace().ToSkColorSpace());
-          cache_->texture_backing = sk_make_sp<VideoTextureBacking>(
-              mailbox, sk_image_info, wraps_video_frame_texture,
-              raster_context_provider);
-        } else {
-          cache_->source_texture = ri->CreateAndConsumeForGpuRaster(mailbox);
 
-          // TODO(nazabris): Handle scoped access correctly. This follows the
-          // current pattern but is most likely bugged. Access should last for
-          // the lifetime of the SkImage.
-          ScopedSharedImageAccess(ri, cache_->source_texture, mailbox);
-          auto source_image =
-              WrapGLTexture(wraps_video_frame_texture
-                                ? video_frame->mailbox_holder(0).texture_target
-                                : GL_TEXTURE_2D,
-                            cache_->source_texture, video_frame->coded_size(),
-                            video_frame->ColorSpace(), raster_context_provider);
-          if (!source_image) {
-            // Couldn't create the SkImage.
-            cache_.reset();
-            return false;
-          }
-          cache_->texture_backing = sk_make_sp<VideoTextureBacking>(
-              std::move(source_image), mailbox, wraps_video_frame_texture,
-              raster_context_provider);
-        }
-      }
       paint_image_builder.set_texture_backing(
-          cache_->texture_backing, cc::PaintImage::GetNextContentId());
+          sk_sp<VideoTextureBacking>(new VideoTextureBacking(
+              std::move(source_image), raster_context_provider)),
+          cc::PaintImage::GetNextContentId());
     } else {
       cache_.emplace(video_frame->unique_id());
       paint_image_builder.set_paint_image_generator(
@@ -1892,7 +1840,7 @@
     }
     cache_->paint_image = paint_image_builder.TakePaintImage();
     if (!cache_->paint_image) {
-      // Couldn't create the PaintImage.
+      // Couldn't create the SkImage.
       cache_.reset();
       return false;
     }
@@ -1955,9 +1903,4 @@
   return gfx::Size(cache_->paint_image.width(), cache_->paint_image.height());
 }
 
-bool PaintCanvasVideoRenderer::CacheBackingWrapsTexture() const {
-  return cache_ && cache_->texture_backing &&
-         cache_->texture_backing->wraps_video_frame_texture();
-}
-
 }  // namespace media
diff --git a/media/renderers/paint_canvas_video_renderer.h b/media/renderers/paint_canvas_video_renderer.h
index 0c6b406..a422e23 100644
--- a/media/renderers/paint_canvas_video_renderer.h
+++ b/media/renderers/paint_canvas_video_renderer.h
@@ -41,7 +41,6 @@
 }
 
 namespace media {
-class VideoTextureBacking;
 
 // Handles rendering of VideoFrames to PaintCanvases.
 class MEDIA_EXPORT PaintCanvasVideoRenderer {
@@ -212,15 +211,22 @@
     // to the visible size of the VideoFrame. Its contents are generated lazily.
     cc::PaintImage paint_image;
 
-    // The backing for the source texture. This is also responsible for managing
-    // the lifetime of the texture.
-    sk_sp<VideoTextureBacking> texture_backing;
+    // The context provider used to generate |source_mailbox| and
+    // |source_texture|. This is only set if the VideoFrame was texture-backed.
+    scoped_refptr<viz::RasterContextProvider> raster_context_provider;
 
-    // The GL texture ID used in non-OOP code path.
+    // The mailbox for the source texture. This can be either the source
+    // VideoFrame's texture (if |wraps_video_frame_texture| is true) or a newly
+    // allocated shared image (if |wraps_video_frame_texture| is false) if a
+    // copy or conversion was necessary.
+    // This is only set if the VideoFrame was texture-backed.
+    gpu::Mailbox source_mailbox;
+
+    // The texture ID created when importing |source_mailbox|.
     // This is only set if the VideoFrame was texture-backed.
     uint32_t source_texture = 0;
 
-    // The allocated size of VideoFrame texture.
+    // The allocated size of |source_mailbox|.
     // This is only set if the VideoFrame was texture-backed.
     gfx::Size coded_size;
 
@@ -229,6 +235,10 @@
     // This is only set if the VideoFrame was texture-backed.
     gfx::Rect visible_rect;
 
+    // Whether |source_mailbox| directly points to a texture of the VideoFrame
+    // (if true), or to an allocated shared image (if false).
+    bool wraps_video_frame_texture = false;
+
     // Used to allow recycling of the previous shared image. This requires that
     // no external users have access to this resource via SkImage. Returns true
     // if the existing resource can be recycled.
@@ -256,8 +266,6 @@
       unsigned int type,
       bool flip_y);
 
-  bool CacheBackingWrapsTexture() const;
-
   base::Optional<Cache> cache_;
 
   // If |cache_| is not used for a while, it's deleted to save memory.
diff --git a/net/third_party/quiche/BUILD.gn b/net/third_party/quiche/BUILD.gn
index f3b0b3d..082c6ae 100644
--- a/net/third_party/quiche/BUILD.gn
+++ b/net/third_party/quiche/BUILD.gn
@@ -72,6 +72,8 @@
       "src/http2/decoder/payload_decoders/ping_payload_decoder.h",
       "src/http2/decoder/payload_decoders/priority_payload_decoder.cc",
       "src/http2/decoder/payload_decoders/priority_payload_decoder.h",
+      "src/http2/decoder/payload_decoders/priority_update_payload_decoder.cc",
+      "src/http2/decoder/payload_decoders/priority_update_payload_decoder.h",
       "src/http2/decoder/payload_decoders/push_promise_payload_decoder.cc",
       "src/http2/decoder/payload_decoders/push_promise_payload_decoder.h",
       "src/http2/decoder/payload_decoders/rst_stream_payload_decoder.cc",
@@ -1195,6 +1197,7 @@
     "src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h",
     "src/http2/decoder/payload_decoders/ping_payload_decoder_test.cc",
     "src/http2/decoder/payload_decoders/priority_payload_decoder_test.cc",
+    "src/http2/decoder/payload_decoders/priority_update_payload_decoder_test.cc",
     "src/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc",
     "src/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc",
     "src/http2/decoder/payload_decoders/settings_payload_decoder_test.cc",
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 3c9d005..bdfc3d8 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -499,6 +499,9 @@
   if (status_ != OK)
     return;
 
+  if (context_->require_network_isolation_key())
+    DCHECK(!isolation_info_.IsEmpty());
+
   // Some values can be NULL, but the job factory must not be.
   DCHECK(context_->job_factory());
 
diff --git a/pdf/BUILD.gn b/pdf/BUILD.gn
index f78853d..759e3e5 100644
--- a/pdf/BUILD.gn
+++ b/pdf/BUILD.gn
@@ -253,6 +253,7 @@
       ":internal",
       ":ppapi_migration",
       "//base",
+      "//base:i18n",
       "//build:chromeos_buildflags",
       "//net",
       "//ppapi/cpp:objects",
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index 4a4fb96..d443a21 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -17,6 +17,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/feature_list.h"
+#include "base/i18n/number_formatting.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
@@ -130,6 +131,7 @@
 // Metadata (Plugin -> Page)
 constexpr char kJSMetadataType[] = "metadata";
 constexpr char kJSMetadataData[] = "metadataData";
+constexpr char kJSVersion[] = "version";
 constexpr char kJSTitle[] = "title";
 constexpr char kJSAuthor[] = "author";
 constexpr char kJSSubject[] = "subject";
@@ -535,6 +537,48 @@
   return pp_text_runs;
 }
 
+// Converts |version| to a formatted string.
+base::string16 GetFormattedVersion(PdfVersion version) {
+  double value = 0;
+  switch (version) {
+    case PdfVersion::k1_0:
+      value = 1.0;
+      break;
+    case PdfVersion::k1_1:
+      value = 1.1;
+      break;
+    case PdfVersion::k1_2:
+      value = 1.2;
+      break;
+    case PdfVersion::k1_3:
+      value = 1.3;
+      break;
+    case PdfVersion::k1_4:
+      value = 1.4;
+      break;
+    case PdfVersion::k1_5:
+      value = 1.5;
+      break;
+    case PdfVersion::k1_6:
+      value = 1.6;
+      break;
+    case PdfVersion::k1_7:
+      value = 1.7;
+      break;
+    case PdfVersion::k2_0:
+      value = 2.0;
+      break;
+    case PdfVersion::kUnknown:
+    case PdfVersion::k1_8:  // Not an actual version
+      return base::string16();
+  }
+  // The default case is excluded from the above switch statement to ensure that
+  // all supported versions are determinantly handled.
+
+  DCHECK_NE(0, value);
+  return base::FormatDouble(value, 1);
+}
+
 }  // namespace
 
 OutOfProcessInstance::OutOfProcessInstance(PP_Instance instance)
@@ -2381,6 +2425,10 @@
   const DocumentMetadata& document_metadata = engine()->GetDocumentMetadata();
   pp::VarDictionary metadata_data;
 
+  base::string16 version = GetFormattedVersion(document_metadata.version);
+  if (!version.empty())
+    metadata_data.Set(pp::Var(kJSVersion), pp::Var(base::UTF16ToUTF8(version)));
+
   if (!document_metadata.title.empty())
     metadata_data.Set(pp::Var(kJSTitle), pp::Var(document_metadata.title));
 
diff --git a/remoting/protocol/webrtc_data_stream_adapter.cc b/remoting/protocol/webrtc_data_stream_adapter.cc
index df52f61..2f972cc 100644
--- a/remoting/protocol/webrtc_data_stream_adapter.cc
+++ b/remoting/protocol/webrtc_data_stream_adapter.cc
@@ -59,8 +59,7 @@
 
   rtc::CopyOnWriteBuffer buffer;
   buffer.SetSize(message->ByteSize());
-  message->SerializeWithCachedSizesToArray(
-      reinterpret_cast<uint8_t*>(buffer.data()));
+  message->SerializeWithCachedSizesToArray(buffer.MutableData());
   pending_messages_.emplace(
       webrtc::DataBuffer(std::move(buffer), true /* binary */),
       std::move(done));
diff --git a/services/device/battery/android/java/src/org/chromium/device/battery/BatteryStatusManager.java b/services/device/battery/android/java/src/org/chromium/device/battery/BatteryStatusManager.java
index 44c2696..4d5d3f4d 100644
--- a/services/device/battery/android/java/src/org/chromium/device/battery/BatteryStatusManager.java
+++ b/services/device/battery/android/java/src/org/chromium/device/battery/BatteryStatusManager.java
@@ -15,6 +15,7 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
+import org.chromium.base.task.AsyncTask;
 import org.chromium.device.mojom.BatteryStatus;
 
 /**
@@ -140,10 +141,22 @@
         batteryStatus.level = level;
 
         if (mAndroidBatteryManager != null) {
-            updateBatteryStatus(batteryStatus);
+            // Doing an AsyncTask since querying the BatteryManager might be slow. In the past, it
+            // has caused ANRs when executed on the main thread - see crbug.com/1163401.
+            new AsyncTask<BatteryStatus>() {
+                @Override
+                protected BatteryStatus doInBackground() {
+                    updateBatteryStatus(batteryStatus);
+                    return batteryStatus;
+                }
+                @Override
+                protected void onPostExecute(BatteryStatus batteryStatus) {
+                    mCallback.onBatteryStatusChanged(batteryStatus);
+                }
+            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        } else {
+            mCallback.onBatteryStatusChanged(batteryStatus);
         }
-
-        mCallback.onBatteryStatusChanged(batteryStatus);
     }
 
     private void updateBatteryStatus(BatteryStatus batteryStatus) {
diff --git a/services/media_session/public/cpp/media_image_manager.cc b/services/media_session/public/cpp/media_image_manager.cc
index 825c90c..73fde77 100644
--- a/services/media_session/public/cpp/media_image_manager.cc
+++ b/services/media_session/public/cpp/media_image_manager.cc
@@ -88,6 +88,16 @@
     }
   }
 
+  // If we haven't found an image based on size then we should check if there
+  // are any images that have an "any" size which is denoted by a single empty
+  // gfx::Size value.
+  if (!selected.has_value()) {
+    for (auto& image : images) {
+      if (image.sizes.size() == 1 && image.sizes[0].IsEmpty())
+        return image;
+    }
+  }
+
   return selected;
 }
 
diff --git a/services/media_session/public/cpp/media_image_manager_unittest.cc b/services/media_session/public/cpp/media_image_manager_unittest.cc
index 9c8d0189..dceed3b 100644
--- a/services/media_session/public/cpp/media_image_manager_unittest.cc
+++ b/services/media_session/public/cpp/media_image_manager_unittest.cc
@@ -204,4 +204,17 @@
   EXPECT_EQ(image1, manager()->SelectImage(images));
 }
 
+TEST_F(MediaImageManagerTest, PickImageWithAnySize) {
+  MediaImageManager manager(10, 10);
+
+  std::vector<MediaImage> images;
+
+  // Empty size denotes "any" value.
+  MediaImage image;
+  image.sizes.push_back(gfx::Size());
+  images.push_back(image);
+
+  EXPECT_TRUE(manager.SelectImage(images));
+}
+
 }  // namespace media_session
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 238fc37..afaf375 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -33,6 +33,7 @@
 #include "build/chromecast_buildflags.h"
 #include "build/chromeos_buildflags.h"
 #include "components/cookie_config/cookie_store_util.h"
+#include "components/domain_reliability/features.h"
 #include "components/domain_reliability/monitor.h"
 #include "components/network_session_configurator/browser/network_session_configurator.h"
 #include "components/network_session_configurator/common/network_switches.h"
@@ -42,6 +43,7 @@
 #include "components/prefs/pref_service_factory.h"
 #include "crypto/sha2.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "net/base/features.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_delegate.h"
@@ -853,7 +855,7 @@
     const net::NetworkIsolationKey& network_isolation_key,
     const base::Optional<std::string>& user_agent,
     base::Value body) {
-  if (url_request_context_->require_network_isolation_key())
+  if (require_network_isolation_key_)
     DCHECK(!network_isolation_key.IsEmpty());
 
   DCHECK(body.is_dict());
@@ -885,7 +887,7 @@
 void NetworkContext::QueueSignedExchangeReport(
     mojom::SignedExchangeReportPtr report,
     const net::NetworkIsolationKey& network_isolation_key) {
-  if (url_request_context_->require_network_isolation_key())
+  if (require_network_isolation_key_)
     DCHECK(!network_isolation_key.IsEmpty());
 
   net::NetworkErrorLoggingService* logging_service =
@@ -1387,7 +1389,7 @@
     const std::string& ocsp_result,
     const std::string& sct_list,
     VerifyCertForSignedExchangeCallback callback) {
-  if (url_request_context_->require_network_isolation_key())
+  if (require_network_isolation_key_)
     DCHECK(!network_isolation_key.IsEmpty());
 
   int cert_verify_id = ++next_cert_verify_id_;
@@ -2193,8 +2195,29 @@
   auto result =
       URLRequestContextOwner(std::move(pref_service), builder.Build());
 
-  result.url_request_context->set_require_network_isolation_key(
-      params_->require_network_isolation_key);
+  require_network_isolation_key_ = params_->require_network_isolation_key;
+
+  // If `require_network_isolation_key_` is true, but the features that can
+  // trigger another URLRequest are not set to respect NetworkIsolationKeys,
+  // the URLRequests that they create might not have a NIK, so only set the
+  // corresponding value in the URLRequestContext to true at the URLRequest
+  // layer if all those features are set to respect NIK.
+  if (require_network_isolation_key_ &&
+      base::FeatureList::IsEnabled(
+          net::features::kPartitionConnectionsByNetworkIsolationKey) &&
+      base::FeatureList::IsEnabled(
+          net::features::kPartitionExpectCTStateByNetworkIsolationKey) &&
+      base::FeatureList::IsEnabled(
+          net::features::kPartitionHttpServerPropertiesByNetworkIsolationKey) &&
+      base::FeatureList::IsEnabled(
+          net::features::kPartitionNelAndReportingByNetworkIsolationKey) &&
+      base::FeatureList::IsEnabled(
+          net::features::kPartitionSSLSessionsByNetworkIsolationKey) &&
+      base::FeatureList::IsEnabled(
+          domain_reliability::features::
+              kPartitionDomainReliabilityByNetworkIsolationKey)) {
+    result.url_request_context->set_require_network_isolation_key(true);
+  }
 
   // Subscribe the CertVerifier to configuration changes that are exposed via
   // the mojom::SSLConfig, but which are not part of the
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 022b586d..90391023 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -529,6 +529,10 @@
     return &cors_origin_access_list_;
   }
 
+  bool require_network_isolation_key() const {
+    return require_network_isolation_key_;
+  }
+
  private:
   URLRequestContextOwner MakeURLRequestContext(
       mojo::PendingRemote<mojom::URLLoaderFactory>
@@ -758,6 +762,11 @@
   // manages the lifetiem of a WebBundleURLLoaderFactory object.
   WebBundleManager web_bundle_manager_;
 
+  // Whether all external consumers are expected to provide a non-empty
+  // NetworkIsolationKey with all requests. When set, enabled a variety of
+  // DCHECKs on APIs used by external callers.
+  bool require_network_isolation_key_ = false;
+
   base::WeakPtrFactory<NetworkContext> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(NetworkContext);
diff --git a/services/network/public/cpp/data_element.cc b/services/network/public/cpp/data_element.cc
index 32879fe..db8d7309 100644
--- a/services/network/public/cpp/data_element.cc
+++ b/services/network/public/cpp/data_element.cc
@@ -28,11 +28,10 @@
 DataElement::DataElement(DataElement&& other) = default;
 DataElement& DataElement::operator=(DataElement&& other) = default;
 
-void DataElement::SetToFilePathRange(
-    const base::FilePath& path,
-    uint64_t offset,
-    uint64_t length,
-    const base::Time& expected_modification_time) {
+void DataElement::SetToFilePathRange(const base::FilePath& path,
+                                     uint64_t offset,
+                                     uint64_t length,
+                                     base::Time expected_modification_time) {
   type_ = mojom::DataElementType::kFile;
   path_ = path;
   offset_ = offset;
diff --git a/services/network/public/cpp/data_element.h b/services/network/public/cpp/data_element.h
index 7eeb277b..1e4d1e8 100644
--- a/services/network/public/cpp/data_element.h
+++ b/services/network/public/cpp/data_element.h
@@ -75,29 +75,11 @@
     length_ = buf_.size();
   }
 
-  // Sets TYPE_BYTES data, and clears the internal bytes buffer.
-  // For use with AppendBytes.
-  void SetToEmptyBytes() {
-    type_ = mojom::DataElementType::kBytes;
-    buf_.clear();
-    length_ = 0;
-  }
-
-  // Copies and appends the given data into the element. SetToEmptyBytes or
-  // SetToBytes must be called before this method.
-  void AppendBytes(const char* bytes, int bytes_len) {
-    DCHECK_EQ(type_, mojom::DataElementType::kBytes);
-    DCHECK_NE(length_, std::numeric_limits<uint64_t>::max());
-    buf_.insert(buf_.end(), reinterpret_cast<const uint8_t*>(bytes),
-                reinterpret_cast<const uint8_t*>(bytes + bytes_len));
-    length_ = buf_.size();
-  }
-
   // Sets TYPE_FILE data with range.
   void SetToFilePathRange(const base::FilePath& path,
                           uint64_t offset,
                           uint64_t length,
-                          const base::Time& expected_modification_time);
+                          base::Time expected_modification_time);
 
   // Sets TYPE_DATA_PIPE data. The data pipe consumer can safely wait for the
   // callback passed to Read() to be invoked before reading the request body.
diff --git a/services/network/public/cpp/is_potentially_trustworthy.cc b/services/network/public/cpp/is_potentially_trustworthy.cc
index 87a5005..3dacd3c 100644
--- a/services/network/public/cpp/is_potentially_trustworthy.cc
+++ b/services/network/public/cpp/is_potentially_trustworthy.cc
@@ -199,6 +199,27 @@
   return false;
 }
 
+bool IsSchemeConsideredAuthenticated(base::StringPiece scheme) {
+  // The code below is based on the specification at
+  // https://w3c.github.io/webappsec-secure-contexts/#potentially-trustworthy-origin
+
+  // 7. If origin’s scheme component is one which the user agent considers to be
+  //    authenticated, return "Potentially Trustworthy".
+  //    Note: See §7.1 Packaged Applications for detail here.
+  //
+  // Note that this ignores some schemes that are considered trustworthy by
+  // higher layers (e.g. see GetSchemesBypassingSecureContextCheck in //chrome).
+  //
+  // See also
+  // - content::ContentClient::AddAdditionalSchemes and
+  //   content::ContentClient::Schemes::local_schemes and
+  //   content::ContentClient::Schemes::secure_schemes
+  // - url::AddLocalScheme
+  // - url::AddSecureScheme
+  return base::Contains(url::GetSecureSchemes(), scheme) ||
+         base::Contains(url::GetLocalSchemes(), scheme);
+}
+
 }  // namespace
 
 bool IsOriginPotentiallyTrustworthy(const url::Origin& origin) {
@@ -228,27 +249,16 @@
 
   // 6. If origin’s scheme component is file, return "Potentially Trustworthy".
   //
-  // This is somewhat redundant with the GetLocalSchemes-based check below.
+  // This is somewhat redundant with the GetLocalSchemes-based
+  // IsSchemeConsideredAuthenticated check below.
   if (origin.scheme() == url::kFileScheme)
     return true;
 
   // 7. If origin’s scheme component is one which the user agent considers to be
   //    authenticated, return "Potentially Trustworthy".
   //    Note: See §7.1 Packaged Applications for detail here.
-  //
-  // Note that this ignores some schemes that are considered trustworthy by
-  // higher layers (e.g. see GetSchemesBypassingSecureContextCheck in //chrome).
-  //
-  // See also
-  // - content::ContentClient::AddAdditionalSchemes and
-  //   content::ContentClient::Schemes::local_schemes and
-  //   content::ContentClient::Schemes::secure_schemes
-  // - url::AddLocalScheme
-  // - url::AddSecureScheme
-  if (base::Contains(url::GetSecureSchemes(), origin.scheme()) ||
-      base::Contains(url::GetLocalSchemes(), origin.scheme())) {
+  if (IsSchemeConsideredAuthenticated(origin.scheme()))
     return true;
-  }
 
   // 8. If origin has been configured as a trustworthy origin, return
   //    "Potentially Trustworthy".
@@ -278,7 +288,15 @@
   //    Note: The origin of blob: and filesystem: URLs is the origin of the
   //    context in which they were created. Therefore, blobs created in a
   //    trustworthy origin will themselves be potentially trustworthy.
-  return IsOriginPotentiallyTrustworthy(url::Origin::Create(url));
+  url::Origin origin = url::Origin::Create(url);
+  if (origin.opaque() && IsSchemeConsideredAuthenticated(url.scheme_piece())) {
+    // Authenticated schemes should be treated as trustworthy, even if they
+    // translate into an opaque origin (e.g. because some of them might also be
+    // registered as a no-access, like the //content-layer chrome-error:// or
+    // the //chrome-layer chrome-native://).
+    return true;
+  }
+  return IsOriginPotentiallyTrustworthy(origin);
 }
 
 // static
diff --git a/services/network/public/cpp/is_potentially_trustworthy_unittest.cc b/services/network/public/cpp/is_potentially_trustworthy_unittest.cc
index 2b72d8fb..0f595fa2 100644
--- a/services/network/public/cpp/is_potentially_trustworthy_unittest.cc
+++ b/services/network/public/cpp/is_potentially_trustworthy_unittest.cc
@@ -66,7 +66,7 @@
   EXPECT_TRUE(IsUrlPotentiallyTrustworthy("about:srcdoc#ref"));
   EXPECT_TRUE(IsUrlPotentiallyTrustworthy("about:srcdoc?x=2#ref"));
 
-  EXPECT_FALSE(IsUrlPotentiallyTrustworthy("about:about"));
+  EXPECT_TRUE(IsUrlPotentiallyTrustworthy("about:mumble"));
 
   EXPECT_TRUE(IsUrlPotentiallyTrustworthy("data:test/plain;blah"));
   EXPECT_FALSE(IsUrlPotentiallyTrustworthy("javascript:alert('blah')"));
@@ -151,37 +151,39 @@
       IsUrlPotentiallyTrustworthy("quic-transport://example.com/counter"));
 }
 
-// Tests the trustworthiness of an URL and origin whose scheme was added to the
-// custom sets of standard and secure schemes. A scheme must be added to both
-// to be considered trustworthy.
-TEST(IsPotentiallyTrustworthy, CustomScheme) {
-  const char* custom_scheme = "custom-scheme";
-  const char* custom_scheme_example = "custom-scheme://example.com";
+TEST(IsPotentiallyTrustworthy, CustomSchemes) {
+  url::ScopedSchemeRegistryForTests scoped_registry;
+  url::AddSecureScheme("sec-nonstd-scheme");
+  url::AddSecureScheme("sec-std-scheme");
+  url::AddStandardScheme("sec-std-scheme", url::SCHEME_WITH_HOST);
+  url::AddSecureScheme("sec-noaccess-scheme");
+  url::AddNoAccessScheme("sec-noaccess-scheme");
+  url::AddNoAccessScheme("nonsec-noaccess-scheme");
 
-  EXPECT_FALSE(IsOriginPotentiallyTrustworthy(custom_scheme_example));
-  EXPECT_FALSE(IsUrlPotentiallyTrustworthy(custom_scheme_example));
+  // Unrecognized / unknown schemes are not trustworthy.
+  EXPECT_FALSE(IsOriginPotentiallyTrustworthy("unknown-scheme://example.com"));
+  EXPECT_FALSE(IsUrlPotentiallyTrustworthy("unknown-scheme://example.com"));
 
-  {
-    url::ScopedSchemeRegistryForTests scoped_registry;
-    url::AddSecureScheme(custom_scheme);
-    EXPECT_FALSE(IsOriginPotentiallyTrustworthy(custom_scheme_example));
-    EXPECT_FALSE(IsUrlPotentiallyTrustworthy(custom_scheme_example));
-  }
+  // Secure URLs are trustworthy, even if their scheme is also marked as
+  // no-access, or are not marked as standard.  See also //chrome-layer
+  // ChromeContentClientTest.AdditionalSchemes test and
+  // https://crbug.com/734581.
+  EXPECT_TRUE(IsUrlPotentiallyTrustworthy("sec-nonstd-scheme://blah/x.js"));
+  EXPECT_TRUE(IsUrlPotentiallyTrustworthy("sec-std-scheme://blah/x.js"));
+  EXPECT_TRUE(IsUrlPotentiallyTrustworthy("sec-noaccess-scheme://blah/x.js"));
+  EXPECT_TRUE(IsOriginPotentiallyTrustworthy("sec-std-scheme://blah/x.js"));
+  // No-access and non-standard/non-local schemes translate into an
+  // untrustworthy, opaque origin.
+  // TODO(lukasza): Maybe if the spec had a notion of an origin *precursor*,
+  // then it could inspect the scheme of the precursor.  After this, it may be
+  // possible to EXPECT_TRUE below...
+  EXPECT_FALSE(IsOriginPotentiallyTrustworthy("sec-nonstd-scheme://blah/x.js"));
+  EXPECT_FALSE(
+      IsOriginPotentiallyTrustworthy("sec-noaccess-scheme://blah/x.js"));
 
-  {
-    url::ScopedSchemeRegistryForTests scoped_registry;
-    url::AddStandardScheme(custom_scheme, url::SchemeType::SCHEME_WITH_HOST);
-    EXPECT_FALSE(IsOriginPotentiallyTrustworthy(custom_scheme_example));
-    EXPECT_FALSE(IsUrlPotentiallyTrustworthy(custom_scheme_example));
-  }
-
-  {
-    url::ScopedSchemeRegistryForTests scoped_registry;
-    url::AddStandardScheme(custom_scheme, url::SchemeType::SCHEME_WITH_HOST);
-    url::AddSecureScheme(custom_scheme);
-    EXPECT_TRUE(IsOriginPotentiallyTrustworthy(custom_scheme_example));
-    EXPECT_TRUE(IsUrlPotentiallyTrustworthy(custom_scheme_example));
-  }
+  // No-access, non-secure schemes are untrustworthy.
+  EXPECT_FALSE(IsUrlPotentiallyTrustworthy("nonsec-noaccess-scheme:blah"));
+  EXPECT_FALSE(IsOriginPotentiallyTrustworthy("nonsec-noaccess-scheme:blah"));
 }
 
 // Tests that were for the removed blink::network_utils::IsOriginSecure.
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index f33e95c..ad66e31 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -461,6 +461,7 @@
     mojom::CrossOriginEmbedderPolicyReporter* coep_reporter,
     uint32_t request_id,
     int keepalive_request_size,
+    bool require_network_isolation_key,
     scoped_refptr<ResourceSchedulerClient> resource_scheduler_client,
     base::WeakPtr<KeepaliveStatisticsRecorder> keepalive_statistics_recorder,
     base::WeakPtr<NetworkUsageAccumulator> network_usage_accumulator,
@@ -561,7 +562,7 @@
                                    origin, origin, net::SiteForCookies()));
   }
 
-  if (url_request_context_->require_network_isolation_key())
+  if (require_network_isolation_key)
     DCHECK(!url_request_->isolation_info().IsEmpty());
 
   if (factory_params_->disable_secure_dns) {
diff --git a/services/network/url_loader.h b/services/network/url_loader.h
index 600dd2f..1937335 100644
--- a/services/network/url_loader.h
+++ b/services/network/url_loader.h
@@ -105,6 +105,10 @@
   // have the |obey_origin_policy| flag set.
   // |trust_token_helper_factory| must be non-null exactly when the request has
   // Trust Tokens parameters.
+  //
+  // TODO(mmenke): This parameter list is getting a bit excessive. Either pass
+  // in a struct, or just pass in a pointer to the NetworkContext or
+  // URLLoaderFactory directly.
   URLLoader(
       net::URLRequestContext* url_request_context,
       mojom::NetworkServiceClient* network_service_client,
@@ -120,6 +124,7 @@
       mojom::CrossOriginEmbedderPolicyReporter* reporter,
       uint32_t request_id,
       int keepalive_request_size,
+      bool require_network_isolation_key,
       scoped_refptr<ResourceSchedulerClient> resource_scheduler_client,
       base::WeakPtr<KeepaliveStatisticsRecorder> keepalive_statistics_recorder,
       base::WeakPtr<NetworkUsageAccumulator> network_usage_accumulator,
diff --git a/services/network/url_loader_factory.cc b/services/network/url_loader_factory.cc
index 9d75573..adf7d7e 100644
--- a/services/network/url_loader_factory.cc
+++ b/services/network/url_loader_factory.cc
@@ -292,7 +292,8 @@
       std::move(data_pipe_use_tracker),
       static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation),
       params_.get(), coep_reporter_ ? coep_reporter_.get() : nullptr,
-      request_id, keepalive_request_size, resource_scheduler_client_,
+      request_id, keepalive_request_size,
+      context_->require_network_isolation_key(), resource_scheduler_client_,
       std::move(keepalive_statistics_recorder),
       std::move(network_usage_accumulator),
       header_client_.is_bound() ? header_client_.get() : nullptr,
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc
index 31ae172..da9ed8f 100644
--- a/services/network/url_loader_unittest.cc
+++ b/services/network/url_loader_unittest.cc
@@ -612,10 +612,11 @@
         client_.CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params,
         /*coep_reporter=*/nullptr, 0 /* request_id */,
-        0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        0 /* keepalive_request_size */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     ran_ = true;
@@ -1551,7 +1552,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -1609,7 +1611,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -1668,7 +1671,8 @@
       loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(),
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params, /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -1749,7 +1753,8 @@
       loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(),
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params, /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -1819,7 +1824,8 @@
       loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(),
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params, /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -1884,7 +1890,8 @@
       loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(),
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params, /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -2140,7 +2147,8 @@
       loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(),
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params, /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -2271,7 +2279,8 @@
       loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(),
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params, /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, nullptr /* resource_scheduler_client */,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      nullptr /* resource_scheduler_client */,
       nullptr /* keepalive_statistics_reporter */,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
@@ -2322,7 +2331,8 @@
       loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(),
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params, /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, nullptr /* resource_scheduler_client */,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      nullptr /* resource_scheduler_client */,
       nullptr /* keepalive_statistics_reporter */,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
@@ -2435,7 +2445,8 @@
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -2468,9 +2479,8 @@
       loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request,
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
-      /*coep_reporter=*/nullptr,
-
-      0 /* request_id */, 0 /* keepalive_request_size */,
+      /*coep_reporter=*/nullptr, 0 /* request_id */,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
       resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
@@ -2529,10 +2539,11 @@
         client.CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params,
         /*coep_reporter=*/nullptr, 0 /* request_id */,
-        0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        0 /* keepalive_request_size */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     client.RunUntilRedirectReceived();
@@ -2572,7 +2583,8 @@
       client.CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -2629,7 +2641,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -2676,7 +2689,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -2727,7 +2741,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -2782,7 +2797,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -2846,7 +2862,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -2917,7 +2934,8 @@
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -2954,7 +2972,8 @@
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -3060,10 +3079,11 @@
         client.CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params,
         /*coep_reporter=*/nullptr, 0 /* request_id */,
-        0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        0 /* keepalive_request_size */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     loaders.emplace_back(
@@ -3086,7 +3106,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -3128,7 +3149,8 @@
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -3525,7 +3547,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -3573,7 +3596,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -3621,7 +3645,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -3670,7 +3695,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -3718,7 +3744,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -3763,7 +3790,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -3808,7 +3836,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -3848,7 +3877,8 @@
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -3954,7 +3984,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -4008,7 +4039,8 @@
       loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(),
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params, /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -4053,7 +4085,8 @@
       loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(),
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params, /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -4098,7 +4131,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -4152,7 +4186,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -4211,7 +4246,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -4272,7 +4308,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -4314,7 +4351,8 @@
       mojom::kURLLoadOptionBlockAllCookies, request, client()->CreateRemote(),
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params, /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -4346,7 +4384,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -4378,7 +4417,8 @@
       /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS,
       &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -4434,7 +4474,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -4492,7 +4533,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -4552,7 +4594,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -4589,10 +4632,11 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params, /*coep_reporter=*/nullptr,
         0 /* request_id */, 0 /* keepalive_request_size */,
-        resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */, cookie_observer.GetRemote());
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
+        cookie_observer.GetRemote());
 
     delete_run_loop.Run();
     loader_client.RunUntilComplete();
@@ -4623,10 +4667,11 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params, /*coep_reporter=*/nullptr,
         0 /* request_id */, 0 /* keepalive_request_size */,
-        resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */, cookie_observer.GetRemote());
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
+        cookie_observer.GetRemote());
 
     delete_run_loop.Run();
     loader_client.RunUntilComplete();
@@ -4663,7 +4708,8 @@
       loader_client.CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */, cookie_observer.GetRemote());
@@ -4709,10 +4755,11 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params, /*coep_reporter=*/nullptr,
         0 /* request_id */, 0 /* keepalive_request_size */,
-        resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */, cookie_observer.GetRemote());
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
+        cookie_observer.GetRemote());
 
     loader_client.RunUntilComplete();
     delete_run_loop.Run();
@@ -4756,10 +4803,10 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params, /*coep_reporter=*/nullptr,
         0 /* request_id */, 0 /* keepalive_request_size */,
-        resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     delete_run_loop.Run();
@@ -4810,10 +4857,10 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params, /*coep_reporter=*/nullptr,
         0 /* request_id */, 0 /* keepalive_request_size */,
-        resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     delete_run_loop.Run();
@@ -4856,10 +4903,10 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params, /*coep_reporter=*/nullptr,
         0 /* request_id */, 0 /* keepalive_request_size */,
-        resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     delete_run_loop.Run();
@@ -4906,10 +4953,10 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params, /*coep_reporter=*/nullptr,
         0 /* request_id */, 0 /* keepalive_request_size */,
-        resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     delete_run_loop.Run();
@@ -4955,10 +5002,11 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params,
         /*coep_reporter=*/nullptr, 0 /* request_id */,
-        0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        0 /* keepalive_request_size */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     loader_client.RunUntilRedirectReceived();
@@ -5010,10 +5058,11 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params,
         /*coep_reporter=*/nullptr, 0 /* request_id */,
-        0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        0 /* keepalive_request_size */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     loader_client.RunUntilRedirectReceived();
@@ -5060,10 +5109,10 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params, /*coep_reporter=*/nullptr,
         0 /* request_id */, 0 /* keepalive_request_size */,
-        resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     loader_client.RunUntilComplete();
@@ -5108,10 +5157,10 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params, /*coep_reporter=*/nullptr,
         0 /* request_id */, 0 /* keepalive_request_size */,
-        resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     loader_client.RunUntilComplete();
@@ -5153,10 +5202,10 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params, /*coep_reporter=*/nullptr,
         0 /* request_id */, 0 /* keepalive_request_size */,
-        resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     delete_run_loop.Run();
@@ -5210,10 +5259,11 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params, /*coep_reporter=*/nullptr,
         0 /* request_id */, 0 /* keepalive_request_size */,
-        resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */, cookie_observer.GetRemote());
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
+        cookie_observer.GetRemote());
 
     delete_run_loop.Run();
     loader_client.RunUntilComplete();
@@ -5267,10 +5317,11 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params, /*coep_reporter=*/nullptr,
         0 /* request_id */, 0 /* keepalive_request_size */,
-        resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */, cookie_observer.GetRemote());
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
+        cookie_observer.GetRemote());
 
     delete_run_loop.Run();
     loader_client.RunUntilComplete();
@@ -5311,10 +5362,11 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params, /*coep_reporter=*/nullptr,
         0 /* request_id */, 0 /* keepalive_request_size */,
-        resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */, cookie_observer.GetRemote());
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, nullptr /* origin_policy_manager */,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
+        cookie_observer.GetRemote());
 
     delete_run_loop.Run();
     loader_client.RunUntilComplete();
@@ -5405,10 +5457,11 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params,
         /*coep_reporter=*/nullptr, 0 /* request_id */,
-        0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        &mock_origin_policy_manager, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        0 /* keepalive_request_size */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, &mock_origin_policy_manager,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     loader_client.RunUntilComplete();
@@ -5460,10 +5513,11 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params,
         /*coep_reporter=*/nullptr, 0 /* request_id */,
-        0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        &mock_origin_policy_manager, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        0 /* keepalive_request_size */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, &mock_origin_policy_manager,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     loader_client.RunUntilComplete();
@@ -5502,10 +5556,11 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params,
         /*coep_reporter=*/nullptr, 0 /* request_id */,
-        0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        &mock_origin_policy_manager, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        0 /* keepalive_request_size */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, &mock_origin_policy_manager,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     loader_client.RunUntilResponseBodyArrived();
@@ -5550,10 +5605,11 @@
         /*reponse_body_use_tracker=*/base::nullopt,
         TRAFFIC_ANNOTATION_FOR_TESTS, &params,
         /*coep_reporter=*/nullptr, 0 /* request_id */,
-        0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
-        nullptr /* network_usage_accumulator */, nullptr /* header_client */,
-        &mock_origin_policy_manager, nullptr /* trust_token_helper */,
-        nullptr /* origin_access_list */,
+        0 /* keepalive_request_size */,
+        false /* require_network_isolation_key */, resource_scheduler_client(),
+        nullptr, nullptr /* network_usage_accumulator */,
+        nullptr /* header_client */, &mock_origin_policy_manager,
+        nullptr /* trust_token_helper */, nullptr /* origin_access_list */,
         mojo::NullRemote() /* cookie_observer */);
 
     loader_client.RunUntilComplete();
@@ -5845,7 +5901,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */,
       std::make_unique<MockTrustTokenRequestHelperFactory>(
@@ -5902,7 +5959,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */,
       std::make_unique<MockTrustTokenRequestHelperFactory>(
@@ -5947,7 +6005,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */,
       std::make_unique<MockTrustTokenRequestHelperFactory>(
@@ -5992,7 +6051,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */,
       std::make_unique<MockTrustTokenRequestHelperFactory>(
@@ -6036,7 +6096,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */,
       std::make_unique<MockTrustTokenRequestHelperFactory>(
@@ -6084,7 +6145,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -6133,7 +6195,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
@@ -6174,7 +6237,8 @@
       client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt,
       TRAFFIC_ANNOTATION_FOR_TESTS, &params,
       /*coep_reporter=*/nullptr, 0 /* request_id */,
-      0 /* keepalive_request_size */, resource_scheduler_client(), nullptr,
+      0 /* keepalive_request_size */, false /* require_network_isolation_key */,
+      resource_scheduler_client(), nullptr,
       nullptr /* network_usage_accumulator */, nullptr /* header_client */,
       nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */,
       nullptr /* origin_access_list */,
diff --git a/services/network/websocket.cc b/services/network/websocket.cc
index 525a2c5..66406b29 100644
--- a/services/network/websocket.cc
+++ b/services/network/websocket.cc
@@ -432,10 +432,6 @@
       reassemble_short_messages_(base::FeatureList::IsEnabled(
           network::features::kWebSocketReassembleShortMessages)) {
   DCHECK(handshake_client_);
-  // If |require_network_isolation_key| is set on the URLRequestContext,
-  // |isolation_info| must not be empty.
-  DCHECK(!factory_->GetURLRequestContext()->require_network_isolation_key() ||
-         !isolation_info.IsEmpty());
   // |delay| should be zero if this connection is not throttled.
   DCHECK(pending_connection_tracker.has_value() || delay.is_zero());
   if (auth_handler_) {
diff --git a/services/network/websocket_factory.cc b/services/network/websocket_factory.cc
index 2ac4e3b..6cd881c 100644
--- a/services/network/websocket_factory.cc
+++ b/services/network/websocket_factory.cc
@@ -48,6 +48,11 @@
     return;
   }
 
+  // If |require_network_isolation_key| is set, |isolation_info| must not be
+  // empty.
+  if (context_->require_network_isolation_key())
+    DCHECK(!isolation_info.IsEmpty());
+
   if (throttler_.HasTooManyPendingConnections(process_id)) {
     // Too many websockets!
     mojo::Remote<mojom::WebSocketHandshakeClient> handshake_client_remote(
diff --git a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
index 79899d6..8b4fea1 100644
--- a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
+++ b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
@@ -133,6 +133,8 @@
     return ChromeThreadDescriptor::THREAD_MAIN;
   } else if (base::MatchPattern(thread_name, "Chrome*IOThread")) {
     return ChromeThreadDescriptor::THREAD_IO;
+  } else if (base::MatchPattern(thread_name, "NetworkService")) {
+    return ChromeThreadDescriptor::THREAD_NETWORK_SERVICE;
   } else if (base::MatchPattern(thread_name, "ThreadPoolForegroundWorker*")) {
     return ChromeThreadDescriptor::THREAD_POOL_FG_WORKER;
   } else if (base::MatchPattern(thread_name, "ThreadPoolBackgroundWorker*")) {
diff --git a/services/viz/public/cpp/gpu/context_provider_command_buffer.cc b/services/viz/public/cpp/gpu/context_provider_command_buffer.cc
index 2a37d497..3e02b19 100644
--- a/services/viz/public/cpp/gpu/context_provider_command_buffer.cc
+++ b/services/viz/public/cpp/gpu/context_provider_command_buffer.cc
@@ -377,8 +377,8 @@
 class GrDirectContext* ContextProviderCommandBuffer::GrContext() {
   DCHECK(bind_tried_);
   DCHECK_EQ(bind_result_, gpu::ContextResult::kSuccess);
-  if (!support_grcontext_ || !ContextSupport()->HasGrContextSupport())
-    return nullptr;
+  DCHECK(support_grcontext_);
+  DCHECK(ContextSupport()->HasGrContextSupport());
   CheckValidThreadOrLockAcquired();
 
   if (gr_context_)
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index 7aa778f5b..d84809b 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -67,7 +67,7 @@
             ]
           },
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/"
@@ -287,7 +287,7 @@
             ]
           },
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/"
@@ -1333,7 +1333,7 @@
             ]
           },
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/"
@@ -2379,7 +2379,7 @@
             ]
           },
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/"
@@ -3425,7 +3425,7 @@
             ]
           },
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/"
@@ -4471,7 +4471,7 @@
             ]
           },
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/"
@@ -5389,7 +5389,7 @@
           ],
           "idempotent": false,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
@@ -5541,7 +5541,7 @@
           ],
           "idempotent": false,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
@@ -5693,7 +5693,7 @@
           ],
           "idempotent": false,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
@@ -5845,7 +5845,7 @@
           ],
           "idempotent": false,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
@@ -5997,7 +5997,7 @@
           ],
           "idempotent": false,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
@@ -6149,7 +6149,7 @@
           ],
           "idempotent": false,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
@@ -6233,7 +6233,7 @@
           ],
           "idempotent": false,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
@@ -6317,7 +6317,7 @@
           ],
           "idempotent": false,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
@@ -6401,7 +6401,7 @@
           ],
           "idempotent": false,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
@@ -6485,7 +6485,7 @@
           ],
           "idempotent": false,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 403b3d5..c5ede94 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -240,11 +240,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.148"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.148",
         "resultdb": {
           "enable": true
         },
@@ -254,7 +254,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.147"
+              "revision": "version:87.0.4280.148"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -317,11 +317,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.85"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.85",
         "resultdb": {
           "enable": true
         },
@@ -331,7 +331,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.84"
+              "revision": "version:88.0.4324.85"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -394,11 +394,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.148"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.148",
         "resultdb": {
           "enable": true
         },
@@ -408,7 +408,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.147"
+              "revision": "version:87.0.4280.148"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -471,11 +471,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.85"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.85",
         "resultdb": {
           "enable": true
         },
@@ -485,7 +485,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.84"
+              "revision": "version:88.0.4324.85"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -769,11 +769,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.148"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.148",
         "resultdb": {
           "enable": true
         },
@@ -783,7 +783,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.147"
+              "revision": "version:87.0.4280.148"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -846,11 +846,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.85"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.85",
         "resultdb": {
           "enable": true
         },
@@ -860,7 +860,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.84"
+              "revision": "version:88.0.4324.85"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -923,11 +923,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.148"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.148",
         "resultdb": {
           "enable": true
         },
@@ -937,7 +937,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.147"
+              "revision": "version:87.0.4280.148"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1000,11 +1000,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.85"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.85",
         "resultdb": {
           "enable": true
         },
@@ -1014,7 +1014,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.84"
+              "revision": "version:88.0.4324.85"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1298,11 +1298,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.148"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.148",
         "resultdb": {
           "enable": true
         },
@@ -1312,7 +1312,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.147"
+              "revision": "version:87.0.4280.148"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1375,11 +1375,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.85"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.85",
         "resultdb": {
           "enable": true
         },
@@ -1389,7 +1389,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.84"
+              "revision": "version:88.0.4324.85"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1452,11 +1452,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.148"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.148",
         "resultdb": {
           "enable": true
         },
@@ -1466,7 +1466,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.147"
+              "revision": "version:87.0.4280.148"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1529,11 +1529,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.85"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.85",
         "resultdb": {
           "enable": true
         },
@@ -1543,7 +1543,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.84"
+              "revision": "version:88.0.4324.85"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1827,11 +1827,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.148"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.148",
         "resultdb": {
           "enable": true
         },
@@ -1841,7 +1841,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.147"
+              "revision": "version:87.0.4280.148"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1904,11 +1904,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.85"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.85",
         "resultdb": {
           "enable": true
         },
@@ -1918,7 +1918,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.84"
+              "revision": "version:88.0.4324.85"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1981,11 +1981,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.148"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.148",
         "resultdb": {
           "enable": true
         },
@@ -1995,7 +1995,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.147"
+              "revision": "version:87.0.4280.148"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -2058,11 +2058,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.85"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.85",
         "resultdb": {
           "enable": true
         },
@@ -2072,7 +2072,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.84"
+              "revision": "version:88.0.4324.85"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 422168a..c89a5238 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -232,7 +232,7 @@
             ]
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/"
@@ -1208,7 +1208,7 @@
           ],
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index 1aef999..be65779 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -5654,7 +5654,7 @@
       },
       {
         "args": [
-          "--test-launcher-jobs=1"
+          "--test-launcher-jobs=2"
         ],
         "merge": {
           "args": [],
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 19567905..ef832f8 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -266,6 +266,52 @@
       }
     ]
   },
+  "lacros-eve-perf-fyi": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "-v",
+          "--browser=lacros-chrome",
+          "--upload-results",
+          "--test-shard-map-filename=lacros-eve-perf-fyi_map.json",
+          "--remote=variable_chromeos_device_hostname"
+        ],
+        "isolate_name": "performance_test_suite",
+        "merge": {
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "performance_test_suite",
+        "override_compile_targets": [
+          "performance_test_suite"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "device_type": "eve",
+              "gpu": null,
+              "os": "ChromeOS",
+              "pool": "chrome.tests"
+            }
+          ],
+          "expiration": 7200,
+          "hard_timeout": 21600,
+          "ignore_task_failure": false,
+          "io_timeout": 21600,
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 1
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "requires_simultaneous_shard_dispatch": true,
+          "script": "//testing/trigger_scripts/perf_device_trigger.py"
+        }
+      }
+    ]
+  },
   "linux-perf-fyi": {
     "isolated_scripts": [
       {
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json
index 43cf64b..5fa8a3b 100644
--- a/testing/buildbot/internal.chromeos.fyi.json
+++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -52,7 +52,7 @@
           ],
           "idempotent": false,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/"
@@ -258,7 +258,7 @@
             ]
           },
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/"
@@ -1173,7 +1173,7 @@
           ],
           "idempotent": false,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "chrome_all_tast_tests",
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index e8bbabd..dd06abf 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -523,7 +523,7 @@
       'Mac10.13 Tests (dbg)': {
         # https://crbug.com/1152770
         'args': [
-          '--test-launcher-jobs=1',
+          '--test-launcher-jobs=2',
         ],
         'swarming': {
           'shards': 20,
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 407885f..6052412f 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -458,7 +458,7 @@
       'chrome_all_tast_tests': {
         'swarming': {
           'idempotent': False,  # https://crbug.com/923426#c27
-          'shards': 2,
+          'shards': 4,
         },
         'resultdb': {
           'enable': True,
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 45e49019..d4b12e4 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -319,13 +319,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=88',
     ],
-    'identifier': 'Implementation Tests For 88.0.4324.84',
+    'identifier': 'Implementation Tests For 88.0.4324.85',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M88',
-          'revision': 'version:88.0.4324.84',
+          'revision': 'version:88.0.4324.85',
         }
       ],
     },
@@ -342,13 +342,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=87',
     ],
-    'identifier': 'Implementation Tests For 87.0.4280.147',
+    'identifier': 'Implementation Tests For 87.0.4280.148',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M87',
-          'revision': 'version:87.0.4280.147',
+          'revision': 'version:87.0.4280.148',
         }
       ],
     },
@@ -388,13 +388,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--client-version=88',
     ],
-    'identifier': 'Client Tests For 88.0.4324.84',
+    'identifier': 'Client Tests For 88.0.4324.85',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M88',
-          'revision': 'version:88.0.4324.84',
+          'revision': 'version:88.0.4324.85',
         }
       ],
     },
@@ -411,13 +411,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--client-version=87',
     ],
-    'identifier': 'Client Tests For 87.0.4280.147',
+    'identifier': 'Client Tests For 87.0.4280.148',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M87',
-          'revision': 'version:87.0.4280.147',
+          'revision': 'version:87.0.4280.148',
         }
       ],
     },
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 075f4cb..86badee 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3069,6 +3069,7 @@
   kSamePartyCookieInclusionOverruledSameSite = 3748,
   kEmbedElementWithoutTypeSrcChanged = 3749,
   kPaymentHandlerStandardizedPaymentMethodIdentifier = 3750,
+  kWebCodecsAudioEncoder = 3751,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h
index 6813404..6091eeb 100644
--- a/third_party/blink/public/web/web_ax_object.h
+++ b/third_party/blink/public/web/web_ax_object.h
@@ -81,9 +81,7 @@
   BLINK_EXPORT bool operator>(const WebAXObject& other) const;
   BLINK_EXPORT bool operator>=(const WebAXObject& other) const;
   BLINK_EXPORT static WebAXObject FromWebNode(const WebNode&);
-  BLINK_EXPORT static WebAXObject FromWebDocument(
-      const WebDocument&,
-      bool update_layout_if_necessary = true);
+  BLINK_EXPORT static WebAXObject FromWebDocument(const WebDocument&);
   BLINK_EXPORT static WebAXObject FromWebDocumentByID(const WebDocument&, int);
   BLINK_EXPORT static WebAXObject FromWebDocumentFocused(
       const WebDocument&,
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 058fadc..1402bb1 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -13,6 +13,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_animator_constructor.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_frame_output_callback.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_frame_output_callback.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk_output_callback.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk_output_callback.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_blink_audio_worklet_process_callback.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_blink_audio_worklet_process_callback.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_blink_audio_worklet_processor_constructor.cc",
@@ -107,6 +109,10 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_android_pay_method_data.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_app_banner_prompt_result.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_app_banner_prompt_result.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_encoder_config.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_encoder_config.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_encoder_init.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_encoder_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_buffer_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_buffer_options.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_buffer_source_options.cc",
@@ -1239,6 +1245,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_context.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_decoder.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_decoder.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_encoder.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_encoder.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_destination_node.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_destination_node.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_frame.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index 96f53b886..29ef3b5 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -743,6 +743,9 @@
           "//third_party/blink/renderer/modules/webcodecs/audio_decoder.idl",
           "//third_party/blink/renderer/modules/webcodecs/audio_decoder_config.idl",
           "//third_party/blink/renderer/modules/webcodecs/audio_decoder_init.idl",
+          "//third_party/blink/renderer/modules/webcodecs/audio_encoder.idl",
+          "//third_party/blink/renderer/modules/webcodecs/audio_encoder_config.idl",
+          "//third_party/blink/renderer/modules/webcodecs/audio_encoder_init.idl",
           "//third_party/blink/renderer/modules/webcodecs/audio_frame.idl",
           "//third_party/blink/renderer/modules/webcodecs/audio_frame_init.idl",
           "//third_party/blink/renderer/modules/webcodecs/audio_frame_output_callback.idl",
@@ -756,6 +759,7 @@
           "//third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk_init.idl",
           "//third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.idl",
           "//third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_init.idl",
+          "//third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk_output_callback.idl",
           "//third_party/blink/renderer/modules/webcodecs/image_decoder.idl",
           "//third_party/blink/renderer/modules/webcodecs/image_decoder_init.idl",
           "//third_party/blink/renderer/modules/webcodecs/image_frame.idl",
diff --git a/third_party/blink/renderer/bindings/modules/v8/generated.gni b/third_party/blink/renderer/bindings/modules/v8/generated.gni
index 5600b9d..3a75dbe 100644
--- a/third_party/blink/renderer/bindings/modules/v8/generated.gni
+++ b/third_party/blink/renderer/bindings/modules/v8/generated.gni
@@ -148,6 +148,8 @@
   "$bindings_modules_v8_output_dir/v8_decode_error_callback.h",
   "$bindings_modules_v8_output_dir/v8_decode_success_callback.cc",
   "$bindings_modules_v8_output_dir/v8_decode_success_callback.h",
+  "$bindings_modules_v8_output_dir/v8_encoded_audio_chunk_output_callback.cc",
+  "$bindings_modules_v8_output_dir/v8_encoded_audio_chunk_output_callback.h",
   "$bindings_modules_v8_output_dir/v8_idb_observer_callback.cc",
   "$bindings_modules_v8_output_dir/v8_idb_observer_callback.h",
   "$bindings_modules_v8_output_dir/v8_launch_consumer.cc",
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.cc b/third_party/blink/renderer/core/animation/scroll_timeline.cc
index be41cab..259917a 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline.cc
+++ b/third_party/blink/renderer/core/animation/scroll_timeline.cc
@@ -270,7 +270,6 @@
     }
     resolved_offsets.push_back(resolved_offset.value());
   }
-  // TODO(crbug.com/1094014): Implement clamping for overlapping offsets.
   DCHECK_GE(resolved_offsets.size(), 2u);
   return true;
 }
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.h b/third_party/blink/renderer/core/animation/scroll_timeline.h
index f214ffc..5c6f0ec 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline.h
+++ b/third_party/blink/renderer/core/animation/scroll_timeline.h
@@ -129,6 +129,7 @@
   size_t AttachedAnimationsCount() const { return scroll_animations_.size(); }
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(ScrollTimelineTest, MultipleScrollOffsetsClamping);
   // https://wicg.github.io/scroll-animations/#avoiding-cycles
   // Snapshots scroll timeline current time and phase.
   // Called once per animation frame.
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_test.cc b/third_party/blink/renderer/core/animation/scroll_timeline_test.cc
index 3d4b21cd..46176b7 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline_test.cc
+++ b/third_party/blink/renderer/core/animation/scroll_timeline_test.cc
@@ -878,4 +878,68 @@
   EXPECT_EQ(100, scroll_timeline->CurrentTimeMilliseconds().value());
 }
 
+TEST_F(ScrollTimelineTest, OverlappingScrollOffsets) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #scroller { overflow: scroll; width: 100px; height: 100px; }
+      #spacer { height: 1000px; }
+    </style>
+    <div id='scroller'>
+      <div id ='spacer'></div>
+    </div>
+  )HTML");
+
+  auto* scroller =
+      To<LayoutBoxModelObject>(GetLayoutObjectByElementId("scroller"));
+  ASSERT_TRUE(scroller);
+  PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea();
+  ASSERT_TRUE(scrollable_area);
+  double time_range = 100.0;
+  ScrollTimelineOptions* options = ScrollTimelineOptions::Create();
+  options->setTimeRange(
+      DoubleOrScrollTimelineAutoKeyword::FromDouble(time_range));
+  options->setScrollSource(GetElementById("scroller"));
+  HeapVector<ScrollTimelineOffsetValue> scroll_offsets = {
+      OffsetFromString("90px"), OffsetFromString("40px"),
+      OffsetFromString("10px")};
+  options->setScrollOffsets(scroll_offsets);
+
+  ScrollTimeline* scroll_timeline =
+      ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
+
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 80),
+                                   mojom::blink::ScrollType::kProgrammatic);
+  SimulateFrame();
+  EXPECT_EQ(0, scroll_timeline->CurrentTimeMilliseconds().value());
+
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 95),
+                                   mojom::blink::ScrollType::kProgrammatic);
+  SimulateFrame();
+  EXPECT_EQ(100, scroll_timeline->CurrentTimeMilliseconds().value());
+
+  scroll_offsets = {OffsetFromString("0px"), OffsetFromString("100px"),
+                    OffsetFromString("50px")};
+  options->setScrollOffsets(scroll_offsets);
+
+  scroll_timeline =
+      ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
+
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 40),
+                                   mojom::blink::ScrollType::kProgrammatic);
+  SimulateFrame();
+  EXPECT_EQ(20, scroll_timeline->CurrentTimeMilliseconds().value());
+
+  scroll_offsets = {OffsetFromString("50px"), OffsetFromString("0px"),
+                    OffsetFromString("100px")};
+  options->setScrollOffsets(scroll_offsets);
+
+  scroll_timeline =
+      ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
+
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 60),
+                                   mojom::blink::ScrollType::kProgrammatic);
+  SimulateFrame();
+  EXPECT_EQ(80, scroll_timeline->CurrentTimeMilliseconds().value());
+}
+
 }  //  namespace blink
diff --git a/third_party/blink/renderer/core/css/view-source.css b/third_party/blink/renderer/core/css/view-source.css
index 17b08a6..a54f79a9 100644
--- a/third_party/blink/renderer/core/css/view-source.css
+++ b/third_party/blink/renderer/core/css/view-source.css
@@ -40,7 +40,8 @@
 .line-wrap {
     width: 100%;
     white-space: pre-wrap !important;
-    word-break: break-word;
+    word-break: normal;
+    overflow-wrap: anywhere;
 }
 
 .line-wrap-control {
diff --git a/third_party/blink/renderer/core/editing/frame_selection.cc b/third_party/blink/renderer/core/editing/frame_selection.cc
index f813916f..e8e2867 100644
--- a/third_party/blink/renderer/core/editing/frame_selection.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection.cc
@@ -1293,6 +1293,11 @@
   return layout_selection_->ComputeSelectionStateForCursor(position);
 }
 
+SelectionState FrameSelection::ComputeLayoutSelectionStateForInlineTextBox(
+    const InlineTextBox& text_box) const {
+  return layout_selection_->ComputeSelectionStateForInlineTextBox(text_box);
+}
+
 bool FrameSelection::IsDirectional() const {
   return is_directional_;
 }
diff --git a/third_party/blink/renderer/core/editing/frame_selection.h b/third_party/blink/renderer/core/editing/frame_selection.h
index 5ca1f29..2b302c46 100644
--- a/third_party/blink/renderer/core/editing/frame_selection.h
+++ b/third_party/blink/renderer/core/editing/frame_selection.h
@@ -44,6 +44,7 @@
 
 class CaretDisplayItemClient;
 class Element;
+class InlineTextBox;
 class LayoutBlock;
 class LayoutText;
 class LocalFrame;
@@ -240,6 +241,7 @@
   void PageActivationChanged();
 
   bool IsHandleVisible() const { return is_handle_visible_; }
+  void SetHandleVisibleForTesting() { is_handle_visible_ = true; }
   bool ShouldShrinkNextTap() const { return should_shrink_next_tap_; }
 
   // Returns true if a word is selected.
@@ -288,6 +290,8 @@
       const NGInlineCursor& cursor) const;
   SelectionState ComputeLayoutSelectionStateForCursor(
       const NGInlineCursorPosition& position) const;
+  SelectionState ComputeLayoutSelectionStateForInlineTextBox(
+      const InlineTextBox& text_box) const;
 
   void Trace(Visitor*) const override;
 
diff --git a/third_party/blink/renderer/core/editing/layout_selection.cc b/third_party/blink/renderer/core/editing/layout_selection.cc
index 8785a43..ee69daa 100644
--- a/third_party/blink/renderer/core/editing/layout_selection.cc
+++ b/third_party/blink/renderer/core/editing/layout_selection.cc
@@ -30,9 +30,11 @@
 #include "third_party/blink/renderer/core/editing/visible_position.h"
 #include "third_party/blink/renderer/core/editing/visible_units.h"
 #include "third_party/blink/renderer/core/html/forms/text_control_element.h"
+#include "third_party/blink/renderer/core/layout/api/line_layout_api_shim.h"
 #include "third_party/blink/renderer/core/layout/layout_text.h"
 #include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
@@ -708,40 +710,34 @@
   }
 }
 
-SelectionState LayoutSelection::ComputeSelectionStateForCursor(
-    const NGInlineCursorPosition& position) const {
-  if (!position)
-    return SelectionState::kNone;
-
-  DCHECK(position.IsText());
-
-  // Selection on ellipsis is not supported.
-  if (position.IsEllipsis())
-    return SelectionState::kNone;
-
-  const NGTextOffset offset = position.TextOffset();
-  const unsigned start_offset = offset.start;
-  const unsigned end_offset = offset.end;
-  // Determine the state of the overall selection, relative to the LayoutObject
-  // associated with the current cursor position. This state will allow us know
-  // which offset comparisons are valid, and determine if the selection
-  // endpoints fall within the current cursor position.
-  switch (GetSelectionStateFor(position)) {
+// Given |state| that describes the provided offsets relationship to the
+// |paint_range_| (and thus which comparisons are valid), returns a
+// SelectionState that reflects where the endpoints of the selection fall,
+// relative to the range expressed by the offsets.
+SelectionState LayoutSelection::ComputeSelectionStateFromOffsets(
+    SelectionState state,
+    unsigned start_offset,
+    unsigned end_offset) const {
+  switch (state) {
     case SelectionState::kStart: {
-      const unsigned start_in_block = paint_range_->start_offset.value();
+      const unsigned start_in_block =
+          paint_range_->start_offset.value_or(start_offset);
       return start_offset <= start_in_block && start_in_block <= end_offset
                  ? SelectionState::kStart
                  : SelectionState::kNone;
     }
     case SelectionState::kEnd: {
-      const unsigned end_in_block = paint_range_->end_offset.value();
+      const unsigned end_in_block =
+          paint_range_->end_offset.value_or(end_offset);
       return start_offset <= end_in_block && end_in_block <= end_offset
                  ? SelectionState::kEnd
                  : SelectionState::kNone;
     }
     case SelectionState::kStartAndEnd: {
-      const unsigned start_in_block = paint_range_->start_offset.value();
-      const unsigned end_in_block = paint_range_->end_offset.value();
+      const unsigned start_in_block =
+          paint_range_->start_offset.value_or(start_offset);
+      const unsigned end_in_block =
+          paint_range_->end_offset.value_or(end_offset);
       const bool is_start_in_current_cursor =
           start_offset <= start_in_block && start_in_block <= end_offset;
       const bool is_end_in_current_cursor =
@@ -763,6 +759,42 @@
   }
 }
 
+SelectionState LayoutSelection::ComputeSelectionStateForCursor(
+    const NGInlineCursorPosition& position) const {
+  if (!position)
+    return SelectionState::kNone;
+
+  DCHECK(position.IsText());
+
+  // Selection on ellipsis is not supported.
+  if (position.IsEllipsis())
+    return SelectionState::kNone;
+
+  const NGTextOffset offset = position.TextOffset();
+  const unsigned start_offset = offset.start;
+  const unsigned end_offset = offset.end;
+  // Determine the state of the overall selection, relative to the LayoutObject
+  // associated with the current cursor position. This state will allow us know
+  // which offset comparisons are valid, and determine if the selection
+  // endpoints fall within the current cursor position.
+  SelectionState state = GetSelectionStateFor(position);
+  return ComputeSelectionStateFromOffsets(state, start_offset, end_offset);
+}
+
+SelectionState LayoutSelection::ComputeSelectionStateForInlineTextBox(
+    const InlineTextBox& text_box) const {
+  AssertIsValid();
+  unsigned start_offset = static_cast<unsigned>(text_box.CaretMinOffset());
+  unsigned end_offset = static_cast<unsigned>(text_box.CaretMaxOffset());
+  // Determine the state of the overall selection, relative to the
+  // InlineTextBox. This state will allow us know which offset comparisons are
+  // valid, and determine if the selection endpoints fall within InlineTextBox.
+  const LayoutText* text = To<LayoutText>(
+      LineLayoutAPIShim::ConstLayoutObjectFrom(text_box.GetLineLayoutItem()));
+  SelectionState state = GetSelectionStateFor(*text);
+  return ComputeSelectionStateFromOffsets(state, start_offset, end_offset);
+}
+
 static NewPaintRangeAndSelectedNodes CalcSelectionRangeAndSetSelectionState(
     const FrameSelection& frame_selection) {
   const SelectionInDOMTree& selection_in_dom =
diff --git a/third_party/blink/renderer/core/editing/layout_selection.h b/third_party/blink/renderer/core/editing/layout_selection.h
index ee941a0..a29df9a 100644
--- a/third_party/blink/renderer/core/editing/layout_selection.h
+++ b/third_party/blink/renderer/core/editing/layout_selection.h
@@ -29,6 +29,7 @@
 
 namespace blink {
 
+class InlineTextBox;
 class IntRect;
 class LayoutObject;
 class LayoutText;
@@ -61,6 +62,14 @@
   SelectionState ComputeSelectionStateForCursor(
       const NGInlineCursorPosition&) const;
 
+  // Compute the layout selection state relative to the InlineTextBox.
+  // E.g. a state of kStart means that the selection starts within the line
+  // (and ends elsewhere), where kStartAndEnd means the selection both starts
+  // and ends within the line. This information is used at paint time to
+  // determine the edges of the layout selection.
+  SelectionState ComputeSelectionStateForInlineTextBox(
+      const InlineTextBox&) const;
+
   static bool IsSelected(const LayoutObject&);
 
   void ContextDestroyed();
@@ -68,6 +77,10 @@
   void Trace(Visitor*) const;
 
  private:
+  SelectionState ComputeSelectionStateFromOffsets(SelectionState state,
+                                                  unsigned start_offset,
+                                                  unsigned end_offset) const;
+
   void AssertIsValid() const;
 
   Member<FrameSelection> frame_selection_;
diff --git a/third_party/blink/renderer/core/editing/layout_selection_test.cc b/third_party/blink/renderer/core/editing/layout_selection_test.cc
index 6a044580..5acd68b 100644
--- a/third_party/blink/renderer/core/editing/layout_selection_test.cc
+++ b/third_party/blink/renderer/core/editing/layout_selection_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/layout_text.h"
 #include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
+#include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
@@ -943,6 +944,69 @@
       DumpSelectionInfo());
 }
 
+TEST_P(LayoutSelectionTest, StartAndEndSelectionState) {
+  if (LayoutNGEnabled())
+    return;
+
+  Selection().SetSelectionAndEndTyping(
+      SetSelectionTextToBody("<div>f^oo|</div><div>bar</div>"));
+  UpdateAllLifecyclePhasesForTest();
+  Node* foo_div = GetDocument().body()->firstChild();
+  auto& foo_text = *To<LayoutText>(foo_div->firstChild()->GetLayoutObject());
+  InlineTextBox* text_box = foo_text.FirstTextBox();
+
+  EXPECT_EQ(SelectionState::kStartAndEnd,
+            Selection().ComputeLayoutSelectionStateForInlineTextBox(*text_box));
+
+  Node* bar_div = GetDocument().body()->lastChild();
+  auto& bar_text = *To<LayoutText>(bar_div->firstChild()->GetLayoutObject());
+  text_box = bar_text.FirstTextBox();
+  EXPECT_EQ(SelectionState::kNone,
+            Selection().ComputeLayoutSelectionStateForInlineTextBox(*text_box));
+}
+
+TEST_P(LayoutSelectionTest, StartAndEndMultilineSelectionState) {
+  if (LayoutNGEnabled())
+    return;
+
+  Selection().SetSelectionAndEndTyping(SetSelectionTextToBody(
+      "<div style='white-space:pre'>f^oo\nbar\nba|z</div>"));
+  UpdateAllLifecyclePhasesForTest();
+  auto& div_text = *To<LayoutText>(
+      GetDocument().body()->firstChild()->firstChild()->GetLayoutObject());
+  for (const InlineTextBox* box : div_text.TextBoxes()) {
+    SelectionState state =
+        Selection().ComputeLayoutSelectionStateForInlineTextBox(*box);
+    if (box == div_text.FirstTextBox())
+      EXPECT_EQ(SelectionState::kStart, state);
+    else if (box == div_text.LastTextBox())
+      EXPECT_EQ(SelectionState::kEnd, state);
+    else
+      EXPECT_EQ(SelectionState::kInside, state);
+  }
+}
+
+TEST_P(LayoutSelectionTest, StartAndEndBR) {
+  if (LayoutNGEnabled())
+    return;
+
+  Selection().SetSelectionAndEndTyping(SetSelectionTextToBody(
+      "<div style='white-space:pre'>^<br>foo<br>|</div>"));
+  UpdateAllLifecyclePhasesForTest();
+  auto& first_br_text = *To<LayoutText>(
+      GetDocument().body()->firstChild()->firstChild()->GetLayoutObject());
+  const InlineTextBox* box = first_br_text.FirstTextBox();
+  SelectionState state =
+      Selection().ComputeLayoutSelectionStateForInlineTextBox(*box);
+  EXPECT_EQ(SelectionState::kStart, state);
+  auto& last_br_text = *To<LayoutText>(
+      GetDocument().body()->firstChild()->lastChild()->GetLayoutObject());
+
+  box = last_br_text.FirstTextBox();
+  state = Selection().ComputeLayoutSelectionStateForInlineTextBox(*box);
+  EXPECT_EQ(SelectionState::kEnd, state);
+}
+
 class NGLayoutSelectionTest
     : public LayoutSelectionTestBase,
       private ScopedLayoutNGForTest,
diff --git a/third_party/blink/renderer/core/html/html_view_source_document.cc b/third_party/blink/renderer/core/html/html_view_source_document.cc
index d5e4af46..0b4d0a6 100644
--- a/third_party/blink/renderer/core/html/html_view_source_document.cc
+++ b/third_party/blink/renderer/core/html/html_view_source_document.cc
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
 #include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/events/mouse_event.h"
+#include "third_party/blink/renderer/core/html/forms/html_form_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_label_element.h"
 #include "third_party/blink/renderer/core/html/html_anchor_element.h"
@@ -57,8 +58,7 @@
       : table_(table), checkbox_(checkbox) {}
 
   void Invoke(ExecutionContext*, Event* event) override {
-    DCHECK(DynamicTo<MouseEvent>(event));
-    DCHECK_EQ(event->type(), event_type_names::kClick);
+    DCHECK_EQ(event->type(), event_type_names::kChange);
     table_->setAttribute(html_names::kClassAttr,
                          checkbox_->checked() ? "line-wrap" : "");
   }
@@ -108,28 +108,32 @@
   line_number_ = 0;
 
   // Create a checkbox to control line wrapping.
-  auto* label = MakeGarbageCollected<HTMLLabelElement>(*this);
-  label->setAttribute(html_names::kClassAttr, "line-wrap-control");
   auto* checkbox =
       MakeGarbageCollected<HTMLInputElement>(*this, CreateElementFlags());
   checkbox->setAttribute(html_names::kTypeAttr, "checkbox");
-  label->ParserAppendChild(checkbox);
+  checkbox->addEventListener(
+      event_type_names::kChange,
+      MakeGarbageCollected<ViewSourceEventListener>(table, checkbox),
+      /*use_capture=*/false);
+  auto* label = MakeGarbageCollected<HTMLLabelElement>(*this);
   label->ParserAppendChild(
       Text::Create(*this, WTF::AtomicString(Locale::DefaultLocale().QueryString(
                               IDS_VIEW_SOURCE_LINE_WRAP))));
+  label->setAttribute(html_names::kClassAttr, "line-wrap-control");
+  label->ParserAppendChild(checkbox);
+  // Add the checkbox to a form with autocomplete=off, to avoid form
+  // restoration from changing the value of the checkbox.
+  auto* form = MakeGarbageCollected<HTMLFormElement>(*this);
+  form->setAttribute(html_names::kAutocompleteAttr, "off");
+  form->ParserAppendChild(label);
   auto* tr = MakeGarbageCollected<HTMLTableRowElement>(*this);
   auto* td =
       MakeGarbageCollected<HTMLTableCellElement>(html_names::kTdTag, *this);
   td->setAttribute(html_names::kColspanAttr, "2");
   td->setAttribute(html_names::kClassAttr, "line-wrap-cell");
-  td->ParserAppendChild(label);
+  td->ParserAppendChild(form);
   tr->ParserAppendChild(td);
   tbody_->ParserAppendChild(tr);
-
-  auto* listener =
-      MakeGarbageCollected<ViewSourceEventListener>(table, checkbox);
-  checkbox->addEventListener(event_type_names::kClick, listener,
-                             /*use_capture=*/false);
 }
 
 void HTMLViewSourceDocument::AddSource(const String& source, HTMLToken& token) {
diff --git a/third_party/blink/renderer/core/html/html_view_source_document_test.cc b/third_party/blink/renderer/core/html/html_view_source_document_test.cc
index 8c8665b..5a507e73 100644
--- a/third_party/blink/renderer/core/html/html_view_source_document_test.cc
+++ b/third_party/blink/renderer/core/html/html_view_source_document_test.cc
@@ -39,8 +39,10 @@
       GetDocument().documentElement()->outerHTML(),
       "<html><head></head><body><div "
       "class=\"line-gutter-backdrop\"></div><table><tbody><tr><td "
-      "colspan=\"2\" class=\"line-wrap-cell\"><label "
-      "class=\"line-wrap-control\"><input type=\"checkbox\"></label>"
+      "colspan=\"2\" class=\"line-wrap-cell\"><form "
+      "autocomplete=\"off\"><label "
+      "class=\"line-wrap-control\"><input "
+      "type=\"checkbox\"></label></form>"
       "</td></tr><tr><td class=\"line-number\" value=\"1\"></td><td "
       "class=\"line-content\"><br></td></tr><tr><td class=\"line-number\" "
       "value=\"2\"></td><td class=\"line-content\">      <span "
@@ -94,8 +96,10 @@
       GetDocument().documentElement()->outerHTML(),
       "<html><head></head><body><div "
       "class=\"line-gutter-backdrop\"></div><table><tbody><tr><td "
-      "colspan=\"2\" class=\"line-wrap-cell\"><label "
-      "class=\"line-wrap-control\"><input type=\"checkbox\"></label>"
+      "colspan=\"2\" class=\"line-wrap-cell\"><form "
+      "autocomplete=\"off\"><label "
+      "class=\"line-wrap-control\"><input "
+      "type=\"checkbox\"></label></form>"
       "</td></tr><tr><td class=\"line-number\" value=\"1\"></td><td "
       "class=\"line-content\"><br></td></tr><tr><td class=\"line-number\" "
       "value=\"2\"></td><td class=\"line-content\">      <span "
@@ -148,8 +152,10 @@
       GetDocument().documentElement()->outerHTML(),
       "<html><head></head><body><div "
       "class=\"line-gutter-backdrop\"></div><table><tbody><tr><td "
-      "colspan=\"2\" class=\"line-wrap-cell\"><label "
-      "class=\"line-wrap-control\"><input type=\"checkbox\"></label>"
+      "colspan=\"2\" class=\"line-wrap-cell\"><form "
+      "autocomplete=\"off\"><label "
+      "class=\"line-wrap-control\"><input "
+      "type=\"checkbox\"></label></form>"
       "</td></tr><tr><td class=\"line-number\" value=\"1\"></td><td "
       "class=\"line-content\"><br></td></tr><tr><td class=\"line-number\" "
       "value=\"2\"></td><td class=\"line-content\">      <span "
@@ -215,8 +221,10 @@
       GetDocument().documentElement()->outerHTML(),
       "<html><head></head><body><div "
       "class=\"line-gutter-backdrop\"></div><table><tbody><tr><td "
-      "colspan=\"2\" class=\"line-wrap-cell\"><label "
-      "class=\"line-wrap-control\"><input type=\"checkbox\"></label>"
+      "colspan=\"2\" class=\"line-wrap-cell\"><form "
+      "autocomplete=\"off\"><label "
+      "class=\"line-wrap-control\"><input "
+      "type=\"checkbox\"></label></form>"
       "</td></tr><tr><td class=\"line-number\" value=\"1\"></td><td "
       "class=\"line-content\"><br></td></tr><tr><td class=\"line-number\" "
       "value=\"2\"></td><td class=\"line-content\">      <span "
@@ -285,8 +293,10 @@
       GetDocument().documentElement()->outerHTML(),
       "<html><head></head><body><div "
       "class=\"line-gutter-backdrop\"></div><table><tbody><tr><td "
-      "colspan=\"2\" class=\"line-wrap-cell\"><label "
-      "class=\"line-wrap-control\"><input type=\"checkbox\"></label>"
+      "colspan=\"2\" class=\"line-wrap-cell\"><form "
+      "autocomplete=\"off\"><label "
+      "class=\"line-wrap-control\"><input "
+      "type=\"checkbox\"></label></form>"
       "</td></tr><tr><td class=\"line-number\" value=\"1\"></td><td "
       "class=\"line-content\"><br></td></tr><tr><td class=\"line-number\" "
       "value=\"2\"></td><td class=\"line-content\"><br></td></tr><tr><td "
@@ -319,8 +329,10 @@
   std::string expected_beginning(
       "<html><head></head><body><div "
       "class=\"line-gutter-backdrop\"></div><table><tbody><tr><td "
-      "colspan=\"2\" class=\"line-wrap-cell\"><label "
-      "class=\"line-wrap-control\"><input type=\"checkbox\"></label>"
+      "colspan=\"2\" class=\"line-wrap-cell\"><form "
+      "autocomplete=\"off\"><label "
+      "class=\"line-wrap-control\"><input "
+      "type=\"checkbox\"></label></form>"
       "</td></tr><tr><td class=\"line-number\" value=\"1\">"
       "</td><td class=\"line-content\">      ");
   std::string expected_ending(
@@ -337,8 +349,10 @@
   EXPECT_EQ(GetDocument().documentElement()->outerHTML(),
             "<html><head></head><body><div "
             "class=\"line-gutter-backdrop\"></div><table><tbody><tr><td "
-            "colspan=\"2\" class=\"line-wrap-cell\"><label "
-            "class=\"line-wrap-control\"><input type=\"checkbox\"></label>"
+            "colspan=\"2\" class=\"line-wrap-cell\"><form "
+            "autocomplete=\"off\"><label "
+            "class=\"line-wrap-control\"><input "
+            "type=\"checkbox\"></label></form>"
             "</td></tr><tr><td class=\"line-number\" value=\"1\"></td><td "
             "class=\"line-content\">1234567<span "
             "class=\"html-end-of-file\"></span></td></tr></tbody></table></"
@@ -361,8 +375,10 @@
       GetDocument().documentElement()->outerHTML(),
       "<html><head></head><body><div "
       "class=\"line-gutter-backdrop\"></div><table><tbody><tr><td "
-      "colspan=\"2\" class=\"line-wrap-cell\"><label "
-      "class=\"line-wrap-control\"><input type=\"checkbox\"></label></td></tr>"
+      "colspan=\"2\" class=\"line-wrap-cell\"><form "
+      "autocomplete=\"off\"><label "
+      "class=\"line-wrap-control\"><input "
+      "type=\"checkbox\"></label></form></td></tr>"
       "<tr><td class=\"line-number\" value=\"1\"></td><td "
       "class=\"line-content\"><br></td></tr><tr><td class=\"line-number\" "
       "value=\"2\"></td><td class=\"line-content\">      <span "
@@ -430,8 +446,10 @@
       GetDocument().documentElement()->outerHTML(),
       "<html><head></head><body><div "
       "class=\"line-gutter-backdrop\"></div><table><tbody><tr><td "
-      "colspan=\"2\" class=\"line-wrap-cell\"><label "
-      "class=\"line-wrap-control\"><input type=\"checkbox\"></label>"
+      "colspan=\"2\" class=\"line-wrap-cell\"><form "
+      "autocomplete=\"off\"><label "
+      "class=\"line-wrap-control\"><input "
+      "type=\"checkbox\"></label></form>"
       "</td></tr><tr><td class=\"line-number\" value=\"1\"></td><td "
       "class=\"line-content\"><br></td></tr><tr><td class=\"line-number\" "
       "value=\"2\"></td><td class=\"line-content\">      <span "
@@ -463,9 +481,11 @@
       GetDocument().documentElement()->outerHTML(),
       "<html><head></head><body><div "
       "class=\"line-gutter-backdrop\"></div><table><tbody><tr><td "
-      "colspan=\"2\" class=\"line-wrap-cell\"><label "
-      "class=\"line-wrap-control\"><input type=\"checkbox\">"
-      "</label></td></tr><tr><td class=\"line-number\" value=\"1\"></td><td "
+      "colspan=\"2\" class=\"line-wrap-cell\"><form "
+      "autocomplete=\"off\"><label "
+      "class=\"line-wrap-control\"><input "
+      "type=\"checkbox\"></label></form>"
+      "</td></tr><tr><td class=\"line-number\" value=\"1\"></td><td "
       "class=\"line-content\"><br></td></tr><tr><td class=\"line-number\" "
       "value=\"2\"></td><td class=\"line-content\">      Incomplete token "
       "test</td></tr><tr><td class=\"line-number\" value=\"3\"></td><td "
@@ -487,8 +507,10 @@
       GetDocument().documentElement()->outerHTML(),
       "<html><head></head><body><div "
       "class=\"line-gutter-backdrop\"></div><table><tbody><tr><td "
-      "colspan=\"2\" class=\"line-wrap-cell\"><label "
-      "class=\"line-wrap-control\"><input type=\"checkbox\"></label>"
+      "colspan=\"2\" class=\"line-wrap-cell\"><form "
+      "autocomplete=\"off\"><label "
+      "class=\"line-wrap-control\"><input "
+      "type=\"checkbox\"></label></form>"
       "</td></tr><tr><td class=\"line-number\" value=\"1\"></td>"
       "<td class=\"line-content\"><span "
       "class=\"html-tag\">&lt;textarea&gt;</span>foobar in "
@@ -505,8 +527,10 @@
       GetDocument().documentElement()->outerHTML(),
       "<html><head></head><body><div "
       "class=\"line-gutter-backdrop\"></div><table><tbody><tr><td "
-      "colspan=\"2\" class=\"line-wrap-cell\"><label "
-      "class=\"line-wrap-control\"><input type=\"checkbox\"></label>"
+      "colspan=\"2\" class=\"line-wrap-cell\"><form "
+      "autocomplete=\"off\"><label "
+      "class=\"line-wrap-control\"><input "
+      "type=\"checkbox\"></label></form>"
       "</td></tr><tr><td class=\"line-number\" value=\"1\"></td>"
       "<td class=\"line-content\"><span "
       "class=\"html-tag\">&lt;script&gt;</span>foobar in "
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.h b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
index cf5d13b..302adc9 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
@@ -283,10 +283,10 @@
   }
   static blink::NGBlockNode EmptyValue() { return nullptr; }
   static void ConstructDeletedValue(blink::NGBlockNode& slot, bool) {
-    slot = nullptr;
+    slot = blink::NGBlockNode(reinterpret_cast<blink::LayoutBox*>(-1));
   }
   static bool IsDeletedValue(const blink::NGBlockNode& value) {
-    return IsEmptyValue(value);
+    return value.GetLayoutBox() == reinterpret_cast<blink::LayoutBox*>(-1);
   }
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
index 6898faa5..3a4a398 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
@@ -304,8 +304,6 @@
     // out at the outermost context. If this multicol has OOF positioned
     // elements pending layout, store its node for later use.
     if (container_builder_.HasOutOfFlowFragmentainerDescendants()) {
-      // TODO(almaher): Run layout on the pending OOFs once we hit the
-      // outermost fragmentation context.
       container_builder_.AddMulticolWithPendingOOFs(Node());
     }
   }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
index 5fcea1d..981d0a115 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -312,9 +312,6 @@
   }
 
   const WritingModeConverter converter(GetWritingDirection(), fragment.Size());
-  const WritingModeConverter empty_outer_size(GetWritingDirection(),
-                                              PhysicalSize());
-
   const auto& out_of_flow_fragmentainer_descendants =
       box_fragment->OutOfFlowPositionedFragmentainerDescendants();
   for (const auto& descendant : out_of_flow_fragmentainer_descendants) {
@@ -323,8 +320,8 @@
     if (!containing_block_fragment)
       containing_block_fragment = box_fragment;
 
-    LogicalOffset containing_block_offset =
-        converter.ToLogical(descendant.containing_block_offset, PhysicalSize());
+    LogicalOffset containing_block_offset = converter.ToLogical(
+        descendant.containing_block_offset, containing_block_fragment->Size());
     if (!fragment.IsFragmentainerBox())
       containing_block_offset.block_offset += offset.block_offset;
     if (IsBlockFragmentationContextRoot()) {
@@ -332,8 +329,12 @@
           fragmentainer_consumed_block_size_;
     }
 
+    // The static position should remain relative to its containing block
+    // fragment.
+    const WritingModeConverter containing_block_converter(
+        GetWritingDirection(), containing_block_fragment->Size());
     NGLogicalStaticPosition static_position =
-        descendant.static_position.ConvertToLogical(empty_outer_size);
+        descendant.static_position.ConvertToLogical(containing_block_converter);
     AddOutOfFlowFragmentainerDescendant(
         {descendant.node, static_position, descendant.inline_container,
          /* needs_block_offset_adjustment */ false, containing_block_offset,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index 663db7d..f9e6582 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -135,14 +135,23 @@
 
 void NGOutOfFlowLayoutPart::Run(const LayoutBox* only_layout) {
   if (container_builder_->IsBlockFragmentationContextRoot() &&
-      !has_block_fragmentation_ &&
-      container_builder_->HasOutOfFlowFragmentainerDescendants()) {
-    Vector<NGLogicalOutOfFlowPositionedNode> fragmentainer_descendants;
-    container_builder_->SwapOutOfFlowFragmentainerDescendants(
-        &fragmentainer_descendants);
-
-    if (!fragmentainer_descendants.IsEmpty())
+      !has_block_fragmentation_) {
+    if (container_builder_->HasOutOfFlowFragmentainerDescendants()) {
+      Vector<NGLogicalOutOfFlowPositionedNode> fragmentainer_descendants;
+      container_builder_->SwapOutOfFlowFragmentainerDescendants(
+          &fragmentainer_descendants);
+      DCHECK(!fragmentainer_descendants.IsEmpty());
       LayoutFragmentainerDescendants(&fragmentainer_descendants);
+    }
+
+    if (container_builder_->HasMulticolsWithPendingOOFs()) {
+      HashSet<NGBlockNode> multicols_with_pending_oofs;
+      container_builder_->SwapMulticolsWithPendingOOFs(
+          &multicols_with_pending_oofs);
+      DCHECK(!multicols_with_pending_oofs.IsEmpty());
+      for (const NGBlockNode& multicol : multicols_with_pending_oofs)
+        LayoutOOFsInMulticol(multicol);
+    }
   }
 
   const LayoutObject* current_container = container_builder_->GetLayoutObject();
@@ -630,6 +639,60 @@
   } while (true);
 }
 
+// TODO(almaher): Look into moving this to NGColumnLayoutAlgorithm instead.
+void NGOutOfFlowLayoutPart::LayoutOOFsInMulticol(const NGBlockNode& multicol) {
+  Vector<NGLogicalOutOfFlowPositionedNode> oof_nodes_to_layout;
+
+  // Accumulate all of the pending OOF positioned nodes that are stored inside
+  // |multicol|.
+  for (auto& multicol_fragment : multicol.GetLayoutBox()->PhysicalFragments()) {
+    const NGPhysicalBoxFragment* multicol_box_fragment =
+        To<NGPhysicalBoxFragment>(&multicol_fragment);
+    if (!multicol_box_fragment
+             ->HasOutOfFlowPositionedFragmentainerDescendants())
+      continue;
+
+    WritingDirectionMode writing_direction =
+        multicol_box_fragment->Style().GetWritingDirection();
+    const WritingModeConverter converter(writing_direction,
+                                         multicol_box_fragment->Size());
+
+    // Convert the OOF fragmentainer descendants to the logical coordinate space
+    // and store the resulting nodes inside |oof_nodes_to_layout|.
+    for (const auto& descendant :
+         multicol_box_fragment->OutOfFlowPositionedFragmentainerDescendants()) {
+      const NGPhysicalContainerFragment* containing_block_fragment =
+          descendant.containing_block_fragment.get();
+      LogicalOffset containing_block_offset =
+          converter.ToLogical(descendant.containing_block_offset,
+                              containing_block_fragment->Size());
+
+      // The static position should remain relative to its containing block
+      // fragment.
+      const WritingModeConverter containing_block_converter(
+          writing_direction, containing_block_fragment->Size());
+      NGLogicalStaticPosition static_position =
+          descendant.static_position.ConvertToLogical(
+              containing_block_converter);
+
+      NGLogicalOutOfFlowPositionedNode node = {
+          descendant.node,
+          static_position,
+          descendant.inline_container,
+          /* needs_block_offset_adjustment */ false,
+          containing_block_offset,
+          containing_block_fragment};
+      oof_nodes_to_layout.push_back(node);
+    }
+  }
+  DCHECK(!oof_nodes_to_layout.IsEmpty());
+
+  // TODO(almaher): This lays out the OOF nodes in the outer fragmentation
+  // context. We want to lay these out inside the column children of |multicol|
+  // instead.
+  LayoutFragmentainerDescendants(&oof_nodes_to_layout);
+}
+
 void NGOutOfFlowLayoutPart::LayoutFragmentainerDescendants(
     Vector<NGLogicalOutOfFlowPositionedNode>* descendants) {
   original_column_block_size_ =
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
index 5b48d0d..357634b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
@@ -102,6 +102,8 @@
       const NGLogicalOutOfFlowPositionedNode&,
       const LayoutBox* only_layout);
 
+  void LayoutOOFsInMulticol(const NGBlockNode& multicol);
+
   void LayoutFragmentainerDescendants(
       Vector<NGLogicalOutOfFlowPositionedNode>* descendants);
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
index 74756ba..2b31d23f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
@@ -1305,9 +1305,7 @@
 }
 
 // Fragmented OOF element inside a nested multi-column.
-// TODO(almaher): Re-enable once layout is run on the pending OOFs of inner
-// multicols inside a nested fragmentation context.
-TEST_F(NGOutOfFlowLayoutPartTest, DISABLED_AbsposNestedFragmentation) {
+TEST_F(NGOutOfFlowLayoutPartTest, AbsposNestedFragmentation) {
   SetBodyInnerHTML(
       R"HTML(
       <style>
@@ -1335,9 +1333,10 @@
       )HTML");
   String dump = DumpFragmentTree(GetElementById("container"));
 
-  // TODO(almaher): There should be two abspos fragments with height 60 in the
-  // first outer column, and two with height 100/30 in the second outer column.
-  // There should not be a third outer column.
+  // TODO(almaher): The abspos element should be placed in the inner multicol
+  // rather than the outer multicol. The offset is also incorrectly computed due
+  // to the fact that the containing block offset is now relative to the inner
+  // multicol rather than the outer.
   String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
   offset:unplaced size:1000x100
     offset:0,0 size:1000x100
@@ -1350,7 +1349,7 @@
           offset:250,0 size:250x60
             offset:0,0 size:55x60
               offset:0,0 size:25x60
-        offset:0,40 size:5x60
+        offset:0,0 size:5x100
       offset:500,0 size:500x100
         offset:0,0 size:500x100
           offset:0,0 size:250x100
@@ -1361,7 +1360,62 @@
               offset:0,0 size:25x30
         offset:0,0 size:5x100
       offset:1000,0 size:500x100
-        offset:0,0 size:5x90
+        offset:0,0 size:5x50
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+// Test the static position of a fragmented OOF element inside a nested
+// multi-column.
+TEST_F(NGOutOfFlowLayoutPartTest, AbsposNestedFragmentationStaticPos) {
+  SetBodyInnerHTML(
+      R"HTML(
+      <style>
+        .multicol {
+          columns:2; column-fill:auto; column-gap:0px;
+        }
+        .rel {
+          position: relative; width:55px;
+        }
+        .abs {
+          position:absolute; width:5px; height:70px;
+        }
+      </style>
+      <div id="container">
+        <div class="multicol" id="outer" style="height:100px;">
+          <div class="multicol" id="inner">
+            <div class="rel">
+              <div style="height:250px; width:25px;"></div>
+              <div class="abs"></div>
+            </div>
+          </div>
+        </div>
+      </div>
+      )HTML");
+  String dump = DumpFragmentTree(GetElementById("container"));
+
+  // TODO(almaher): The abspos element should be placed in the inner multicol
+  // rather than the outer multicol. The static offset is also incorrectly
+  // computed due to the fact that the containing block offset is now relative
+  // to the inner multicol rather than the outer.
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:1000x100
+      offset:0,0 size:500x100
+        offset:0,0 size:500x100
+          offset:0,0 size:250x100
+            offset:0,0 size:55x100
+              offset:0,0 size:25x100
+          offset:250,0 size:250x100
+            offset:0,0 size:55x100
+              offset:0,0 size:25x100
+        offset:0,50 size:5x50
+      offset:500,0 size:500x100
+        offset:0,0 size:500x100
+          offset:0,0 size:250x100
+            offset:0,0 size:55x50
+              offset:0,0 size:25x50
+        offset:0,0 size:5x20
 )DUMP";
   EXPECT_EQ(expectation, dump);
 }
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
index 2b5982e2..445cb7f 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -31,14 +31,6 @@
 
 namespace {
 
-// TODO(atotic, ikilpatrick)
-// Copy of ShouldScaleColumnsForParent from table_layout_algorithm_auto.cc
-// The real fix would be for containers to understand that
-// Table max really means: max should be trimmed to available inline size.
-bool ShouldIgnorePercentagesForMinMax(const LayoutBox& table) {
-  return false;
-}
-
 NGTableTypes::Caption ComputeCaptionConstraint(
     const ComputedStyle& table_style,
     const NGTableGroupedChildren& grouped_children) {
@@ -99,7 +91,7 @@
 
 // standard: https://www.w3.org/TR/css-tables-3/#computing-the-table-width
 LayoutUnit ComputeAssignableTableInlineSize(
-    const NGBlockNode& table,
+    const NGTableNode& table,
     const NGConstraintSpace& space,
     const NGTableTypes::Columns& column_constraints,
     const NGTableTypes::Caption& caption_constraint,
@@ -113,8 +105,8 @@
 
   const MinMaxSizes grid_min_max =
       NGTableAlgorithmHelpers::ComputeGridInlineMinMax(
-          column_constraints, undistributable_space, is_fixed_layout,
-          /* containing_block_expects_minmax_without_percentages */ false,
+          table, column_constraints, undistributable_space, is_fixed_layout,
+          /* allow_column_percentages */ true,
           /* skip_collapsed_columns */ false);
 
   // Standard: "used width of the table".
@@ -499,12 +491,11 @@
 
 MinMaxSizesResult NGTableLayoutAlgorithm::ComputeMinMaxSizes(
     const MinMaxSizesInput& input) const {
-  LayoutNGTable* layout_table = To<LayoutNGTable>(Node().GetLayoutBox());
   const bool is_fixed_layout = Style().IsFixedTableLayout();
   // Tables need autosizer.
   base::Optional<TextAutosizer::TableLayoutScope> text_autosizer;
   if (!is_fixed_layout)
-    text_autosizer.emplace(layout_table);
+    text_autosizer.emplace(To<LayoutNGTable>(Node().GetLayoutBox()));
 
   const LogicalSize border_spacing = Style().TableBorderSpacing();
   NGTableGroupedChildren grouped_children(Node());
@@ -523,8 +514,8 @@
 
   const MinMaxSizes grid_min_max =
       NGTableAlgorithmHelpers::ComputeGridInlineMinMax(
-          *column_constraints, undistributable_space, is_fixed_layout,
-          ShouldIgnorePercentagesForMinMax(*layout_table),
+          Node(), *column_constraints, undistributable_space, is_fixed_layout,
+          /* allow_column_percentages */ false,
           /* skip_collapsed_columns */ true);
 
   MinMaxSizes min_max{
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
index 6c68e26..2f43e53f 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h"
 
 #include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
+#include "third_party/blink/renderer/core/layout/ng/table/ng_table_node.h"
 
 namespace blink {
 
@@ -823,12 +824,13 @@
 }  // namespace
 
 MinMaxSizes NGTableAlgorithmHelpers::ComputeGridInlineMinMax(
+    const NGTableNode& node,
     const NGTableTypes::Columns& column_constraints,
     LayoutUnit undistributable_space,
     bool is_fixed_layout,
-    bool containing_block_expects_minmax_without_percentages,
+    bool allow_column_percentages,
     bool skip_collapsed_columns) {
-  MinMaxSizes minmax;
+  MinMaxSizes min_max;
   // https://www.w3.org/TR/css-tables-3/#computing-the-table-width
   // Compute standard GRID_MIN/GRID_MAX. They are sum of column_constraints.
   //
@@ -847,9 +849,9 @@
   // T% * MINSUM + M = MINSUM.
 
   // Minimum total size estimate based on column's min_inline_size and percent.
-  LayoutUnit percent_maxsize_estimate;
+  LayoutUnit percent_max_size_estimate;
   // Sum of max_inline_sizes of non-percentage columns.
-  LayoutUnit non_percent_maxsize_sum;
+  LayoutUnit non_percent_max_size_sum;
   float percent_sum = 0;
   for (const NGTableTypes::Column& column : column_constraints.data) {
     if (skip_collapsed_columns && column.is_collapsed)
@@ -858,50 +860,50 @@
       // In fixed layout, constrained cells minimum inline size is their
       // maximum.
       if (is_fixed_layout && column.IsFixed()) {
-        minmax.min_size += *column.max_inline_size;
+        min_max.min_size += *column.max_inline_size;
       } else {
-        minmax.min_size += *column.min_inline_size;
+        min_max.min_size += *column.min_inline_size;
       }
       if (column.percent && *column.percent > 0) {
         if (*column.max_inline_size > LayoutUnit()) {
           LayoutUnit estimate = LayoutUnit(
               100 / *column.percent *
               (*column.max_inline_size - column.percent_border_padding));
-          percent_maxsize_estimate =
-              std::max(percent_maxsize_estimate, estimate);
+          percent_max_size_estimate =
+              std::max(percent_max_size_estimate, estimate);
         }
       } else {
-        non_percent_maxsize_sum += *column.max_inline_size;
+        non_percent_max_size_sum += *column.max_inline_size;
       }
     }
     if (column.max_inline_size)
-      minmax.max_size += *column.max_inline_size;
+      min_max.max_size += *column.max_inline_size;
     if (column.percent)
       percent_sum += *column.percent;
   }
   DCHECK_LE(percent_sum, 100.0f);
 
-  // Table max inline size constraint can be computed from:
-  // total column percentage combined with max_inline_size of nonpercent
-  // columns.
-  if (percent_sum > 0 && !containing_block_expects_minmax_without_percentages) {
+  // Table max inline size constraint can be computed from the total column
+  // percentage combined with max_inline_size of non-percent columns.
+  if (percent_sum > 0 &&
+      (allow_column_percentages || node.AllowColumnPercentages())) {
     LayoutUnit size_from_percent_and_fixed;
     DCHECK_GE(percent_sum, 0.0f);
-    if (non_percent_maxsize_sum != LayoutUnit()) {
+    if (non_percent_max_size_sum != LayoutUnit()) {
       if (percent_sum == 100.0f) {
         size_from_percent_and_fixed = NGTableTypes::kTableMaxInlineSize;
       } else {
         size_from_percent_and_fixed =
-            LayoutUnit((100 / (100 - percent_sum)) * non_percent_maxsize_sum);
+            LayoutUnit((100 / (100 - percent_sum)) * non_percent_max_size_sum);
       }
     }
-    minmax.max_size = std::max(minmax.max_size, size_from_percent_and_fixed);
-    minmax.max_size = std::max(minmax.max_size, percent_maxsize_estimate);
+    min_max.max_size = std::max(min_max.max_size, size_from_percent_and_fixed);
+    min_max.max_size = std::max(min_max.max_size, percent_max_size_estimate);
   }
 
-  minmax.max_size = std::max(minmax.min_size, minmax.max_size);
-  minmax += undistributable_space;
-  return minmax;
+  min_max.max_size = std::max(min_max.min_size, min_max.max_size);
+  min_max += undistributable_space;
+  return min_max;
 }
 
 void NGTableAlgorithmHelpers::DistributeColspanCellsToColumns(
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h
index cf41e4a5..895533ba 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h
@@ -10,6 +10,8 @@
 
 namespace blink {
 
+class NGTableNode;
+
 // Table size distribution algorithms.
 class CORE_EXPORT NGTableAlgorithmHelpers {
  public:
@@ -24,17 +26,18 @@
     return current_column + 1;
   }
 
-  // Flex/grid containing blocks need Table minmax size to be computed without
-  // using percentages.
-  // |containing_block_expects_minmax_without_percentages| is used to do
-  // this.
+  // Flex/grid/table-cell containing blocks require that the table min/max
+  // sizes be computed without percentages. |allow_column_percentages| is used
+  // to change this behaviour.
+  //
   // |undistributable_space| is size of space not occupied by cells
   // (borders, border spacing).
   static MinMaxSizes ComputeGridInlineMinMax(
+      const NGTableNode& node,
       const NGTableTypes::Columns& column_constraints,
       LayoutUnit undistributable_space,
       bool is_fixed_layout,
-      bool containing_block_expects_minmax_without_percentages,
+      bool allow_column_percentages,
       bool skip_collapsed_columns);
 
   static void DistributeColspanCellsToColumns(
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
index a05e941f..ba846d75 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
@@ -5,12 +5,12 @@
 #include "third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/layout/ng/table/ng_table_node.h"
+#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 
 namespace blink {
 
-class NGTableAlgorithmHelpersTest : public testing::Test {
-  void SetUp() override {}
-
+class NGTableAlgorithmHelpersTest : public RenderingTest {
  public:
   NGTableTypes::Column MakeColumn(int min_width,
                                   int max_width,
@@ -179,12 +179,19 @@
 }
 
 TEST_F(NGTableAlgorithmHelpersTest, ComputeGridInlineMinMax) {
+  SetBodyInnerHTML(R"HTML(
+    <div style="display: flex;">
+      <table id=target></table>
+    <div>
+  )HTML");
+  NGTableNode node(To<LayoutBox>(GetLayoutObjectByElementId("target")));
+
   scoped_refptr<NGTableTypes::Columns> column_constraints =
       base::MakeRefCounted<NGTableTypes::Columns>();
 
   LayoutUnit undistributable_space;
   bool is_fixed_layout = false;
-  bool containing_block_expects_minmax_without_percentages = false;
+  bool allow_column_percentages = true;
   bool skip_collapsed_columns = false;
 
   // No percentages, just sums up min/max.
@@ -193,9 +200,8 @@
   column_constraints->data.push_back(MakeColumn(30, 300));
 
   MinMaxSizes minmax = NGTableAlgorithmHelpers::ComputeGridInlineMinMax(
-      *column_constraints, undistributable_space, is_fixed_layout,
-      containing_block_expects_minmax_without_percentages,
-      skip_collapsed_columns);
+      node, *column_constraints, undistributable_space, is_fixed_layout,
+      allow_column_percentages, skip_collapsed_columns);
   EXPECT_EQ(minmax.min_size, LayoutUnit(60));
   EXPECT_EQ(minmax.max_size, LayoutUnit(600));
 
@@ -206,32 +212,28 @@
   column_constraints->data.push_back(MakeColumn(10, 10));
   column_constraints->data.push_back(MakeColumn(10, 10));
   minmax = NGTableAlgorithmHelpers::ComputeGridInlineMinMax(
-      *column_constraints, undistributable_space, is_fixed_layout,
-      containing_block_expects_minmax_without_percentages,
-      skip_collapsed_columns);
+      node, *column_constraints, undistributable_space, is_fixed_layout,
+      allow_column_percentages, skip_collapsed_columns);
   EXPECT_EQ(minmax.min_size, LayoutUnit(30));
   EXPECT_EQ(minmax.max_size, LayoutUnit(990));
 
-  // Without percent, minmax ignores percent
-  containing_block_expects_minmax_without_percentages = true;
+  allow_column_percentages = false;
   minmax = NGTableAlgorithmHelpers::ComputeGridInlineMinMax(
-      *column_constraints, undistributable_space, is_fixed_layout,
-      containing_block_expects_minmax_without_percentages,
-      skip_collapsed_columns);
+      node, *column_constraints, undistributable_space, is_fixed_layout,
+      allow_column_percentages, skip_collapsed_columns);
   EXPECT_EQ(minmax.min_size, LayoutUnit(30));
   EXPECT_EQ(minmax.max_size, LayoutUnit(119));
 
   // Percentage: total percentage of 20%, and non-percent width of 800 =>
   // table max size of 800 + (20% * 800/80%) = 1000
-  containing_block_expects_minmax_without_percentages = false;
+  allow_column_percentages = true;
   column_constraints->data.Shrink(0);
   column_constraints->data.push_back(MakeColumn(10, 100, 10));
   column_constraints->data.push_back(MakeColumn(10, 10, 10));
   column_constraints->data.push_back(MakeColumn(10, 800));
   minmax = NGTableAlgorithmHelpers::ComputeGridInlineMinMax(
-      *column_constraints, undistributable_space, is_fixed_layout,
-      containing_block_expects_minmax_without_percentages,
-      skip_collapsed_columns);
+      node, *column_constraints, undistributable_space, is_fixed_layout,
+      allow_column_percentages, skip_collapsed_columns);
   EXPECT_EQ(minmax.min_size, LayoutUnit(30));
   EXPECT_EQ(minmax.max_size, LayoutUnit(1000));
 }
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_node.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_node.cc
index 18b8ccd5..f4bd80b 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_node.cc
@@ -46,4 +46,21 @@
                                                         border_padding);
 }
 
+bool NGTableNode::AllowColumnPercentages() const {
+  // TODO(layout-dev): This function breaks the rule of "no tree-walks".
+  // However for this specific case it adds a lot of overhead for little gain.
+  // In the future, we could have a bit on a LayoutObject which indicates if we
+  // should allow column percentages, and maintain this when adding/removing
+  // from the tree.
+  const LayoutBlock* block = box_->ContainingBlock();
+  while (!block->IsLayoutView()) {
+    if (block->IsTableCell() || block->IsFlexibleBoxIncludingNG() ||
+        block->IsLayoutGrid())
+      return false;
+
+    block = block->ContainingBlock();
+  }
+  return true;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_node.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_node.h
index 01a7eff..31f7d7a 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_node.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_node.h
@@ -27,6 +27,16 @@
 
   LayoutUnit ComputeTableInlineSize(const NGConstraintSpace&,
                                     const NGBoxStrut& border_padding) const;
+
+  // Tables are special in that their max intrinsic-size can be "infinite"
+  // (they should consume as much space as possible). However a lot of layout
+  // modes (flex/grid) don't deal well with "infinite" max intrinsic-size, so
+  // we disable this behaviour whenever we are an arbitrary descendant of one
+  // of these layout modes.
+  //
+  // TODO(layout-dev): This isn't ideal, as we may have a fixed inline-size
+  // parent where an "infinite" size would be fine.
+  bool AllowColumnPercentages() const;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/paint/box_painter_base.cc b/third_party/blink/renderer/core/paint/box_painter_base.cc
index ae02a2a..0d26ffb 100644
--- a/third_party/blink/renderer/core/paint/box_painter_base.cc
+++ b/third_party/blink/renderer/core/paint/box_painter_base.cc
@@ -136,10 +136,13 @@
       }
     }
 
-    // Draw only the shadow.
-    context.SetShadow(shadow_offset, shadow_blur, shadow_color,
-                      DrawLooperBuilder::kShadowRespectsTransforms,
-                      DrawLooperBuilder::kShadowIgnoresAlpha, kDrawShadowOnly);
+    // Draw only the shadow. If the color of the shadow is transparent we will
+    // set an empty draw looper.
+    DrawLooperBuilder draw_looper_builder;
+    draw_looper_builder.AddShadow(shadow_offset, shadow_blur, shadow_color,
+                                  DrawLooperBuilder::kShadowRespectsTransforms,
+                                  DrawLooperBuilder::kShadowIgnoresAlpha);
+    context.SetDrawLooper(draw_looper_builder.DetachDrawLooper());
 
     if (has_border_radius) {
       FloatRoundedRect rounded_fill_rect = border;
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
index 18dbf5e..36ace737 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
@@ -356,25 +356,28 @@
 
 bool CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
     const PaintLayer& layer) {
-  const auto& layout_object = layer.GetLayoutObject();
-  if (!layout_object.StyleRef().HasViewportConstrainedPosition() &&
-      !layout_object.StyleRef().HasStickyConstrainedPosition())
-    return false;
-
   // Don't promote fixed position elements that are descendants of a non-view
   // container, e.g. transformed elements.  They will stay fixed wrt the
   // container rather than the enclosing frame.
-  EPosition position = layout_object.StyleRef().GetPosition();
-  if (position == EPosition::kFixed) {
-    return layer.FixedToViewport() &&
-           layout_object.GetFrameView()->LayoutViewport()->ScrollsOverflow();
+  if (layer.FixedToViewport()) {
+    // We check for |HasOverflow| instead of |ScrollsOverflow| to ensure fixed
+    // position elements are composited under overflow: hidden, which can still
+    // have smooth scroll animations.
+    LocalFrameView* frame_view = layer.GetLayoutObject().GetFrameView();
+    return frame_view->LayoutViewport()->HasOverflow();
   }
-  DCHECK_EQ(position, EPosition::kSticky);
 
   // Don't promote sticky position elements that cannot move with scrolls.
-  if (!layer.SticksToScroller())
-    return false;
-  return layer.AncestorScrollContainerLayer()->ScrollsOverflow();
+  if (layer.SticksToScroller()) {
+    // We check for |HasOverflow| instead of |ScrollsOverflow| to ensure sticky
+    // position elements are composited under overflow: hidden, which can still
+    // have smooth scroll animations.
+    return layer.AncestorScrollContainerLayer()
+        ->GetScrollableArea()
+        ->HasOverflow();
+  }
+
+  return false;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
index ca9787a1..44f8635 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
@@ -102,9 +102,25 @@
 
 TEST_F(CompositingReasonFinderTest, OnlyScrollingStickyPositionPromoted) {
   SetBodyInnerHTML(R"HTML(
-    <style>.scroller {width: 400px; height: 400px; overflow: auto;
-    will-change: transform;}
-    .sticky { position: sticky; top: 0; width: 10px; height: 10px;}
+    <style>
+      .scroller {
+        width: 400px;
+        height: 400px;
+        overflow: auto;
+        will-change: transform;
+      }
+      .sticky {
+        position: sticky;
+        top: 0;
+        width: 10px;
+        height: 10px;
+      }
+      .overflow-hidden {
+        width: 400px;
+        height: 400px;
+        overflow: hidden;
+        will-change: transform;
+      }
     </style>
     <div class='scroller'>
       <div id='sticky-scrolling' class='sticky'></div>
@@ -113,14 +129,49 @@
     <div class='scroller'>
       <div id='sticky-no-scrolling' class='sticky'></div>
     </div>
+    <div class='overflow-hidden'>
+      <div id='overflow-hidden-scrolling' class='sticky'></div>
+      <div style='height: 2000px;'></div>
+    </div>
+    <div class='overflow-hidden'>
+      <div id='overflow-hidden-no-scrolling' class='sticky'></div>
+    </div>
   )HTML");
 
-  EXPECT_EQ(
-      kPaintsIntoOwnBacking,
-      GetPaintLayerByElementId("sticky-scrolling")->GetCompositingState());
-  EXPECT_EQ(
-      kNotComposited,
-      GetPaintLayerByElementId("sticky-no-scrolling")->GetCompositingState());
+  auto& sticky_scrolling =
+      *To<LayoutBoxModelObject>(GetLayoutObjectByElementId("sticky-scrolling"));
+  EXPECT_TRUE(
+      CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
+          *sticky_scrolling.Layer()));
+
+  auto& sticky_no_scrolling = *To<LayoutBoxModelObject>(
+      GetLayoutObjectByElementId("sticky-no-scrolling"));
+  EXPECT_FALSE(
+      CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
+          *sticky_no_scrolling.Layer()));
+
+  auto& overflow_hidden_scrolling = *To<LayoutBoxModelObject>(
+      GetLayoutObjectByElementId("overflow-hidden-scrolling"));
+  EXPECT_TRUE(
+      CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
+          *overflow_hidden_scrolling.Layer()));
+
+  auto& overflow_hidden_no_scrolling = *To<LayoutBoxModelObject>(
+      GetLayoutObjectByElementId("overflow-hidden-no-scrolling"));
+  EXPECT_FALSE(
+      CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
+          *overflow_hidden_no_scrolling.Layer()));
+
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+    EXPECT_EQ(kPaintsIntoOwnBacking,
+              sticky_scrolling.Layer()->GetCompositingState());
+    EXPECT_EQ(kNotComposited,
+              sticky_no_scrolling.Layer()->GetCompositingState());
+    EXPECT_EQ(kPaintsIntoOwnBacking,
+              overflow_hidden_scrolling.Layer()->GetCompositingState());
+    EXPECT_EQ(kNotComposited,
+              overflow_hidden_no_scrolling.Layer()->GetCompositingState());
+  }
 }
 
 void CompositingReasonFinderTest::CheckCompositingReasonsForAnimation(
diff --git a/third_party/blink/renderer/core/paint/inline_text_box_painter.cc b/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
index 081b466..9eb2f32 100644
--- a/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
+++ b/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
@@ -22,6 +22,7 @@
 #include "third_party/blink/renderer/core/paint/highlight_painting_utils.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
 #include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
+#include "third_party/blink/renderer/core/paint/selection_bounds_recorder.h"
 #include "third_party/blink/renderer/core/paint/text_decoration_info.h"
 #include "third_party/blink/renderer/core/paint/text_painter.h"
 #include "third_party/blink/renderer/platform/graphics/dom_node_id.h"
@@ -153,18 +154,6 @@
   physical_overflow.Move(paint_offset);
   IntRect visual_rect = EnclosingIntRect(physical_overflow);
 
-  // The text clip phase already has a DrawingRecorder. Text clips are initiated
-  // only in BoxPainter::PaintFillLayer, which is already within a
-  // DrawingRecorder.
-  base::Optional<DrawingRecorder> recorder;
-  if (paint_info.phase != PaintPhase::kTextClip) {
-    if (DrawingRecorder::UseCachedDrawingIfPossible(
-            paint_info.context, inline_text_box_, paint_info.phase))
-      return;
-    recorder.emplace(paint_info.context, inline_text_box_, paint_info.phase,
-                     visual_rect);
-  }
-
   GraphicsContext& context = paint_info.context;
   PhysicalOffset box_origin =
       inline_text_box_.PhysicalLocation() + paint_offset;
@@ -182,6 +171,35 @@
                         PhysicalSize(inline_text_box_.LogicalWidth(),
                                      inline_text_box_.LogicalHeight()));
 
+  base::Optional<SelectionBoundsRecorder> selection_recorder;
+  if (have_selection && paint_info.phase == PaintPhase::kForeground &&
+      !is_printing) {
+    const FrameSelection& frame_selection =
+        InlineLayoutObject().GetFrame()->Selection();
+    SelectionState selection_state =
+        frame_selection.ComputeLayoutSelectionStateForInlineTextBox(
+            inline_text_box_);
+    if (SelectionBoundsRecorder::ShouldRecordSelection(frame_selection,
+                                                       selection_state)) {
+      PhysicalRect selection_rect =
+          GetSelectionRect<InlineTextBoxPainter::PaintOptions::kNormal>(
+              context, box_rect, style_to_use, style_to_use.GetFont());
+      selection_recorder.emplace(selection_state, selection_rect,
+                                 context.GetPaintController());
+    }
+  }
+
+  // The text clip phase already has a DrawingRecorder. Text clips are initiated
+  // only in BoxPainter::PaintFillLayer, which is already within a
+  // DrawingRecorder.
+  base::Optional<DrawingRecorder> recorder;
+  if (paint_info.phase != PaintPhase::kTextClip) {
+    if (DrawingRecorder::UseCachedDrawingIfPossible(context, inline_text_box_,
+                                                    paint_info.phase))
+      return;
+    recorder.emplace(context, inline_text_box_, paint_info.phase, visual_rect);
+  }
+
   unsigned length = inline_text_box_.Len();
   const String& layout_item_string =
       inline_text_box_.GetLineLayoutItem().GetText();
diff --git a/third_party/blink/renderer/core/paint/inline_text_box_painter_test.cc b/third_party/blink/renderer/core/paint/inline_text_box_painter_test.cc
index 9754276f..1b505e57 100644
--- a/third_party/blink/renderer/core/paint/inline_text_box_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/inline_text_box_painter_test.cc
@@ -6,6 +6,7 @@
 
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/editing/testing/selection_sample.h"
 #include "third_party/blink/renderer/core/paint/paint_controller_paint_test.h"
 
 using testing::ElementsAre;
@@ -27,4 +28,64 @@
   EXPECT_EQ(6u, ContentDisplayItems().size());
 }
 
+class InlineTextBoxPainterNonNGTest : public PaintControllerPaintTest,
+                                      public ScopedLayoutNGForTest {
+ public:
+  InlineTextBoxPainterNonNGTest() : ScopedLayoutNGForTest(false) {}
+};
+
+INSTANTIATE_PAINT_TEST_SUITE_P(InlineTextBoxPainterNonNGTest);
+
+TEST_P(InlineTextBoxPainterNonNGTest, RecordedSelectionAll) {
+  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+    return;
+  SetBodyInnerHTML("<span>A<br>B<br>C</span>");
+
+  GetDocument().GetFrame()->Selection().SetHandleVisibleForTesting();
+  GetDocument().GetFrame()->Selection().SelectAll();
+  UpdateAllLifecyclePhasesForTest();
+
+  auto chunks = ContentPaintChunks();
+  EXPECT_EQ(chunks.size(), 1u);
+  EXPECT_TRUE(chunks.begin()->layer_selection_data->start.has_value());
+  EXPECT_TRUE(chunks.begin()->layer_selection_data->end.has_value());
+  PaintedSelectionBound start =
+      chunks.begin()->layer_selection_data->start.value();
+  EXPECT_EQ(start.type, gfx::SelectionBound::LEFT);
+  EXPECT_EQ(start.edge_start, IntPoint(8, 8));
+  EXPECT_EQ(start.edge_end, IntPoint(8, 9));
+
+  PaintedSelectionBound end = chunks.begin()->layer_selection_data->end.value();
+  EXPECT_EQ(end.type, gfx::SelectionBound::RIGHT);
+  EXPECT_EQ(end.edge_start, IntPoint(9, 10));
+  EXPECT_EQ(end.edge_end, IntPoint(9, 11));
+}
+
+TEST_P(InlineTextBoxPainterNonNGTest, RecordedSelectionMultiline) {
+  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+    return;
+
+  GetDocument().GetFrame()->Selection().SetSelectionAndEndTyping(
+      SelectionSample::SetSelectionText(
+          GetDocument().body(),
+          "<div style='white-space:pre'>f^oo\nbar\nb|az</div>"));
+  GetDocument().GetFrame()->Selection().SetHandleVisibleForTesting();
+  UpdateAllLifecyclePhasesForTest();
+
+  auto chunks = ContentPaintChunks();
+  EXPECT_EQ(chunks.size(), 1u);
+  EXPECT_TRUE(chunks.begin()->layer_selection_data->start.has_value());
+  EXPECT_TRUE(chunks.begin()->layer_selection_data->end.has_value());
+  PaintedSelectionBound start =
+      chunks.begin()->layer_selection_data->start.value();
+  EXPECT_EQ(start.type, gfx::SelectionBound::LEFT);
+  EXPECT_EQ(start.edge_start, IntPoint(8, 8));
+  EXPECT_EQ(start.edge_end, IntPoint(8, 9));
+
+  PaintedSelectionBound end = chunks.begin()->layer_selection_data->end.value();
+  EXPECT_EQ(end.type, gfx::SelectionBound::RIGHT);
+  EXPECT_EQ(end.edge_start, IntPoint(9, 10));
+  EXPECT_EQ(end.edge_end, IntPoint(9, 11));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index 0b1a6df..9b6f31e 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -5909,7 +5909,7 @@
   EXPECT_EQ(3u, NumFragments(fixed));
   for (int i = 0; i < 3; i++) {
     const auto& fragment = FragmentAt(fixed, i);
-    EXPECT_EQ(PhysicalOffset(20, -180 + i * 400), fragment.PaintOffset());
+    EXPECT_EQ(PhysicalOffset(0, 0), fragment.PaintOffset());
     EXPECT_EQ(LayoutUnit(400 * i), fragment.LogicalTopInFlowThread());
   }
 
@@ -5917,7 +5917,7 @@
   EXPECT_EQ(3u, NumFragments(fixed_child));
   for (int i = 0; i < 3; i++) {
     const auto& fragment = FragmentAt(fixed_child, i);
-    EXPECT_EQ(PhysicalOffset(20, -170 + i * 400), fragment.PaintOffset());
+    EXPECT_EQ(PhysicalOffset(0, 10), fragment.PaintOffset());
     EXPECT_EQ(LayoutUnit(i * 400), fragment.LogicalTopInFlowThread());
   }
 
diff --git a/third_party/blink/renderer/core/paint/paint_timing_detector.cc b/third_party/blink/renderer/core/paint/paint_timing_detector.cc
index 94839d6..7842dfc 100644
--- a/third_party/blink/renderer/core/paint/paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/paint_timing_detector.cc
@@ -440,7 +440,6 @@
   visitor->Trace(frame_view_);
   visitor->Trace(largest_contentful_paint_calculator_);
   visitor->Trace(callback_manager_);
-  visitor->Trace(visualizer_);
 }
 
 void PaintTimingCallbackManagerImpl::
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
index 134f8ea..a894531 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
@@ -257,7 +257,7 @@
 }
 
 void TextRecordsManager::CleanUpLargestTextPaint() {
-  ltp_manager_.reset();
+  ltp_manager_.Clear();
 }
 
 void TextRecordsManager::RemoveInvisibleRecord(const LayoutObject& object) {
@@ -370,9 +370,10 @@
 
 TextRecordsManager::TextRecordsManager(
     LocalFrameView* frame_view,
-    PaintTimingDetector* paint_timing_detector) {
-  ltp_manager_.emplace(frame_view, paint_timing_detector);
-}
+    PaintTimingDetector* paint_timing_detector)
+    : ltp_manager_(MakeGarbageCollected<LargestTextPaintManager>(
+          frame_view,
+          paint_timing_detector)) {}
 
 void TextRecordsManager::Trace(Visitor* visitor) const {
   visitor->Trace(text_element_timing_);
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
index cfac87e..02e075f2 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
@@ -47,8 +47,8 @@
   base::TimeTicks paint_time = base::TimeTicks();
 };
 
-class CORE_EXPORT LargestTextPaintManager {
-  DISALLOW_NEW();
+class CORE_EXPORT LargestTextPaintManager final
+    : public GarbageCollected<LargestTextPaintManager> {
   using TextRecordSetComparator = bool (*)(const base::WeakPtr<TextRecord>&,
                                            const base::WeakPtr<TextRecord>&);
   using TextRecordSet =
@@ -173,9 +173,7 @@
   // candidate.
   void ReportLargestIgnoredText();
 
-  inline bool IsRecordingLargestTextPaint() const {
-    return ltp_manager_.has_value();
-  }
+  inline bool IsRecordingLargestTextPaint() const { return ltp_manager_; }
 
   void Trace(Visitor*) const;
 
@@ -198,7 +196,7 @@
   // LCP computations, even if the size of the text itself is not 0. They are
   // considered invisible objects by Largest Contentful Paint.
   Deque<std::unique_ptr<TextRecord>> size_zero_texts_queued_for_paint_time_;
-  base::Optional<LargestTextPaintManager> ltp_manager_;
+  Member<LargestTextPaintManager> ltp_manager_;
   Member<TextElementTiming> text_element_timing_;
 };
 
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
index b1d297f..01e3ead 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
@@ -73,7 +73,7 @@
         .GetTextPaintTimingDetector();
   }
 
-  base::Optional<LargestTextPaintManager>& GetLargestTextPaintManager() {
+  LargestTextPaintManager* GetLargestTextPaintManager() {
     return GetTextPaintTimingDetector()->records_manager_.ltp_manager_;
   }
 
diff --git a/third_party/blink/renderer/core/testing/dictionary_test.cc b/third_party/blink/renderer/core/testing/dictionary_test.cc
index 15f6ecf..f8e2116 100644
--- a/third_party/blink/renderer/core/testing/dictionary_test.cc
+++ b/third_party/blink/renderer/core/testing/dictionary_test.cc
@@ -66,8 +66,10 @@
     enum_or_null_member_ = testing_dictionary->enumOrNullMember();
   if (testing_dictionary->hasElementMember())
     element_member_ = testing_dictionary->elementMember();
-  if (testing_dictionary->hasElementOrNullMember())
+  if (testing_dictionary->hasElementOrNullMember()) {
     element_or_null_member_ = testing_dictionary->elementOrNullMember();
+    has_element_or_null_member_ = true;
+  }
   if (testing_dictionary->hasObjectMember())
     object_member_ = testing_dictionary->objectMember();
   object_or_null_member_with_default_ =
@@ -75,7 +77,9 @@
   if (testing_dictionary->hasDoubleOrStringMember())
     double_or_string_member_ = testing_dictionary->doubleOrStringMember();
   if (testing_dictionary->hasDoubleOrStringSequenceMember()) {
-    double_or_string_sequence_member_ =
+    double_or_string_sequence_or_null_member_ =
+        MakeGarbageCollected<HeapVector<DoubleOrString>>();
+    *double_or_string_sequence_or_null_member_ =
         testing_dictionary->doubleOrStringSequenceMember();
   }
   // eventTargetOrNullMember has a default null value.
@@ -149,10 +153,12 @@
   enum_member_with_default_ = String();
   enum_or_null_member_ = base::nullopt;
   element_member_ = nullptr;
-  element_or_null_member_.reset();
+  element_or_null_member_.Clear();
+  has_element_or_null_member_ = false;
   object_member_ = ScriptValue();
   object_or_null_member_with_default_ = ScriptValue();
   double_or_string_member_ = DoubleOrString();
+  double_or_string_sequence_or_null_member_ = nullptr;
   event_target_or_null_member_ = nullptr;
   derived_string_member_ = base::nullopt;
   derived_string_member_with_default_ = String();
@@ -211,15 +217,15 @@
     dict->setEnumOrNullMember(enum_or_null_member_.value());
   if (element_member_)
     dict->setElementMember(element_member_);
-  if (element_or_null_member_.has_value())
-    dict->setElementOrNullMember(element_or_null_member_.value());
+  if (has_element_or_null_member_)
+    dict->setElementOrNullMember(element_or_null_member_);
   dict->setObjectMember(object_member_);
   dict->setObjectOrNullMemberWithDefault(object_or_null_member_with_default_);
   if (!double_or_string_member_.IsNull())
     dict->setDoubleOrStringMember(double_or_string_member_);
-  if (double_or_string_sequence_member_) {
+  if (double_or_string_sequence_or_null_member_) {
     dict->setDoubleOrStringSequenceMember(
-        double_or_string_sequence_member_.value());
+        *double_or_string_sequence_or_null_member_);
   }
   dict->setEventTargetOrNullMember(event_target_or_null_member_);
   dict->setInternalEnumOrInternalEnumSequenceMember(
@@ -251,7 +257,7 @@
   visitor->Trace(element_or_null_member_);
   visitor->Trace(object_member_);
   visitor->Trace(object_or_null_member_with_default_);
-  visitor->Trace(double_or_string_sequence_member_);
+  visitor->Trace(double_or_string_sequence_or_null_member_);
   visitor->Trace(event_target_or_null_member_);
   visitor->Trace(any_member_);
   visitor->Trace(callback_function_member_);
diff --git a/third_party/blink/renderer/core/testing/dictionary_test.h b/third_party/blink/renderer/core/testing/dictionary_test.h
index 1840a36..5bbd24c5 100644
--- a/third_party/blink/renderer/core/testing/dictionary_test.h
+++ b/third_party/blink/renderer/core/testing/dictionary_test.h
@@ -56,6 +56,8 @@
   // Some members are not wrapped with Optional because:
   //  - |longMemberWithDefault| has a non-null default value
   //  - String and PtrTypes can express whether they are null
+  //  - base::Optional does not work with GarbageCollected types when used on
+  //  heap.
   base::Optional<int> long_member_;
   base::Optional<int> long_member_with_clamp_;
   base::Optional<int> long_member_with_enforce_range_;
@@ -82,11 +84,12 @@
   base::Optional<String> enum_or_null_member_;
 #endif
   Member<Element> element_member_;
-  base::Optional<Member<Element>> element_or_null_member_;
+  Member<Element> element_or_null_member_;
+  bool has_element_or_null_member_ = false;
   ScriptValue object_member_;
   ScriptValue object_or_null_member_with_default_;
   DoubleOrString double_or_string_member_;
-  base::Optional<HeapVector<DoubleOrString>> double_or_string_sequence_member_;
+  Member<HeapVector<DoubleOrString>> double_or_string_sequence_or_null_member_;
   Member<EventTarget> event_target_or_null_member_;
   base::Optional<String> derived_string_member_;
   String derived_string_member_with_default_;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index b060f21..fe114147 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1495,21 +1495,6 @@
   return kExpandedUndefined;
 }
 
-bool AXNodeObject::IsModal() const {
-  if (RoleValue() != ax::mojom::blink::Role::kDialog &&
-      RoleValue() != ax::mojom::blink::Role::kAlertDialog)
-    return false;
-
-  bool modal = false;
-  if (HasAOMPropertyOrARIAAttribute(AOMBooleanProperty::kModal, modal))
-    return modal;
-
-  if (GetNode() && IsA<HTMLDialogElement>(*GetNode()))
-    return To<Element>(GetNode())->IsInTopLayer();
-
-  return false;
-}
-
 bool AXNodeObject::IsRequired() const {
   auto* form_control = DynamicTo<HTMLFormControlElement>(GetNode());
   if (form_control && form_control->IsRequired())
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.h b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
index 124ca76..9df431f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
@@ -119,7 +119,6 @@
   // Check object state.
   bool IsClickable() const final;
   AccessibilityExpanded IsExpanded() const override;
-  bool IsModal() const final;
   bool IsRequired() const final;
   bool IsControl() const override;
   AXRestriction Restriction() const override;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index b4aeb2fd..7a0b5ab6 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -1690,6 +1690,22 @@
   return false;
 }
 
+bool AXObject::IsModal() const {
+  if (RoleValue() != ax::mojom::blink::Role::kDialog &&
+      RoleValue() != ax::mojom::blink::Role::kAlertDialog)
+    return false;
+
+  bool modal = false;
+  if (HasAOMPropertyOrARIAAttribute(AOMBooleanProperty::kModal, modal)) {
+    return modal;
+  }
+
+  if (GetNode() && IsA<HTMLDialogElement>(*GetNode()))
+    return To<Element>(GetNode())->IsInTopLayer();
+
+  return false;
+}
+
 bool AXObject::IsBlockedByAriaModalDialog(
     IgnoredReasons* ignored_reasons) const {
   AXObject* active_aria_modal_dialog =
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 926e219..61e7017 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -473,7 +473,7 @@
   virtual bool IsLineBreakingObject() const { return false; }
   virtual bool IsLinked() const { return false; }
   virtual bool IsLoaded() const { return false; }
-  virtual bool IsModal() const { return false; }
+  virtual bool IsModal() const;
   virtual bool IsMultiSelectable() const { return false; }
   virtual bool IsOffScreen() const { return false; }
   virtual bool IsRequired() const { return false; }
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h
index 2510f01..237a1a8a 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h
@@ -24,6 +24,12 @@
 class CSSValue;
 class Element;
 
+enum ShadowMode {
+  kDrawShadowAndForeground,
+  kDrawShadowOnly,
+  kDrawForegroundOnly
+};
+
 class CanvasRenderingContext2DState final
     : public GarbageCollected<CanvasRenderingContext2DState>,
       public FontSelectorClient {
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index 0bb1729..886a782 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -1443,12 +1443,9 @@
 }
 
 // static
-WebAXObject WebAXObject::FromWebDocument(const WebDocument& web_document,
-                                         bool update_layout_if_necessary) {
-  if (update_layout_if_necessary &&
-      !MaybeUpdateLayoutAndCheckValidity(web_document)) {
+WebAXObject WebAXObject::FromWebDocument(const WebDocument& web_document) {
+  if (!MaybeUpdateLayoutAndCheckValidity(web_document))
     return WebAXObject();
-  }
   const Document* document = web_document.ConstUnwrap<Document>();
   auto* cache = To<AXObjectCacheImpl>(document->ExistingAXObjectCache());
   return cache ? WebAXObject(cache->GetOrCreate(document->GetLayoutView()))
diff --git a/third_party/blink/renderer/modules/mediasource/media_source.cc b/third_party/blink/renderer/modules/mediasource/media_source.cc
index 8a61a4e..db69f38 100644
--- a/third_party/blink/renderer/modules/mediasource/media_source.cc
+++ b/third_party/blink/renderer/modules/mediasource/media_source.cc
@@ -133,7 +133,9 @@
       active_source_buffers_(
           MakeGarbageCollected<SourceBufferList>(GetExecutionContext(),
                                                  async_event_queue_.Get())),
-      live_seekable_range_(MakeGarbageCollected<TimeRanges>()) {
+      has_live_seekable_range_(false),
+      live_seekable_range_start_(0.0),
+      live_seekable_range_end_(0.0) {
   DVLOG(1) << __func__ << " this=" << this;
 
   DCHECK(RuntimeEnabledFeatures::MediaSourceInWorkersEnabled() ||
@@ -667,11 +669,13 @@
 
 void MediaSource::Trace(Visitor* visitor) const {
   visitor->Trace(async_event_queue_);
-  {
-    MutexLocker lock(attachment_link_lock_);
-    visitor->Trace(attachment_tracer_);
-    visitor->Trace(live_seekable_range_);
-  }
+
+  // |attachment_tracer_| is only set when this object is owned by the main
+  // thread and is possibly involved in a SameThreadMediaSourceAttachment.
+  // Therefore, it is thread-safe to access it here without taking the
+  // |attachment_link_lock_|.
+  visitor->Trace(TS_UNCHECKED_READ(attachment_tracer_));
+
   visitor->Trace(source_buffers_);
   visitor->Trace(active_source_buffers_);
   EventTargetWithInlineData::Trace(visitor);
@@ -800,29 +804,26 @@
 
     {
       // If cross-thread, protect against concurrent usage of
-      // |live_seekable_range_|, since that member is updated without taking the
+      // |*live_seekable_range*|, since those are updated without taking the
       // attachment's internal |attachment_state_lock_|.
       MutexLocker lock(attachment_link_lock_);
 
       // 1. If live seekable range is not empty:
-      if (live_seekable_range_->length() != 0) {
+      if (has_live_seekable_range_) {
         // 1.1. Let union ranges be the union of live seekable range and the
         //      HTMLMediaElement.buffered attribute.
         // 1.2. Return a single range with a start time equal to the
         //      earliest start time in union ranges and an end time equal to
         //      the highest end time in union ranges and abort these steps.
         if (buffered.empty()) {
-          ranges.emplace_back(
-              live_seekable_range_->start(0, ASSERT_NO_EXCEPTION),
-              live_seekable_range_->end(0, ASSERT_NO_EXCEPTION));
+          ranges.emplace_back(live_seekable_range_start_,
+                              live_seekable_range_end_);
           return ranges;
         }
 
         ranges.emplace_back(
-            std::min(live_seekable_range_->start(0, ASSERT_NO_EXCEPTION),
-                     buffered.front().start),
-            std::max(live_seekable_range_->end(0, ASSERT_NO_EXCEPTION),
-                     buffered.back().end));
+            std::min(live_seekable_range_start_, buffered.front().start),
+            std::max(live_seekable_range_end_, buffered.back().end));
         return ranges;
       }
     }
@@ -1089,10 +1090,12 @@
     // SeekableInternal simultaneously, if attached fully. Here, for simplicity,
     // we don't need to take the full attachment exclusive
     // |attachment_state_lock_| so long as we
-    // fully protect access to |live_seekable_range_| read/write with
+    // fully protect access to |*live_seekable_range*| read/write with
     // |attachment_link_lock_|.
     MutexLocker lock(attachment_link_lock_);
-    live_seekable_range_ = MakeGarbageCollected<TimeRanges>(start, end);
+    has_live_seekable_range_ = true;
+    live_seekable_range_start_ = start;
+    live_seekable_range_end_ = end;
   }
 }
 
@@ -1116,12 +1119,15 @@
     // If we are cross-thread, then main thread could be running
     // SeekableInternal simultaneously, if attached fully. Here, for simplicity,
     // we don't need to take the full attachment exclusive
-    // |attachment_state_lock_|so long as we
-    // fully protect access to |live_seekable_range_| read/write with
+    // |attachment_state_lock_| so long as we
+    // fully protect access to |*live_seekable_range*| read/write with
     // |attachment_link_lock_|.
     MutexLocker lock(attachment_link_lock_);
-    if (live_seekable_range_->length() != 0)
-      live_seekable_range_ = MakeGarbageCollected<TimeRanges>();
+    if (has_live_seekable_range_) {
+      has_live_seekable_range_ = false;
+      live_seekable_range_start_ = 0.0;
+      live_seekable_range_end_ = 0.0;
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/modules/mediasource/media_source.h b/third_party/blink/renderer/modules/mediasource/media_source.h
index e9e4f62..2866b90 100644
--- a/third_party/blink/renderer/modules/mediasource/media_source.h
+++ b/third_party/blink/renderer/modules/mediasource/media_source.h
@@ -264,7 +264,7 @@
 
   // |attachment_link_lock_| protects read/write of |media_source_attachment_|,
   // |attachment_tracer_|, |context_already_destroyed_|, and
-  // |live_seekable_range_|.  It is only truly necessary for
+  // |*live_seekable_range*_|.  It is only truly necessary for
   // CrossThreadAttachment usage of worker MSE, to prevent read/write collision
   // on main thread versus worker thread. Note that |attachment_link_lock_| must
   // be released before attempting CrossThreadMediaSourceAttachment
@@ -289,7 +289,11 @@
   Member<SourceBufferList> source_buffers_;
   Member<SourceBufferList> active_source_buffers_;
 
-  Member<TimeRanges> live_seekable_range_ GUARDED_BY(attachment_link_lock_);
+  // These are kept as raw data (not an Oilpan managed GC-able TimeRange) to
+  // avoid need to take lock during ::Trace, which could lead to deadlock.
+  bool has_live_seekable_range_ GUARDED_BY(attachment_link_lock_);
+  double live_seekable_range_start_ GUARDED_BY(attachment_link_lock_);
+  double live_seekable_range_end_ GUARDED_BY(attachment_link_lock_);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediasource/track_default_list.cc b/third_party/blink/renderer/modules/mediasource/track_default_list.cc
index 4a4bb665..a1c58cab 100644
--- a/third_party/blink/renderer/modules/mediasource/track_default_list.cc
+++ b/third_party/blink/renderer/modules/mediasource/track_default_list.cc
@@ -59,8 +59,6 @@
   return track_defaults_[index].Get();
 }
 
-TrackDefaultList::TrackDefaultList() = default;
-
 TrackDefaultList::TrackDefaultList(
     const HeapVector<Member<TrackDefault>>& track_defaults)
     : track_defaults_(track_defaults) {}
diff --git a/third_party/blink/renderer/modules/mediasource/track_default_list.h b/third_party/blink/renderer/modules/mediasource/track_default_list.h
index a3d7b4a..e558f6d 100644
--- a/third_party/blink/renderer/modules/mediasource/track_default_list.h
+++ b/third_party/blink/renderer/modules/mediasource/track_default_list.h
@@ -21,7 +21,7 @@
   static TrackDefaultList* Create(const HeapVector<Member<TrackDefault>>&,
                                   ExceptionState&);
 
-  TrackDefaultList();
+  TrackDefaultList() = default;
   explicit TrackDefaultList(const HeapVector<Member<TrackDefault>>&);
 
   unsigned length() const { return track_defaults_.size(); }
@@ -30,7 +30,7 @@
   void Trace(Visitor*) const override;
 
  private:
-  const HeapVector<Member<TrackDefault>> track_defaults_;
+  const HeapVector<Member<TrackDefault>> track_defaults_{};
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/BUILD.gn b/third_party/blink/renderer/modules/webcodecs/BUILD.gn
index 5ad6f8c5..834d7c1 100644
--- a/third_party/blink/renderer/modules/webcodecs/BUILD.gn
+++ b/third_party/blink/renderer/modules/webcodecs/BUILD.gn
@@ -13,6 +13,8 @@
     "audio_decoder.h",
     "audio_decoder_broker.cc",
     "audio_decoder_broker.h",
+    "audio_encoder.cc",
+    "audio_encoder.h",
     "audio_frame.cc",
     "audio_frame.h",
     "codec_config_eval.h",
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc b/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
index 38014a9..629c57b1 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
@@ -14,6 +14,7 @@
 #include "media/base/supported_types.h"
 #include "media/base/waiting.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_decoder_config.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_decoder_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk.h"
@@ -27,6 +28,28 @@
 
 namespace blink {
 
+bool IsValidConfig(const AudioDecoderConfig& config,
+                   media::AudioType& out_audio_type,
+                   String& out_console_message) {
+  media::AudioCodec codec = media::kUnknownAudioCodec;
+  bool is_codec_ambiguous = true;
+  bool parse_succeeded = ParseAudioCodecString("", config.codec().Utf8(),
+                                               &is_codec_ambiguous, &codec);
+
+  if (!parse_succeeded) {
+    out_console_message = "Failed to parse codec string.";
+    return false;
+  }
+
+  if (is_codec_ambiguous) {
+    out_console_message = "Codec string is ambiguous.";
+    return false;
+  }
+
+  out_audio_type = {codec};
+  return true;
+}
+
 // static
 std::unique_ptr<AudioDecoderTraits::MediaDecoderType>
 AudioDecoderTraits::CreateDecoder(
@@ -82,29 +105,30 @@
 }
 
 // static
+ScriptPromise AudioDecoder::isConfigSupported(ScriptState* script_state,
+                                              const AudioDecoderConfig* config,
+                                              ExceptionState& exception_state) {
+  media::AudioType audio_type;
+  String console_message;
+
+  if (!IsValidConfig(*config, audio_type, console_message)) {
+    exception_state.ThrowTypeError(console_message);
+    return ScriptPromise();
+  }
+
+  bool is_supported = media::IsSupportedAudioType(audio_type);
+  return ScriptPromise::Cast(script_state, ToV8(is_supported, script_state));
+}
+
+// static
 CodecConfigEval AudioDecoder::MakeMediaAudioDecoderConfig(
     const ConfigType& config,
     MediaConfigType& out_media_config,
     String& out_console_message) {
-  media::AudioCodec codec = media::kUnknownAudioCodec;
-  bool is_codec_ambiguous = true;
-  bool parse_succeeded = ParseAudioCodecString("", config.codec().Utf8(),
-                                               &is_codec_ambiguous, &codec);
+  media::AudioType audio_type;
 
-  if (!parse_succeeded) {
-    out_console_message = "Failed to parse codec string.";
+  if (!IsValidConfig(config, audio_type, out_console_message))
     return CodecConfigEval::kInvalid;
-  }
-
-  if (is_codec_ambiguous) {
-    out_console_message = "Codec string is ambiguous.";
-    return CodecConfigEval::kInvalid;
-  }
-
-  if (!media::IsSupportedAudioType({codec})) {
-    out_console_message = "Configuration is not supported.";
-    return CodecConfigEval::kUnsupported;
-  }
 
   std::vector<uint8_t> extra_data;
   if (config.hasDescription()) {
@@ -131,8 +155,8 @@
 
   // TODO(chcunningham): Add sample format to IDL.
   out_media_config.Initialize(
-      codec, media::kSampleFormatPlanarF32, channel_layout, config.sampleRate(),
-      extra_data, media::EncryptionScheme::kUnencrypted,
+      audio_type.codec, media::kSampleFormatPlanarF32, channel_layout,
+      config.sampleRate(), extra_data, media::EncryptionScheme::kUnencrypted,
       base::TimeDelta() /* seek preroll */, 0 /* codec delay */);
 
   return CodecConfigEval::kSupported;
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder.h b/third_party/blink/renderer/modules/webcodecs/audio_decoder.h
index e6417a7..c148d102 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder.h
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder.h
@@ -12,7 +12,6 @@
 #include "media/base/status.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/bindings/modules/v8/v8_video_frame_output_callback.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_webcodecs_error_callback.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/webcodecs/codec_config_eval.h"
@@ -38,6 +37,7 @@
 class EncodedAudioChunk;
 class ExceptionState;
 class AudioDecoderInit;
+class ScriptPromise;
 class V8AudioFrameOutputCallback;
 
 class MODULES_EXPORT AudioDecoderTraits {
@@ -77,6 +77,10 @@
                               const AudioDecoderInit*,
                               ExceptionState&);
 
+  static ScriptPromise isConfigSupported(ScriptState*,
+                                         const AudioDecoderConfig*,
+                                         ExceptionState&);
+
   // For use by MediaSource and by ::MakeMediaConfig.
   static CodecConfigEval MakeMediaAudioDecoderConfig(
       const ConfigType& config,
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder.idl b/third_party/blink/renderer/modules/webcodecs/audio_decoder.idl
index 7201cd2..7f03b2a5 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder.idl
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder.idl
@@ -23,6 +23,9 @@
   // |decodeQueueSize| is greater than a constant.
   readonly attribute long decodeQueueSize;
 
+  // Which state the decoder is in, indicating which methods can be called.
+  readonly attribute CodecState state;
+
   // Set the stream configuration for future decode() requests.
   //
   // The next decode request must be for a keyframe.
@@ -57,6 +60,7 @@
   // Not recoverable: make a new AudioDecoder if needed.
   [RaisesException] void close();
 
-  // Which state the decoder is in, indicating which methods can be called.
-  readonly attribute CodecState state;
+  // Call prior to configure() to determine whether config will be supported.
+  [CallWith=ScriptState, RaisesException]
+  static Promise<boolean> isConfigSupported(AudioDecoderConfig config);
 };
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc b/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc
new file mode 100644
index 0000000..de3cb2b
--- /dev/null
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder.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 "third_party/blink/renderer/modules/webcodecs/audio_encoder.h"
+
+namespace blink {
+
+AudioEncoder* AudioEncoder::Create(ScriptState* script_state,
+                                   const AudioEncoderInit* init,
+                                   ExceptionState& exception_state) {
+  return MakeGarbageCollected<AudioEncoder>(script_state, init,
+                                            exception_state);
+}
+
+AudioEncoder::AudioEncoder(ScriptState* script_state,
+                           const AudioEncoderInit* init,
+                           ExceptionState& exception_state)
+    : ExecutionContextLifecycleObserver(ExecutionContext::From(script_state)) {}
+
+AudioEncoder::~AudioEncoder() = default;
+
+int32_t AudioEncoder::encodeQueueSize() {
+  return 0;
+}
+
+void AudioEncoder::encode(AudioFrame* frame, ExceptionState&) {}
+
+void AudioEncoder::configure(const AudioEncoderConfig*, ExceptionState&) {}
+
+ScriptPromise AudioEncoder::flush(ExceptionState&) {
+  return ScriptPromise();
+}
+
+void AudioEncoder::reset(ExceptionState&) {}
+
+void AudioEncoder::close(ExceptionState&) {}
+
+String AudioEncoder::state() {
+  return "";
+}
+
+void AudioEncoder::ContextDestroyed() {}
+
+void AudioEncoder::Trace(Visitor* visitor) const {
+  ScriptWrappable::Trace(visitor);
+  ExecutionContextLifecycleObserver::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder.h b/third_party/blink/renderer/modules/webcodecs/audio_encoder.h
new file mode 100644
index 0000000..c44a2e4
--- /dev/null
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_AUDIO_ENCODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_AUDIO_ENCODER_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_codec_state.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk_output_callback.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_output_callback.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_webcodecs_error_callback.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/modules/webcodecs/audio_frame.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+
+namespace blink {
+
+class ExceptionState;
+class AudioEncoderConfig;
+class AudioEncoderInit;
+
+class MODULES_EXPORT AudioEncoder final
+    : public ScriptWrappable,
+      public ActiveScriptWrappable<AudioEncoder>,
+      public ExecutionContextLifecycleObserver {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static AudioEncoder* Create(ScriptState*,
+                              const AudioEncoderInit*,
+                              ExceptionState&);
+  AudioEncoder(ScriptState*, const AudioEncoderInit*, ExceptionState&);
+  ~AudioEncoder() override;
+
+  // audio_encoder.idl implementation.
+  int32_t encodeQueueSize();
+
+  void encode(AudioFrame* frame, ExceptionState&);
+
+  void configure(const AudioEncoderConfig*, ExceptionState&);
+
+  ScriptPromise flush(ExceptionState&);
+
+  void reset(ExceptionState&);
+
+  void close(ExceptionState&);
+
+  String state();
+
+  // ExecutionContextLifecycleObserver override.
+  void ContextDestroyed() override;
+
+  // ScriptWrappable override.
+  bool HasPendingActivity() const override { return false; }
+
+  // GarbageCollected override.
+  void Trace(Visitor*) const override;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_AUDIO_ENCODER_H_
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder.idl b/third_party/blink/renderer/modules/webcodecs/audio_encoder.idl
new file mode 100644
index 0000000..cfec86c3
--- /dev/null
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder.idl
@@ -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.
+
+// https://github.com/WICG/web-codecs
+
+[
+    Exposed=(Window,DedicatedWorker),
+    RuntimeEnabled=WebCodecs,
+    ActiveScriptWrappable
+] interface AudioEncoder {
+    [CallWith=ScriptState, RaisesException, MeasureAs=WebCodecsAudioEncoder]
+    constructor(AudioEncoderInit init);
+
+    // The number of pending encode requests. This does not include requests
+    // that have been sent to the underlying codec.
+    readonly attribute long encodeQueueSize;
+
+    // Enqueues a control message to configure the audio encoder for encoding
+    // audio frames as described by config.
+    [RaisesException]
+    void configure(AudioEncoderConfig config);
+
+    // Enqueues a request to encode a frame.
+    // Results of the encoding (EncodedAudioChunk) are returned via
+    // the output callback provided in configure().
+    [RaisesException]
+    void encode(AudioFrame frame);
+
+
+    // Enqueues a request to produce outputs for all already encoded frames.
+    // Resolved after emitting outputs for all previously encoded frames.
+    [RaisesException]
+    Promise<void> flush();
+
+    // Discard all pending work and current encoder configuration.
+    //
+    // Output for earlier encoding requests will not be emitted.
+    // The next encoded frame will be a keyframe.
+    // Requires configure() to be call to set configuration once again.
+    [RaisesException]
+    void reset();
+
+    // Enqueues a request to shut down the encoder and free its resources.
+    [RaisesException]
+    void close();
+
+    // Which state the encoder is in, indicating which methods can be called.
+    readonly attribute CodecState state;
+};
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder_config.idl b/third_party/blink/renderer/modules/webcodecs/audio_encoder_config.idl
new file mode 100644
index 0000000..c832f43
--- /dev/null
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder_config.idl
@@ -0,0 +1,16 @@
+// 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.
+
+// https://github.com/WICG/web-codecs
+
+dictionary AudioEncoderConfig {
+  // TODO(chcunningham): reference spec registry.
+  required DOMString codec;
+
+  // Rate of samples per second. 44100, 48000, etc.
+  required unsigned long sampleRate;
+
+  // 1, 2, etc.
+  required unsigned long numberOfChannels;
+};
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder_init.idl b/third_party/blink/renderer/modules/webcodecs/audio_encoder_init.idl
new file mode 100644
index 0000000..cc6b93d1
--- /dev/null
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder_init.idl
@@ -0,0 +1,10 @@
+// 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.
+
+// https://github.com/WICG/web-codecs
+
+dictionary AudioEncoderInit {
+  required EncodedAudioChunkOutputCallback output;
+  required WebCodecsErrorCallback error;
+};
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
index f98282c..5a7af56e 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
@@ -269,9 +269,9 @@
     decoder_ = Traits::CreateDecoder(*ExecutionContext::From(script_state_),
                                      gpu_factories_, logger_->log());
     if (!decoder_) {
-      Shutdown(logger_->MakeException(
-          "Configuration error: Could not create decoder.",
-          media::StatusCode::kDecoderCreationFailed));
+      Shutdown(
+          logger_->MakeException("Internal error: Could not create decoder.",
+                                 media::StatusCode::kDecoderCreationFailed));
       return false;
     }
 
@@ -468,7 +468,9 @@
   DCHECK_EQ(pending_request_->type, Request::Type::kConfigure);
 
   if (!status.is_ok()) {
-    Shutdown(logger_->MakeException("Configuration error.", status));
+    Shutdown(logger_->MakeException(
+        "Internal error: failed to flush out frames from previous config.",
+        status));
     return;
   }
 
@@ -490,7 +492,13 @@
   DCHECK_EQ(pending_request_->type, Request::Type::kConfigure);
 
   if (!status.is_ok()) {
-    Shutdown(logger_->MakeException("Decoder initialization error.", status));
+    std::string error_message = "Decoder initialization error.";
+    if (status.code() == media::StatusCode::kDecoderUnsupportedConfig) {
+      error_message =
+          "Unsupported configuration. Check isConfigSupported() prior to "
+          "calling configure().";
+    }
+    Shutdown(logger_->MakeException(error_message, status));
     return;
   }
 
diff --git a/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk_output_callback.idl b/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk_output_callback.idl
new file mode 100644
index 0000000..0b55be9
--- /dev/null
+++ b/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk_output_callback.idl
@@ -0,0 +1,8 @@
+// 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.
+
+// https://github.com/WICG/web-codecs
+
+[RuntimeEnabled=WebCodecs]
+callback EncodedAudioChunkOutputCallback = void(EncodedAudioChunk output);
diff --git a/third_party/blink/renderer/modules/webcodecs/idls.gni b/third_party/blink/renderer/modules/webcodecs/idls.gni
index 808d696..7acc6fa3 100644
--- a/third_party/blink/renderer/modules/webcodecs/idls.gni
+++ b/third_party/blink/renderer/modules/webcodecs/idls.gni
@@ -4,6 +4,7 @@
 
 modules_idl_files = [
   "audio_decoder.idl",
+  "audio_encoder.idl",
   "audio_frame.idl",
   "encoded_video_chunk.idl",
   "encoded_audio_chunk.idl",
@@ -17,6 +18,7 @@
 
 modules_callback_function_idl_files = [
   "audio_frame_output_callback.idl",
+  "encoded_audio_chunk_output_callback.idl",
   "video_encoder_output_callback.idl",
   "video_frame_output_callback.idl",
   "webcodecs_error_callback.idl",
@@ -25,6 +27,8 @@
 modules_dictionary_idl_files = [
   "audio_decoder_config.idl",
   "audio_decoder_init.idl",
+  "audio_encoder_config.idl",
+  "audio_encoder_init.idl",
   "avc_encoder_config.idl",
   "encoded_video_chunk_init.idl",
   "encoded_audio_chunk_init.idl",
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.idl b/third_party/blink/renderer/modules/webcodecs/video_encoder.idl
index e8f2683..94ee06f 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_encoder.idl
+++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.idl
@@ -12,26 +12,18 @@
     [CallWith=ScriptState, RaisesException, MeasureAs=WebCodecsVideoEncoder]
     constructor(VideoEncoderInit init);
 
-    // The number of pending encode requests. This does not include requests that
-    // have been sent to the underlying codec.
-    //
-    // Applications can minimize underflow by enqueueing encode requests until
-    // |encodeQueueSize| is greater than a constant.
+    // The number of pending encode requests. This does not include requests
+    // that have been sent to the underlying codec.
     readonly attribute long encodeQueueSize;
 
-    // Performs original configuration of the encoder.
-    // Resolved after configuration is done. It should be called only
-    // once per encoder instance, before calling any other methods.
+    // Enqueues a control message to configure the video encoder for encoding
+    // frames as described by config.
     [RaisesException]
     void configure(VideoEncoderConfig config);
 
     // Enqueues a request to encode a frame.
     // Results of the encoding (EncodedVideoChunk) are returned via
     // the output callback provided in configure().
-    // Resolved when encoded processed the given frame.
-    // The output callback can be called before or after the result is resolved.
-    // Several encoded requests can be resolved before even a single output
-    // is produced.
     [RaisesException]
     void encode(VideoFrame frame,
                          optional VideoEncoderEncodeOptions options = {});
@@ -46,13 +38,11 @@
     //
     // Output for earlier encoding requests will not be emitted.
     // The next encoded frame will be a keyframe.
-    // Required a configure() to be call to set configuration once again.
+    // Requires configure() to be call to set configuration once again.
     [RaisesException]
     void reset();
 
     // Enqueues a request to shut down the encoder and free its resources.
-    // Resolved after all resources are released and all following requests
-    // rejected.
     [RaisesException]
     void close();
 
diff --git a/third_party/blink/renderer/modules/xr/xr_render_state.cc b/third_party/blink/renderer/modules/xr/xr_render_state.cc
index 0691efd67..9299d921 100644
--- a/third_party/blink/renderer/modules/xr/xr_render_state.cc
+++ b/third_party/blink/renderer/modules/xr/xr_render_state.cc
@@ -63,7 +63,6 @@
 
 void XRRenderState::Trace(Visitor* visitor) const {
   visitor->Trace(base_layer_);
-  visitor->Trace(inline_vertical_fov_);
   ScriptWrappable::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/platform/bindings/script_state.cc b/third_party/blink/renderer/platform/bindings/script_state.cc
index 982aa765..d9a9ad1 100644
--- a/third_party/blink/renderer/platform/bindings/script_state.cc
+++ b/third_party/blink/renderer/platform/bindings/script_state.cc
@@ -17,7 +17,7 @@
     : isolate_(context->GetIsolate()),
       context_(isolate_, context),
       world_(std::move(world)),
-      per_context_data_(std::make_unique<V8PerContextData>(context)),
+      per_context_data_(MakeGarbageCollected<V8PerContextData>(context)),
       reference_from_v8_context_(PERSISTENT_FROM_HERE, this) {
   DCHECK(world_);
   context_.SetWeak(this, &OnV8ContextCollectedCallback);
@@ -34,12 +34,17 @@
   RendererResourceCoordinator::Get()->OnScriptStateDestroyed(this);
 }
 
+void ScriptState::Trace(Visitor* visitor) const {
+  visitor->Trace(per_context_data_);
+}
+
 void ScriptState::DetachGlobalObject() {
   DCHECK(!context_.IsEmpty());
   GetContext()->DetachGlobal();
 }
 
 void ScriptState::DisposePerContextData() {
+  per_context_data_->Dispose();
   per_context_data_ = nullptr;
   InstanceCounters::IncrementCounter(
       InstanceCounters::kDetachedScriptStateCounter);
diff --git a/third_party/blink/renderer/platform/bindings/script_state.h b/third_party/blink/renderer/platform/bindings/script_state.h
index 2a1897e..738b754 100644
--- a/third_party/blink/renderer/platform/bindings/script_state.h
+++ b/third_party/blink/renderer/platform/bindings/script_state.h
@@ -130,7 +130,7 @@
               ExecutionContext* execution_context);
   ~ScriptState();
 
-  void Trace(Visitor*) const {}
+  void Trace(Visitor*) const;
 
   static ScriptState* Current(v8::Isolate* isolate) {  // DEPRECATED
     return From(isolate->GetCurrentContext());
@@ -190,7 +190,7 @@
   }
   void DetachGlobalObject();
 
-  V8PerContextData* PerContextData() const { return per_context_data_.get(); }
+  V8PerContextData* PerContextData() const { return per_context_data_.Get(); }
   void DisposePerContextData();
 
   // This method is expected to be called only from
@@ -217,7 +217,7 @@
   // So you must explicitly clear the std::unique_ptr by calling
   // disposePerContextData() once you no longer need V8PerContextData.
   // Otherwise, the v8::Context will leak.
-  std::unique_ptr<V8PerContextData> per_context_data_;
+  Member<V8PerContextData> per_context_data_;
 
   // v8::Context has an internal field to this ScriptState* as a raw pointer,
   // which is out of scope of Blink GC, but it must be a strong reference.  We
diff --git a/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc b/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc
index 5d02a38..8d92ce43 100644
--- a/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc
+++ b/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc
@@ -46,21 +46,15 @@
 
 namespace {
 
-constexpr char kWrapperBoilerplatesLabel[] =
-    "V8PerContextData::wrapper_boilerplates_";
-constexpr char kConstructorMapLabel[] = "V8PerContextData::constructor_map_";
 constexpr char kContextLabel[] = "V8PerContextData::context_";
 
 }  // namespace
 
 V8PerContextData::V8PerContextData(v8::Local<v8::Context> context)
     : isolate_(context->GetIsolate()),
-      wrapper_boilerplates_(isolate_, kWrapperBoilerplatesLabel),
-      constructor_map_(isolate_, kConstructorMapLabel),
       context_holder_(std::make_unique<gin::ContextHolder>(isolate_)),
       context_(isolate_, context),
-      activity_logger_(nullptr),
-      data_map_(MakeGarbageCollected<DataMap>()) {
+      activity_logger_(nullptr) {
   context_holder_->SetContext(context);
   context_.Get().AnnotateStrongRetainer(kContextLabel);
 
@@ -77,24 +71,47 @@
   }
 }
 
+void V8PerContextData::Dispose() {
+  // These fields are not traced by the garbage collector and could contain
+  // strong GC roots that prevent `this` from otherwise being collected, so
+  // explicitly break any potential cycles in the ownership graph now.
+  context_holder_ = nullptr;
+  if (!context_.IsEmpty())
+    context_.SetPhantom();
+  if (!private_custom_element_definition_id_.IsEmpty())
+    private_custom_element_definition_id_.SetPhantom();
+}
+
+void V8PerContextData::Trace(Visitor* visitor) const {
+  visitor->Trace(wrapper_boilerplates_);
+  visitor->Trace(constructor_map_);
+  visitor->Trace(data_map_);
+}
+
 V8PerContextData* V8PerContextData::From(v8::Local<v8::Context> context) {
   return ScriptState::From(context)->PerContextData();
 }
 
 v8::Local<v8::Object> V8PerContextData::CreateWrapperFromCacheSlowCase(
     const WrapperTypeInfo* type) {
+  DCHECK(!wrapper_boilerplates_.Contains(type));
   v8::Context::Scope scope(GetContext());
   v8::Local<v8::Function> interface_object = ConstructorForType(type);
   CHECK(!interface_object.IsEmpty());
   v8::Local<v8::Object> instance_template =
       V8ObjectConstructor::NewInstance(isolate_, interface_object)
           .ToLocalChecked();
-  wrapper_boilerplates_.Set(type, instance_template);
+
+  TraceWrapperV8Reference<v8::Object> traced_wrapper;
+  traced_wrapper.Set(isolate_, instance_template);
+  wrapper_boilerplates_.insert(type, traced_wrapper);
+
   return instance_template->Clone();
 }
 
 v8::Local<v8::Function> V8PerContextData::ConstructorForTypeSlowCase(
     const WrapperTypeInfo* type) {
+  DCHECK(!constructor_map_.Contains(type));
   v8::Local<v8::Context> context = GetContext();
   v8::Context::Scope scope(context);
 
@@ -109,7 +126,10 @@
           type, context, world, isolate_, parent_interface_object,
           V8ObjectConstructor::CreationMode::kInstallConditionalFeatures);
 
-  constructor_map_.Set(type, interface_object);
+  TraceWrapperV8Reference<v8::Function> traced_wrapper;
+  traced_wrapper.Set(isolate_, interface_object);
+  constructor_map_.insert(type, traced_wrapper);
+
   return interface_object;
 }
 
@@ -130,26 +150,28 @@
     const WrapperTypeInfo* type,
     v8::Local<v8::Object>* prototype_object,
     v8::Local<v8::Function>* interface_object) {
-  *interface_object = constructor_map_.Get(type);
-  if (interface_object->IsEmpty()) {
-    *prototype_object = v8::Local<v8::Object>();
+  auto it = constructor_map_.find(type);
+  if (it == constructor_map_.end()) {
+    interface_object->Clear();
+    prototype_object->Clear();
     return false;
   }
+  *interface_object = it->value.NewLocal(isolate_);
   *prototype_object = PrototypeForType(type);
   DCHECK(!prototype_object->IsEmpty());
   return true;
 }
 
 void V8PerContextData::AddData(const char* key, Data* data) {
-  data_map_->Set(key, data);
+  data_map_.Set(key, data);
 }
 
 void V8PerContextData::ClearData(const char* key) {
-  data_map_->erase(key);
+  data_map_.erase(key);
 }
 
 V8PerContextData::Data* V8PerContextData::GetData(const char* key) {
-  return data_map_->at(key);
+  return data_map_.at(key);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/bindings/v8_per_context_data.h b/third_party/blink/renderer/platform/bindings/v8_per_context_data.h
index 6c7db2f7..bf10ab6 100644
--- a/third_party/blink/renderer/platform/bindings/v8_per_context_data.h
+++ b/third_party/blink/renderer/platform/bindings/v8_per_context_data.h
@@ -36,11 +36,11 @@
 #include "gin/public/context_holder.h"
 #include "gin/public/gin_embedders.h"
 #include "third_party/blink/renderer/platform/bindings/scoped_persistent.h"
-#include "third_party/blink/renderer/platform/bindings/v8_global_value_map.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
@@ -55,9 +55,8 @@
 
 // Used to hold data that is associated with a single v8::Context object, and
 // has a 1:1 relationship with v8::Context.
-class PLATFORM_EXPORT V8PerContextData final {
-  USING_FAST_MALLOC(V8PerContextData);
-
+class PLATFORM_EXPORT V8PerContextData final
+    : public GarbageCollected<V8PerContextData> {
  public:
   explicit V8PerContextData(v8::Local<v8::Context>);
 
@@ -65,23 +64,27 @@
 
   ~V8PerContextData();
 
+  void Trace(Visitor* visitor) const;
+  void Dispose();
+
   v8::Local<v8::Context> GetContext() { return context_.NewLocal(isolate_); }
 
   // To create JS Wrapper objects, we create a cache of a 'boiler plate'
   // object, and then simply Clone that object each time we need a new one.
   // This is faster than going through the full object creation process.
   v8::Local<v8::Object> CreateWrapperFromCache(const WrapperTypeInfo* type) {
-    v8::Local<v8::Object> boilerplate = wrapper_boilerplates_.Get(type);
-    return !boilerplate.IsEmpty() ? boilerplate->Clone()
-                                  : CreateWrapperFromCacheSlowCase(type);
+    auto it = wrapper_boilerplates_.find(type);
+    return it != wrapper_boilerplates_.end()
+               ? it->value.Get()->Clone()
+               : CreateWrapperFromCacheSlowCase(type);
   }
 
   // Returns the interface object that is appropriately initialized (e.g.
   // context-dependent properties are installed).
   v8::Local<v8::Function> ConstructorForType(const WrapperTypeInfo* type) {
-    v8::Local<v8::Function> interface_object = constructor_map_.Get(type);
-    return (!interface_object.IsEmpty()) ? interface_object
-                                         : ConstructorForTypeSlowCase(type);
+    auto it = constructor_map_.find(type);
+    return it != constructor_map_.end() ? it->value.NewLocal(isolate_)
+                                        : ConstructorForTypeSlowCase(type);
   }
 
   v8::Local<v8::Object> PrototypeForType(const WrapperTypeInfo*);
@@ -127,13 +130,15 @@
   v8::Local<v8::Object> CreateWrapperFromCacheSlowCase(const WrapperTypeInfo*);
   v8::Local<v8::Function> ConstructorForTypeSlowCase(const WrapperTypeInfo*);
 
-  v8::Isolate* isolate_;
+  v8::Isolate* const isolate_;
 
   // For each possible type of wrapper, we keep a boilerplate object.
   // The boilerplate is used to create additional wrappers of the same type.
-  V8GlobalValueMap<const WrapperTypeInfo*, v8::Object> wrapper_boilerplates_;
+  HeapHashMap<const WrapperTypeInfo*, TraceWrapperV8Reference<v8::Object>>
+      wrapper_boilerplates_;
 
-  V8GlobalValueMap<const WrapperTypeInfo*, v8::Function> constructor_map_;
+  HeapHashMap<const WrapperTypeInfo*, TraceWrapperV8Reference<v8::Function>>
+      constructor_map_;
 
   std::unique_ptr<gin::ContextHolder> context_holder_;
 
@@ -145,7 +150,7 @@
   V8DOMActivityLogger* activity_logger_;
 
   using DataMap = HeapHashMap<const char*, Member<Data>>;
-  Persistent<DataMap> data_map_;
+  DataMap data_map_;
 
   DISALLOW_COPY_AND_ASSIGN(V8PerContextData);
 };
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc
index 76b953d1..3e7a6a0 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc
@@ -50,11 +50,18 @@
       .IsAllSpecialCharacters<IsBreakableSpace>();
 }
 
-bool ShouldHyphenate(const String& text, unsigned start, unsigned end) {
+bool ShouldHyphenate(const String& text,
+                     unsigned word_start,
+                     unsigned word_end,
+                     unsigned line_start) {
+  // If this is the first word in this line, allow to hyphenate. Otherwise the
+  // word will overflow.
+  if (word_start <= line_start)
+    return true;
   // Do not hyphenate the last word in a paragraph, except when it's a single
   // word paragraph.
-  if (IsAllSpaces(text, end, text.length()))
-    return IsAllSpaces(text, 0, start);
+  if (IsAllSpaces(text, word_end, text.length()))
+    return IsAllSpaces(text, 0, word_start);
   return true;
 }
 
@@ -141,7 +148,7 @@
          LazyLineBreakIterator::IsBreakableSpace(text[word_start]))
     word_start++;
   if (offset >= word_start &&
-      ShouldHyphenate(text, previous_break_opportunity, word_end)) {
+      ShouldHyphenate(text, previous_break_opportunity, word_end, start)) {
     unsigned prefix_length = Hyphenate(offset, word_start, word_end, backwards);
     if (prefix_length)
       return {word_start + prefix_length, true};
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
index 4450500d..9839dd8 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
@@ -158,7 +158,12 @@
 
   cc::PaintFlags dark_mode_flags = flags;
   if (flags.HasShader()) {
-    dark_mode_flags.setColorFilter(immutable_.color_filter->ToSkColorFilter());
+    PaintShader::Type shader_type = flags.getShader()->shader_type();
+    if (shader_type != PaintShader::Type::kImage &&
+        shader_type != PaintShader::Type::kPaintRecord) {
+      dark_mode_flags.setColorFilter(
+          immutable_.color_filter->ToSkColorFilter());
+    }
   } else if (ShouldApplyToColor(flags.getColor(), role)) {
     dark_mode_flags.setColor(inverted_color_cache_->GetInvertedColor(
         immutable_.color_filter.get(), flags.getColor()));
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index 4fa9a11..a4b57960 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -223,32 +223,6 @@
   return dom_node_id_;
 }
 
-void GraphicsContext::SetShadow(
-    const FloatSize& offset,
-    float blur,
-    const Color& color,
-    DrawLooperBuilder::ShadowTransformMode shadow_transform_mode,
-    DrawLooperBuilder::ShadowAlphaMode shadow_alpha_mode,
-    ShadowMode shadow_mode) {
-  DrawLooperBuilder draw_looper_builder;
-  if (!color.Alpha()) {
-    // When shadow-only but there is no shadow, we use an empty draw looper
-    // to disable rendering of the source primitive.  When not shadow-only, we
-    // clear the looper.
-    SetDrawLooper(shadow_mode != kDrawShadowOnly
-                      ? nullptr
-                      : draw_looper_builder.DetachDrawLooper());
-    return;
-  }
-
-  draw_looper_builder.AddShadow(offset, blur, color, shadow_transform_mode,
-                                shadow_alpha_mode);
-  if (shadow_mode == kDrawShadowAndForeground) {
-    draw_looper_builder.AddUnmodifiedContent();
-  }
-  SetDrawLooper(draw_looper_builder.DetachDrawLooper());
-}
-
 void GraphicsContext::SetDrawLooper(sk_sp<SkDrawLooper> draw_looper) {
   MutableState()->SetDrawLooper(std::move(draw_looper));
 }
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.h b/third_party/blink/renderer/platform/graphics/graphics_context.h
index 809469b1..f7c07b9 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.h
@@ -348,15 +348,6 @@
   // not necessarily non-empty), even when the context is disabled.
   sk_sp<PaintRecord> EndRecording();
 
-  void SetShadow(const FloatSize& offset,
-                 float blur,
-                 const Color&,
-                 DrawLooperBuilder::ShadowTransformMode =
-                     DrawLooperBuilder::kShadowRespectsTransforms,
-                 DrawLooperBuilder::ShadowAlphaMode =
-                     DrawLooperBuilder::kShadowRespectsAlpha,
-                 ShadowMode = kDrawShadowAndForeground);
-
   void SetDrawLooper(sk_sp<SkDrawLooper>);
 
   void DrawFocusRing(const Vector<IntRect>&,
diff --git a/third_party/blink/renderer/platform/graphics/graphics_types.h b/third_party/blink/renderer/platform/graphics/graphics_types.h
index 3890907..c7258df 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_types.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_types.h
@@ -134,14 +134,6 @@
   kOrderingBarrier,
 };
 
-// TODO(junov): crbug.com/453113 Relocate ShadowMode to
-// CanvasRenderingContext2DState.h once GraphicsContext no longer uses it.
-enum ShadowMode {
-  kDrawShadowAndForeground,
-  kDrawShadowOnly,
-  kDrawForegroundOnly
-};
-
 enum AntiAliasingMode { kNotAntiAliased, kAntiAliased };
 
 enum GradientSpreadMethod {
diff --git a/third_party/blink/renderer/platform/heap/collection_support/heap_vector.h b/third_party/blink/renderer/platform/heap/collection_support/heap_vector.h
index b0f439e..71ea2a6 100644
--- a/third_party/blink/renderer/platform/heap/collection_support/heap_vector.h
+++ b/third_party/blink/renderer/platform/heap/collection_support/heap_vector.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_VECTOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_VECTOR_H_
 
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator_impl.h"
 #include "third_party/blink/renderer/platform/wtf/type_traits.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -12,11 +13,49 @@
 namespace blink {
 
 template <typename T, wtf_size_t inlineCapacity = 0>
-class HeapVector final : public Vector<T, inlineCapacity, HeapAllocator> {
-  IS_GARBAGE_COLLECTED_CONTAINER_TYPE();
+class HeapVector final : public GarbageCollected<HeapVector<T, inlineCapacity>>,
+                         public Vector<T, inlineCapacity, HeapAllocator> {
   DISALLOW_NEW();
 
-  static void CheckType() {
+ public:
+  HeapVector() = default;
+
+  explicit HeapVector(wtf_size_t size)
+      : Vector<T, inlineCapacity, HeapAllocator>(size) {}
+
+  HeapVector(wtf_size_t size, const T& val)
+      : Vector<T, inlineCapacity, HeapAllocator>(size, val) {}
+
+  template <wtf_size_t otherCapacity>
+  HeapVector(const HeapVector<T, otherCapacity>& other)  // NOLINT
+      : Vector<T, inlineCapacity, HeapAllocator>(other) {}
+
+  HeapVector(const HeapVector& other)
+      : Vector<T, inlineCapacity, HeapAllocator>(other) {}
+
+  HeapVector& operator=(const HeapVector& other) {
+    Vector<T, inlineCapacity, HeapAllocator>::operator=(other);
+    return *this;
+  }
+
+  HeapVector(HeapVector&& other) noexcept
+      : Vector<T, inlineCapacity, HeapAllocator>(std::move(other)) {}
+
+  HeapVector& operator=(HeapVector&& other) noexcept {
+    Vector<T, inlineCapacity, HeapAllocator>::operator=(std::move(other));
+    return *this;
+  }
+
+  HeapVector(std::initializer_list<T> elements)
+      : Vector<T, inlineCapacity, HeapAllocator>(elements) {}
+
+  void Trace(Visitor* visitor) const {
+    CheckType();
+    Vector<T, inlineCapacity, HeapAllocator>::Trace(visitor);
+  }
+
+ private:
+  static constexpr void CheckType() {
     static_assert(
         std::is_trivially_destructible<HeapVector>::value || inlineCapacity,
         "HeapVector must be trivially destructible.");
@@ -28,60 +67,6 @@
     static_assert(WTF::IsTraceableInCollectionTrait<VectorTraits<T>>::value,
                   "Type must be traceable in collection");
   }
-
- public:
-  template <typename>
-  static void* AllocateObject(size_t size) {
-    // On-heap HeapVectors generally should not have inline capacity, but it is
-    // hard to avoid when using a type alias. Hence we only disallow the
-    // VectorTraits<T>::kNeedsDestruction case for now.
-    static_assert(inlineCapacity == 0 || !VectorTraits<T>::kNeedsDestruction,
-                  "on-heap HeapVector<> should not have an inline capacity");
-    return ThreadHeap::Allocate<HeapVector<T, inlineCapacity>>(size);
-  }
-
-  HeapVector() { CheckType(); }
-
-  explicit HeapVector(wtf_size_t size)
-      : Vector<T, inlineCapacity, HeapAllocator>(size) {
-    CheckType();
-  }
-
-  HeapVector(wtf_size_t size, const T& val)
-      : Vector<T, inlineCapacity, HeapAllocator>(size, val) {
-    CheckType();
-  }
-
-  template <wtf_size_t otherCapacity>
-  HeapVector(const HeapVector<T, otherCapacity>& other)  // NOLINT
-      : Vector<T, inlineCapacity, HeapAllocator>(other) {
-    CheckType();
-  }
-
-  HeapVector(const HeapVector& other)
-      : Vector<T, inlineCapacity, HeapAllocator>(other) {
-    CheckType();
-  }
-
-  HeapVector& operator=(const HeapVector& other) {
-    Vector<T, inlineCapacity, HeapAllocator>::operator=(other);
-    return *this;
-  }
-
-  HeapVector(HeapVector&& other) noexcept
-      : Vector<T, inlineCapacity, HeapAllocator>(std::move(other)) {
-    CheckType();
-  }
-
-  HeapVector& operator=(HeapVector&& other) noexcept {
-    Vector<T, inlineCapacity, HeapAllocator>::operator=(std::move(other));
-    return *this;
-  }
-
-  HeapVector(std::initializer_list<T> elements)
-      : Vector<T, inlineCapacity, HeapAllocator>(elements) {
-    CheckType();
-  }
 };
 
 template <typename T, wtf_size_t inlineCapacity>
diff --git a/third_party/blink/renderer/platform/heap/impl/trace_traits.h b/third_party/blink/renderer/platform/heap/impl/trace_traits.h
index 44ae3c94..86d19b7 100644
--- a/third_party/blink/renderer/platform/heap/impl/trace_traits.h
+++ b/third_party/blink/renderer/platform/heap/impl/trace_traits.h
@@ -186,22 +186,6 @@
   }
 };
 
-// While using base::Optional<T> with garbage-collected types is generally
-// disallowed by the OptionalGarbageCollected check in blink_gc_plugin,
-// garbage-collected containers such as HeapVector are allowed and need to be
-// traced.
-template <typename T>
-struct TraceTrait<base::Optional<T>> {
-  STATIC_ONLY(TraceTrait);
-
- public:
-  static void Trace(Visitor* visitor, const base::Optional<T>* optional) {
-    if (*optional != base::nullopt) {
-      TraceIfNeeded<T>::Trace(visitor, optional->value());
-    }
-  }
-};
-
 // Helper for processing ephemerons represented as KeyValuePair. Reorders
 // parameters if needed so that KeyType is always weak.
 template <typename _KeyType,
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index d7c1ee70..b9e9369c 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -288,6 +288,52 @@
     }
   }
 
+  // Helper for posting several tasks to specific queues. |task_descriptor| is a
+  // string with space delimited task identifiers. The first letter of each task
+  // identifier specifies the task queue:
+  // - 'L': Loading task queue
+  // - 'T': Throttleable task queue
+  // - 'P': Pausable task queue
+  // - 'U': Unpausable task queue
+  // - 'D': Deferrable task queue
+  void PostTestTasksToQueuesWithTrait(Vector<String>* run_order,
+                                      const String& task_descriptor) {
+    std::istringstream stream(task_descriptor.Utf8());
+    while (!stream.eof()) {
+      std::string task;
+      stream >> task;
+      switch (task[0]) {
+        case 'L':
+          LoadingTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
+              FROM_HERE, base::BindOnce(&AppendToVectorTestTask, run_order,
+                                        String::FromUTF8(task)));
+          break;
+        case 'T':
+          ThrottleableTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
+              FROM_HERE, base::BindOnce(&AppendToVectorTestTask, run_order,
+                                        String::FromUTF8(task)));
+          break;
+        case 'P':
+          PausableTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
+              FROM_HERE, base::BindOnce(&AppendToVectorTestTask, run_order,
+                                        String::FromUTF8(task)));
+          break;
+        case 'U':
+          UnpausableTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
+              FROM_HERE, base::BindOnce(&AppendToVectorTestTask, run_order,
+                                        String::FromUTF8(task)));
+          break;
+        case 'D':
+          DeferrableTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
+              FROM_HERE, base::BindOnce(&AppendToVectorTestTask, run_order,
+                                        String::FromUTF8(task)));
+          break;
+        default:
+          NOTREACHED();
+      }
+    }
+  }
+
   static void ResetForNavigation(FrameSchedulerImpl* frame_scheduler) {
     frame_scheduler->ResetForNavigation();
   }
@@ -489,10 +535,6 @@
   ++*counter;
 }
 
-void RecordQueueName(String name, Vector<String>* tasks) {
-  tasks->push_back(std::move(name));
-}
-
 // Simulate running a task of a particular length by fast forwarding the task
 // environment clock, which is used to determine the wall time of a task.
 void RunTaskOfLength(base::test::TaskEnvironment* task_environment,
@@ -963,27 +1005,7 @@
 
 TEST_F(FrameSchedulerImplTest, PageFreezeWithKeepActive) {
   Vector<String> tasks;
-  LoadingTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&RecordQueueName,
-                     LoadingTaskQueue()->GetTaskQueue()->GetName(), &tasks));
-  ThrottleableTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&RecordQueueName,
-                     ThrottleableTaskQueue()->GetTaskQueue()->GetName(),
-                     &tasks));
-  DeferrableTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&RecordQueueName,
-                     DeferrableTaskQueue()->GetTaskQueue()->GetName(), &tasks));
-  PausableTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&RecordQueueName,
-                     PausableTaskQueue()->GetTaskQueue()->GetName(), &tasks));
-  UnpausableTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&RecordQueueName,
-                     UnpausableTaskQueue()->GetTaskQueue()->GetName(), &tasks));
+  PostTestTasksToQueuesWithTrait(&tasks, "L1 T1 D1 P1 U1");
 
   page_scheduler_->SetKeepActive(true);  // say we have a Service Worker
   page_scheduler_->SetPageVisible(false);
@@ -992,30 +1014,19 @@
   EXPECT_THAT(tasks, UnorderedElementsAre());
   base::RunLoop().RunUntilIdle();
   // Everything runs except throttleable tasks (timers)
-  EXPECT_THAT(tasks,
-              UnorderedElementsAre(
-                  String(LoadingTaskQueue()->GetTaskQueue()->GetName()),
-                  String(DeferrableTaskQueue()->GetTaskQueue()->GetName()),
-                  String(PausableTaskQueue()->GetTaskQueue()->GetName()),
-                  String(UnpausableTaskQueue()->GetTaskQueue()->GetName())));
+  EXPECT_THAT(tasks, UnorderedElementsAre("L1", "D1", "P1", "U1"));
 
   tasks.clear();
-  LoadingTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&RecordQueueName,
-                     LoadingTaskQueue()->GetTaskQueue()->GetName(), &tasks));
+  PostTestTasksToQueuesWithTrait(&tasks, "L1");
 
   EXPECT_THAT(tasks, UnorderedElementsAre());
   base::RunLoop().RunUntilIdle();
   // loading task runs
-  EXPECT_THAT(tasks, UnorderedElementsAre(String(
-                         LoadingTaskQueue()->GetTaskQueue()->GetName())));
+  EXPECT_THAT(tasks, UnorderedElementsAre("L1"));
 
   tasks.clear();
-  LoadingTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&RecordQueueName,
-                     LoadingTaskQueue()->GetTaskQueue()->GetName(), &tasks));
+  PostTestTasksToQueuesWithTrait(&tasks, "L1");
+
   // KeepActive is false when Service Worker stops.
   page_scheduler_->SetKeepActive(false);
   EXPECT_THAT(tasks, UnorderedElementsAre());
@@ -1027,8 +1038,7 @@
   EXPECT_THAT(tasks, UnorderedElementsAre());
   base::RunLoop().RunUntilIdle();
   // loading task runs
-  EXPECT_THAT(tasks, UnorderedElementsAre(String(
-                         LoadingTaskQueue()->GetTaskQueue()->GetName())));
+  EXPECT_THAT(tasks, UnorderedElementsAre("L1"));
 }
 
 class FrameSchedulerImplTestWithUnfreezableLoading
diff --git a/third_party/blink/renderer/platform/scheduler/test/queueing_time_estimator_perf_test.cc b/third_party/blink/renderer/platform/scheduler/test/queueing_time_estimator_perf_test.cc
index eed45db..e69de29 100644
--- a/third_party/blink/renderer/platform/scheduler/test/queueing_time_estimator_perf_test.cc
+++ b/third_party/blink/renderer/platform/scheduler/test/queueing_time_estimator_perf_test.cc
@@ -1,56 +0,0 @@
-// Copyright 2018 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/timer/lap_timer.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/perf/perf_test.h"
-#include "third_party/blink/renderer/platform/scheduler/test/test_queueing_time_estimator_client.h"
-
-namespace blink {
-namespace scheduler {
-
-namespace {
-
-static constexpr base::TimeDelta kTimeLimit =
-    base::TimeDelta::FromMilliseconds(2000);
-static const int kWarmupRuns = 50;
-static const int kCheckInterval = 100;
-
-class QueueingTimeEstimatorTestPerfTest : public testing::Test {
- public:
-  QueueingTimeEstimatorTestPerfTest()
-      : timer_(kWarmupRuns, kTimeLimit, kCheckInterval) {}
-  base::LapTimer timer_;
-  base::TimeTicks time;
-  TestQueueingTimeEstimatorClient client;
-};
-
-}  // namespace
-
-TEST_F(QueueingTimeEstimatorTestPerfTest, DISABLED_ManyTasks) {
-  const int num_tests = 3;
-  base::TimeDelta task_lengths[num_tests] = {
-      base::TimeDelta::FromSeconds(1), base::TimeDelta::FromMilliseconds(50),
-      base::TimeDelta::FromMilliseconds(1)};
-  std::string test_descriptions[num_tests] = {"OneSecondTasks", "FiftyMsTasks",
-                                              "OneMsTasks"};
-  for (int i = 0; i < num_tests; ++i) {
-    QueueingTimeEstimatorForTest estimator(
-        &client, base::TimeDelta::FromSeconds(1), 20, time);
-    time += base::TimeDelta::FromSeconds(1);
-    timer_.Reset();
-    do {
-      estimator.OnExecutionStarted(time);
-      time += task_lengths[i];
-      estimator.OnExecutionStopped(time);
-      timer_.NextLap();
-    } while (!timer_.HasTimeLimitExpired());
-    perf_test::PrintResult("QueueingTimeEstimatorTestPerfTest", "ManyTasks",
-                           test_descriptions[i], timer_.LapsPerSecond(),
-                           "tasks/s", true);
-  }
-}
-
-}  // namespace scheduler
-}  // namespace blink
diff --git a/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.cc b/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.cc
index b9fbc7db..17cd34e 100644
--- a/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.cc
+++ b/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.cc
@@ -24,26 +24,21 @@
 
 namespace {
 
-template <typename CharType>
-StringView SkipLeadingSpaces(const CharType* text,
-                             unsigned length,
-                             unsigned* num_leading_spaces_out) {
-  const CharType* begin = text;
-  const CharType* end = text + length;
-  while (text != end && Character::TreatAsSpace(*text))
-    text++;
-  *num_leading_spaces_out = text - begin;
-  return StringView(text, static_cast<unsigned>(end - text));
+inline bool ShouldSkipLeadingChar(UChar32 c) {
+  if (Character::TreatAsSpace(c))
+    return true;
+  // Strip leading punctuation, defined as OP and QU line breaking classes,
+  // see UAX #14.
+  const int32_t lb = u_getIntPropertyValue(c, UCHAR_LINE_BREAK);
+  if (lb == U_LB_OPEN_PUNCTUATION || lb == U_LB_QUOTATION)
+    return true;
+  return false;
 }
 
-StringView SkipLeadingSpaces(const StringView& text,
-                             unsigned* num_leading_spaces_out) {
-  if (text.Is8Bit()) {
-    return SkipLeadingSpaces(text.Characters8(), text.length(),
-                             num_leading_spaces_out);
-  }
-  return SkipLeadingSpaces(text.Characters16(), text.length(),
-                           num_leading_spaces_out);
+inline bool ShouldSkipTrailingChar(UChar32 c) {
+  // Strip trailing spaces, punctuation and control characters.
+  const int32_t gc_mask = U_GET_GC_MASK(c);
+  return gc_mask & (U_GC_ZS_MASK | U_GC_P_MASK | U_GC_CC_MASK);
 }
 
 }  // namespace
@@ -86,6 +81,44 @@
   return true;
 }
 
+StringView HyphenationMinikin::WordToHyphenate(
+    const StringView& text,
+    unsigned* num_leading_chars_out) {
+  if (text.Is8Bit()) {
+    const LChar* begin = text.Characters8();
+    const LChar* end = begin + text.length();
+    while (begin != end && ShouldSkipLeadingChar(*begin))
+      ++begin;
+    while (begin != end && ShouldSkipTrailingChar(end[-1]))
+      --end;
+    *num_leading_chars_out = begin - text.Characters8();
+    CHECK_GE(end, begin);
+    return StringView(begin, end - begin);
+  }
+  const UChar* begin = text.Characters16();
+  int index = 0;
+  int len = text.length();
+  while (index < len) {
+    int next_index = index;
+    UChar32 c;
+    U16_NEXT(begin, next_index, len, c);
+    if (!ShouldSkipLeadingChar(c))
+      break;
+    index = next_index;
+  }
+  while (index < len) {
+    int prev_len = len;
+    UChar32 c;
+    U16_PREV(begin, index, prev_len, c);
+    if (!ShouldSkipTrailingChar(c))
+      break;
+    len = prev_len;
+  }
+  *num_leading_chars_out = index;
+  CHECK_GE(len, index);
+  return StringView(begin + index, len - index);
+}
+
 Vector<uint8_t> HyphenationMinikin::Hyphenate(const StringView& text) const {
   Vector<uint8_t> result;
   if (text.Is8Bit()) {
@@ -105,11 +138,11 @@
 wtf_size_t HyphenationMinikin::LastHyphenLocation(
     const StringView& text,
     wtf_size_t before_index) const {
-  unsigned num_leading_spaces;
-  StringView word = SkipLeadingSpaces(text, &num_leading_spaces);
-  if (before_index <= num_leading_spaces)
+  unsigned num_leading_chars;
+  StringView word = WordToHyphenate(text, &num_leading_chars);
+  if (before_index <= num_leading_chars)
     return 0;
-  before_index = std::min<wtf_size_t>(before_index - num_leading_spaces,
+  before_index = std::min<wtf_size_t>(before_index - num_leading_chars,
                                       word.length() - kMinimumSuffixLength);
 
   if (word.length() < kMinimumPrefixLength + kMinimumSuffixLength ||
@@ -122,15 +155,15 @@
   static_assert(kMinimumPrefixLength >= 1, "|beforeIndex - 1| can underflow");
   for (wtf_size_t i = before_index - 1; i >= kMinimumPrefixLength; i--) {
     if (result[i])
-      return i + num_leading_spaces;
+      return i + num_leading_chars;
   }
   return 0;
 }
 
 Vector<wtf_size_t, 8> HyphenationMinikin::HyphenLocations(
     const StringView& text) const {
-  unsigned num_leading_spaces;
-  StringView word = SkipLeadingSpaces(text, &num_leading_spaces);
+  unsigned num_leading_chars;
+  StringView word = WordToHyphenate(text, &num_leading_chars);
 
   Vector<wtf_size_t, 8> hyphen_locations;
   if (word.length() < kMinimumPrefixLength + kMinimumSuffixLength)
@@ -142,7 +175,7 @@
   for (wtf_size_t i = word.length() - kMinimumSuffixLength - 1;
        i >= kMinimumPrefixLength; i--) {
     if (result[i])
-      hyphen_locations.push_back(i + num_leading_spaces);
+      hyphen_locations.push_back(i + num_leading_chars);
   }
   return hyphen_locations;
 }
diff --git a/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.h b/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.h
index e3c39c1..4f7bfcfe 100644
--- a/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.h
+++ b/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.h
@@ -28,6 +28,11 @@
                                 wtf_size_t before_index) const override;
   Vector<wtf_size_t, 8> HyphenLocations(const StringView&) const override;
 
+  // Extract the word to hyphenate by skipping leading and trailing spaces and
+  // punctuations.
+  static StringView WordToHyphenate(const StringView& text,
+                                    unsigned* num_leading_chars_out);
+
   static AtomicString MapLocale(const AtomicString& locale);
 
   static scoped_refptr<HyphenationMinikin> FromFileForTesting(base::File);
diff --git a/third_party/blink/renderer/platform/text/hyphenation_test.cc b/third_party/blink/renderer/platform/text/hyphenation_test.cc
index 1b608fa4..3905814b 100644
--- a/third_party/blink/renderer/platform/text/hyphenation_test.cc
+++ b/third_party/blink/renderer/platform/text/hyphenation_test.cc
@@ -61,6 +61,33 @@
     return nullptr;
   }
 #endif
+
+#if defined(USE_MINIKIN_HYPHENATION)
+  void TestWordToHyphenate(StringView text,
+                           StringView expected,
+                           unsigned expected_num_leading_chars) {
+    unsigned num_leading_chars;
+    const StringView result =
+        HyphenationMinikin::WordToHyphenate(text, &num_leading_chars);
+    EXPECT_EQ(result, expected);
+    EXPECT_EQ(num_leading_chars, expected_num_leading_chars);
+
+    // |WordToHyphenate| has separate codepaths for 8 and 16 bits. Make sure
+    // both codepaths return the same results. When a paragraph has at least one
+    // 16 bits character (e.g., Emoji), there will be 8 bits words in 16 bits
+    // string.
+    if (!text.Is8Bit()) {
+      // If |text| is 16 bits, 16 bits codepath is already tested.
+      return;
+    }
+    String text16 = text.ToString();
+    text16.Ensure16Bit();
+    const StringView result16 =
+        HyphenationMinikin::WordToHyphenate(text16, &num_leading_chars);
+    EXPECT_EQ(result16, expected);
+    EXPECT_EQ(num_leading_chars, expected_num_leading_chars);
+  }
+#endif
 };
 
 TEST_F(HyphenationTest, Get) {
@@ -141,6 +168,18 @@
   EXPECT_THAT(actual, ElementsAreArray(locations));
 }
 
+#if defined(USE_MINIKIN_HYPHENATION)
+TEST_F(HyphenationTest, WordToHyphenate) {
+  TestWordToHyphenate("word", "word", 0);
+  TestWordToHyphenate(" word", "word", 1);
+  TestWordToHyphenate("  word", "word", 2);
+  TestWordToHyphenate("  word..", "word", 2);
+  TestWordToHyphenate(" ( word. ).", "word", 3);
+  TestWordToHyphenate(u" ( \u3042. ).", u"\u3042", 3);
+  TestWordToHyphenate(u" ( \U00020B9F. ).", u"\U00020B9F", 3);
+}
+#endif
+
 TEST_F(HyphenationTest, LeadingSpaces) {
   scoped_refptr<Hyphenation> hyphenation = GetHyphenation("en-us");
 #if defined(OS_ANDROID)
@@ -163,12 +202,6 @@
   String only_spaces("   ");
   EXPECT_THAT(hyphenation->HyphenLocations(only_spaces), ElementsAre());
   EXPECT_EQ(0u, hyphenation->LastHyphenLocation(only_spaces, 3));
-
-  // Line breaker is not supposed to pass with trailing spaces.
-  String trailing_space("principle ");
-  EXPECT_THAT(hyphenation->HyphenLocations(trailing_space),
-              testing::AnyOf(ElementsAre(), ElementsAre(6, 4)));
-  EXPECT_EQ(0u, hyphenation->LastHyphenLocation(trailing_space, 10));
 }
 
 TEST_F(HyphenationTest, English) {
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin.cc b/third_party/blink/renderer/platform/weborigin/security_origin.cc
index 6f460ab..b4102e4 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin.cc
@@ -88,6 +88,10 @@
   return KURL(url.GetPath());
 }
 
+// Note: When changing ShouldTreatAsOpaqueOrigin, consider also updating
+// IsValidInput in //url/scheme_host_port.cc (there might be existing
+// differences in behavior between these 2 layers, but we should avoid
+// introducing new differences).
 static bool ShouldTreatAsOpaqueOrigin(const KURL& url) {
   if (!url.IsValid())
     return true;
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
index ef22581..6926a75 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
@@ -745,6 +745,7 @@
 
 TEST_F(SecurityOriginTest, UrlOriginConversions) {
   url::ScopedSchemeRegistryForTests scoped_registry;
+  url::AddNoAccessScheme("no-access");
   url::AddLocalScheme("nonstandard-but-local");
   struct TestCases {
     const char* const url;
@@ -780,6 +781,9 @@
       {"mailto:localhost/", "", "", 0, true},
       {"about:blank", "", "", 0, true},
 
+      // Custom no-access scheme.
+      {"no-access:blah", "", "", 0, true},
+
       // Registered URLs
       {"ftp://example.com/", "ftp", "example.com", 21},
       {"ws://example.com/", "ws", "example.com", 80},
@@ -958,6 +962,23 @@
   }
 }
 
+// See also OriginTest.ConstructFromGURL_OpaqueOrigin and
+// NavigationUrlRewriteBrowserTest.RewriteToNoAccess.
+TEST_F(SecurityOriginTest, StandardNoAccessScheme) {
+  url::ScopedSchemeRegistryForTests scoped_registry;
+  url::AddStandardScheme("std-no-access", url::SCHEME_WITH_HOST);
+  url::AddNoAccessScheme("std-no-access");
+  url::AddStandardScheme("std", url::SCHEME_WITH_HOST);
+
+  scoped_refptr<const SecurityOrigin> custom_standard_noaccess_origin =
+      SecurityOrigin::CreateFromString("std-no-access://host");
+  EXPECT_TRUE(custom_standard_noaccess_origin->IsOpaque());
+
+  scoped_refptr<const SecurityOrigin> custom_standard_origin =
+      SecurityOrigin::CreateFromString("std://host");
+  EXPECT_FALSE(custom_standard_origin->IsOpaque());
+}
+
 TEST_F(SecurityOriginTest, NonStandardScheme) {
   scoped_refptr<const SecurityOrigin> origin =
       SecurityOrigin::CreateFromString("cow://");
diff --git a/third_party/blink/renderer/platform/wtf/allocator/allocator.h b/third_party/blink/renderer/platform/wtf/allocator/allocator.h
index c6b35283..4d25949c 100644
--- a/third_party/blink/renderer/platform/wtf/allocator/allocator.h
+++ b/third_party/blink/renderer/platform/wtf/allocator/allocator.h
@@ -65,15 +65,6 @@
  private:                                   \
   friend class ::WTF::internal::__thisIsHereToForceASemicolonAfterThisMacro
 
-#define IS_GARBAGE_COLLECTED_CONTAINER_TYPE()         \
-  IS_GARBAGE_COLLECTED_TYPE();                        \
-                                                      \
- public:                                              \
-  using IsGarbageCollectedCollectionTypeMarker = int; \
-                                                      \
- private:                                             \
-  friend class ::WTF::internal::__thisIsHereToForceASemicolonAfterThisMacro
-
 #if defined(__clang__)
 #define ANNOTATE_STACK_ALLOCATED \
   __attribute__((annotate("blink_stack_allocated")))
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 2501294..c194dc8 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2295,6 +2295,8 @@
 # css-pseudo-4 opacity not applied to ::first-line
 crbug.com/1085772 external/wpt/css/css-pseudo/first-line-opacity-001.html [ Failure ]
 
+crbug.com/1165324 http/tests/devtools/console/console-preserve-log-x-process-navigation.js [ Pass Failure ]
+
 # motion-1 issues
 crbug.com/641245 external/wpt/css/motion/offset-path-ray-002.html [ Failure ]
 crbug.com/641245 external/wpt/css/motion/offset-path-ray-003.html [ Failure ]
@@ -4954,8 +4956,6 @@
 
 # Works in main table patch, needs percentage sizing fix.
 crbug.com/958381 virtual/layout_ng_table/external/wpt/css/css-tables/height-distribution/percentage-sizing-of-table-cell-children.html [ Failure ]
-crbug.com/958381 virtual/layout_ng_table/external/wpt/css/css-tables/percent-width-ignored-001.tentative.html [ Failure ]
-crbug.com/958381 virtual/layout_ng_table/external/wpt/css/css-tables/percent-width-ignored-003.tentative.html [ Failure ]
 
 # Sheriff 2019-08-14
 crbug.com/993671 [ Win ] http/tests/media/video-frame-size-change.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt b/third_party/blink/web_tests/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt
index 8f94fa3b..eeb594f 100644
--- a/third_party/blink/web_tests/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt
+++ b/third_party/blink/web_tests/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt
@@ -5,6 +5,24 @@
       "bounds": [800, 4021],
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed lime box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FF00",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 100, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt b/third_party/blink/web_tests/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
index 00639270..3160401 100644
--- a/third_party/blink/web_tests/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
+++ b/third_party/blink/web_tests/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
@@ -17,10 +17,10 @@
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='fixed lime box'",
-      "position": [10, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#00FF00"
+      "backgroundColor": "#00FF00",
+      "transform": 2
     }
   ],
   "transforms": [
@@ -32,6 +32,15 @@
         [0, 0, 1, 0],
         [10, 100, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 100, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt b/third_party/blink/web_tests/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
index 21627b6..0f23d6e0 100644
--- a/third_party/blink/web_tests/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
+++ b/third_party/blink/web_tests/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
@@ -15,9 +15,33 @@
       "transform": 1
     },
     {
+      "name": "LayoutView #document",
+      "bounds": [150, 150],
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed lime box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FF00",
+      "transform": 3
+    },
+    {
       "name": "LayoutIFrame (positioned) IFRAME id='iframe2' class='composited'",
       "bounds": [154, 154],
-      "transform": 2
+      "transform": 4
+    },
+    {
+      "name": "LayoutView #document",
+      "bounds": [150, 150],
+      "transform": 5
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed lime box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FF00",
+      "transform": 6
     },
     {
       "name": "LayoutIFrame (positioned) IFRAME id='iframe3'",
@@ -25,6 +49,18 @@
       "bounds": [154, 154]
     },
     {
+      "name": "LayoutView #document",
+      "bounds": [150, 150],
+      "transform": 7
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed lime box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FF00",
+      "transform": 8
+    },
+    {
       "name": "ContentsLayer for Vertical Scrollbar Layer",
       "position": [785, 0],
       "bounds": [15, 600],
@@ -47,8 +83,66 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [12, 32, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [10, 200, 0, 1]
       ]
+    },
+    {
+      "id": 5,
+      "parent": 4,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [2, 2, 0, 1]
+      ]
+    },
+    {
+      "id": 6,
+      "parent": 5,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 7,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [12, 382, 0, 1]
+      ]
+    },
+    {
+      "id": 8,
+      "parent": 7,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/dark-mode/images/image-as-shader-expected.png b/third_party/blink/web_tests/dark-mode/images/image-as-shader-expected.png
new file mode 100644
index 0000000..cab5749
--- /dev/null
+++ b/third_party/blink/web_tests/dark-mode/images/image-as-shader-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/dark-mode/images/image-as-shader.html b/third_party/blink/web_tests/dark-mode/images/image-as-shader.html
new file mode 100644
index 0000000..a7bf474
--- /dev/null
+++ b/third_party/blink/web_tests/dark-mode/images/image-as-shader.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      html {
+        background: #000 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQYV2NgYGD4DwABBAEAcCBlCwAAAABJRU5ErkJggg==) repeat 0 0;
+      }
+      div {
+        background-color: white;
+        width: 100px;
+        height: 100px;
+      }
+    </style>
+  </head>
+  <body>
+    <div></div>
+  </body>
+ </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/hyphens-auto-last-word-001.html b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/hyphens-auto-last-word-001.html
new file mode 100644
index 0000000..273f060
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/hyphens-auto-last-word-001.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>CSS Text: `hyphens: auto` for last word</title>
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#hyphenation">
+<link rel="match" href="reference/hyphens-auto-last-word-001-ref.html">
+<link rel="match" href="reference/hyphens-auto-last-word-001-ref2.html">
+<style>
+div {
+  hyphens: auto;
+  width: 5ch;
+  border: 1px solid blue;
+}
+</style>
+<body lang="en-us">
+  <div style="width: 10ch">Test example</div>
+  <div>example</div>
+  <div>1 example</div>
+  <div>1234 example</div>
+  <div>example 5678</div>
+  <div>1234 example 5678</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/hyphens-punctuation-001.html b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/hyphens-punctuation-001.html
new file mode 100644
index 0000000..feea836
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/hyphens-punctuation-001.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>CSS Text Test: Automatic hyphenation for trailing punctuation characters</title>
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#hyphens-property">
+<link rel="match" href="reference/hyphens-punctuation-001-ref.html">
+<style>
+div {
+  hyphens: auto;
+  width: 5ch;
+  border: 1px solid blue;
+}
+</style>
+<body lang="en-us">
+  <div>00000 example 00000</div>
+  <div>00000 example. 00000</div>
+  <div>00000 (example 00000</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/reference/hyphens-auto-last-word-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/reference/hyphens-auto-last-word-001-ref.html
new file mode 100644
index 0000000..7fe23fe7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/reference/hyphens-auto-last-word-001-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<style>
+div {
+  width: 5ch;
+  border: 1px solid blue;
+}
+</style>
+<body lang="en-us">
+  <div style="width: 10ch">Test ex&shy;am&shy;ple</div>
+  <div>ex&shy;am&shy;ple</div>
+  <div>1 ex&shy;am&shy;ple</div>
+  <div>1234 ex&shy;am&shy;ple</div>
+  <div>ex&shy;am&shy;ple 5678</div>
+  <div>1234 ex&shy;am&shy;ple 5678</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/reference/hyphens-auto-last-word-001-ref2.html b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/reference/hyphens-auto-last-word-001-ref2.html
new file mode 100644
index 0000000..9c9f413
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/reference/hyphens-auto-last-word-001-ref2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<style>
+div {
+  width: 5ch;
+  border: 1px solid blue;
+}
+</style>
+<body lang="en-us">
+  <div style="width: 10ch">Test example</div>
+  <div>ex&shy;am&shy;ple</div>
+  <div>1<br>ex&shy;am&shy;ple</div>
+  <div>1234 ex&shy;am&shy;ple</div>
+  <div>ex&shy;am&shy;ple 5678</div>
+  <div>1234 ex&shy;am&shy;ple 5678</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/reference/hyphens-punctuation-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/reference/hyphens-punctuation-001-ref.html
new file mode 100644
index 0000000..75e23637
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/reference/hyphens-punctuation-001-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<style>
+div {
+  width: 5ch;
+  border: 1px solid blue;
+}
+</style>
+<body lang="en-us">
+  <div>00000 ex&shy;am&shy;ple 00000</div>
+  <div>00000 ex&shy;am&shy;ple. 00000</div>
+  <div>00000 (ex&shy;am&shy;ple 00000</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/multiple-import-maps/basic-expected.txt b/third_party/blink/web_tests/external/wpt/import-maps/multiple-import-maps/basic-expected.txt
new file mode 100644
index 0000000..b56a107
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/multiple-import-maps/basic-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Second import map should be rejected assert_array_equals: lengths differ, expected array ["onerror 2", "log:B1", "log:B2", "log:A3"] length 4, got ["log:B1", "log:B2", "log:A3"] length 3
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/multiple-import-maps/basic.html b/third_party/blink/web_tests/external/wpt/import-maps/multiple-import-maps/basic.html
new file mode 100644
index 0000000..9ab03dd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/multiple-import-maps/basic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const log = [];
+</script>
+<script type="importmap" onerror="log.push('onerror 1')">
+{
+  "imports": {
+    "../resources/log.js?pipe=sub&name=A1": "../resources/log.js?pipe=sub&name=B1",
+    "../resources/log.js?pipe=sub&name=A2": "../resources/log.js?pipe=sub&name=B2"
+  }
+}
+</script>
+<script type="importmap" onerror="log.push('onerror 2')">
+{
+  "imports": {
+    "../resources/log.js?pipe=sub&name=A1": "../resources/log.js?pipe=sub&name=C1",
+    "../resources/log.js?pipe=sub&name=A3": "../resources/log.js?pipe=sub&name=C3"
+  }
+}
+</script>
+<script>
+// Currently the spec doesn't allow multiple import maps, by setting acquiring
+// import maps to false on preparing the first import map.
+promise_test(() => {
+  return import("../resources/log.js?pipe=sub&name=A1")
+    .then(() => import("../resources/log.js?pipe=sub&name=A2"))
+    .then(() => import("../resources/log.js?pipe=sub&name=A3"))
+    .then(() => assert_array_equals(
+                    log,
+                    ["onerror 2", "log:B1", "log:B2", "log:A3"]))
+  },
+  "Second import map should be rejected");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/multiple-import-maps/with-errors-expected.txt b/third_party/blink/web_tests/external/wpt/import-maps/multiple-import-maps/with-errors-expected.txt
new file mode 100644
index 0000000..2ac5edb1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/multiple-import-maps/with-errors-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Second import map should be rejected after an import map with errors assert_array_equals: lengths differ, expected array ["onerror 2", "log:A"] length 2, got ["log:C"] length 1
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/multiple-import-maps/with-errors.html b/third_party/blink/web_tests/external/wpt/import-maps/multiple-import-maps/with-errors.html
new file mode 100644
index 0000000..a93ecaa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/multiple-import-maps/with-errors.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({allow_uncaught_exception : true});
+
+const log = [];
+</script>
+<script type="importmap" onerror="log.push('onerror 1')">
+Parse Error
+</script>
+<script type="importmap" onerror="log.push('onerror 2')">
+{
+  "imports": {
+    "../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=C"
+  }
+}
+</script>
+<script>
+// Currently the spec doesn't allow multiple import maps, by setting acquiring
+// import maps to false on preparing the first import map.
+// Even the first import map has errors and thus Document's import map is not
+// updated, the second import map is still rejected at preparationg.
+promise_test(() => {
+  return import("../resources/log.js?pipe=sub&name=A")
+    .then(() => assert_array_equals(
+                    log,
+                    ["onerror 2", "log:A"]))
+  },
+  "Second import map should be rejected after an import map with errors");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/avoid-prefetching-on-text-plain.html b/third_party/blink/web_tests/external/wpt/preload/avoid-prefetching-on-text-plain.html
index 45564e1..b14b7e4 100644
--- a/third_party/blink/web_tests/external/wpt/preload/avoid-prefetching-on-text-plain.html
+++ b/third_party/blink/web_tests/external/wpt/preload/avoid-prefetching-on-text-plain.html
@@ -16,11 +16,9 @@
             window.addEventListener("message", function(msg) {
                 // Parse the Performance API data passed from the plain text iframe.
                 const entries = JSON.parse(msg.data);
-                const urls = [];
                 const resource_types = [];
                 for (const entry of entries) {
                     resource_types.push(entry.entryType);
-                    urls.push(entry.name);
                 }
                 // If preloading is working correctly, should only see the text document
                 // represented in the performance information. A 'resource' type indicates
@@ -33,8 +31,6 @@
                     }
                 }
                 assert_false(resource_found, "no resources should be present");
-                assert_equals(urls.length, 1);
-                assert_equals(urls[0].endsWith("avoid-prefetching-on-text-plain-inner.html"), true);
                 done();
             });
             prefetchingIframe.addEventListener('load', function() {
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/current-time.html b/third_party/blink/web_tests/external/wpt/scroll-animations/current-time.html
index cbe2d8d6..fe8c64a 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/current-time.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/current-time.html
@@ -463,86 +463,4 @@
   assert_equals(scrollTimeline.currentTime, scrollTimeline.timeRange);
 }, 'currentTime handles startScrollOffset > endScrollOffset correctly');
 
-promise_test(async t => {
-  const scroller = setupScrollTimelineTest();
-  // Set the timeRange such that currentTime maps directly to the value
-  // scrolled. The contents and scroller are square, so it suffices to compute
-  // one edge and use it for all the timelines.
-  const scrollerSize = scroller.scrollHeight - scroller.clientHeight;
-
-  const scrollTimeline = new ScrollTimeline({
-    scrollSource: scroller,
-    timeRange: scrollerSize,
-    orientation: 'block',
-    scrollOffsets: [CSS.px(10), CSS.px(20), CSS.px(40), CSS.px(70), CSS.px(90)],
-  });
-
-  var offset = 0;
-  var w = 1 / 4; // offset weight
-  var p = 0;         // progress within the offset
-
-  scroller.scrollTop = 10;
-  assert_times_equal(
-    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
-    "current time calculation when scroll = " + scroller.scrollTop);
-
-  p = (12 - 10) / (20 - 10);
-  scroller.scrollTop = 12;
-  await waitForNextFrame();
-  assert_times_equal(
-    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
-    "current time calculation when scroll = " + scroller.scrollTop);
-
-  offset = 1;
-  p = 0;
-  scroller.scrollTop = 20;
-  await waitForNextFrame();
-  assert_times_equal(
-    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
-    "current time calculation when scroll = " + scroller.scrollTop);
-
-  p = (35 - 20) / (40 - 20);
-  scroller.scrollTop = 35;
-  await waitForNextFrame();
-  assert_times_equal(
-    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
-    "current time calculation when scroll = " + scroller.scrollTop);
-
-  offset = 2;
-  p = 0;
-  scroller.scrollTop = 40;
-  await waitForNextFrame();
-  assert_times_equal(
-    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
-    "current time calculation when scroll = " + scroller.scrollTop);
-
-  p = (60 - 40) / (70 - 40);
-  scroller.scrollTop = 60;
-  await waitForNextFrame();
-  assert_times_equal(
-    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
-    "current time calculation when scroll = " + scroller.scrollTop);
-
-  offset = 3;
-  p = 0;
-  scroller.scrollTop = 70;
-  await waitForNextFrame();
-  assert_times_equal(
-    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
-    "current time calculation when scroll = " + scroller.scrollTop);
-
-  p = (80 - 70) / (90 - 70);
-  scroller.scrollTop = 80;
-  await waitForNextFrame();
-  assert_times_equal(
-    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
-    "current time calculation when scroll = " + scroller.scrollTop);
-
-  scroller.scrollTop = 90;
-  await waitForNextFrame();
-  assert_times_equal(
-    scrollTimeline.currentTime, scrollerSize,
-    "current time calculation when scroll = " + scroller.scrollTop);
-}, 'currentTime calculations when multiple scroll offsets are specified');
-
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/multiple-scroll-offsets.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/multiple-scroll-offsets.tentative.html
new file mode 100644
index 0000000..026b2e7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/multiple-scroll-offsets.tentative.html
@@ -0,0 +1,143 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>ScrollTimeline current time algorithm</title>
+<link rel="help" href="https://wicg.github.io/scroll-animations/#current-time-algorithm">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+<script src="./resources/scrolltimeline-utils.js"></script>
+
+<body></body>
+
+<script>
+'use strict';
+
+promise_test(async t => {
+  const scroller = setupScrollTimelineTest();
+  const scrollerSize = scroller.scrollHeight - scroller.clientHeight;
+
+  const scrollTimeline = new ScrollTimeline({
+    scrollSource: scroller,
+    timeRange: scrollerSize,
+    orientation: 'block',
+    scrollOffsets: [CSS.px(10), CSS.px(20), CSS.px(40), CSS.px(70), CSS.px(90)],
+  });
+
+  var offset = 0;
+  var w = 1 / 4; // offset weight
+  var p = 0;     // progress within the offset
+
+  scroller.scrollTop = 10;
+  assert_times_equal(
+    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
+    "current time calculation when scroll = " + scroller.scrollTop);
+
+  p = (12 - 10) / (20 - 10);
+  scroller.scrollTop = 12;
+  await waitForNextFrame();
+  assert_times_equal(
+    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
+    "current time calculation when scroll = " + scroller.scrollTop);
+
+  offset = 1;
+  p = 0;
+  scroller.scrollTop = 20;
+  await waitForNextFrame();
+  assert_times_equal(
+    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
+    "current time calculation when scroll = " + scroller.scrollTop);
+
+  p = (35 - 20) / (40 - 20);
+  scroller.scrollTop = 35;
+  await waitForNextFrame();
+  assert_times_equal(
+    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
+    "current time calculation when scroll = " + scroller.scrollTop);
+
+  offset = 2;
+  p = 0;
+  scroller.scrollTop = 40;
+  await waitForNextFrame();
+  assert_times_equal(
+    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
+    "current time calculation when scroll = " + scroller.scrollTop);
+
+  p = (60 - 40) / (70 - 40);
+  scroller.scrollTop = 60;
+  await waitForNextFrame();
+  assert_times_equal(
+    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
+    "current time calculation when scroll = " + scroller.scrollTop);
+
+  offset = 3;
+  p = 0;
+  scroller.scrollTop = 70;
+  await waitForNextFrame();
+  assert_times_equal(
+    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
+    "current time calculation when scroll = " + scroller.scrollTop);
+
+  p = (80 - 70) / (90 - 70);
+  scroller.scrollTop = 80;
+  await waitForNextFrame();
+  assert_times_equal(
+    scrollTimeline.currentTime, (offset + p) * w * scrollerSize,
+    "current time calculation when scroll = " + scroller.scrollTop);
+
+  scroller.scrollTop = 90;
+  await waitForNextFrame();
+  assert_times_equal(
+    scrollTimeline.currentTime, scrollerSize,
+    "current time calculation when scroll = " + scroller.scrollTop);
+}, 'currentTime calculations when multiple scroll offsets are specified');
+
+promise_test(async t => {
+  const scroller = setupScrollTimelineTest();
+  const scrollerSize = scroller.scrollHeight - scroller.clientHeight;
+
+  var scrollTimeline = new ScrollTimeline({
+    scrollSource: scroller,
+    timeRange: scrollerSize,
+    orientation: 'block',
+    scrollOffsets: [CSS.px(300), CSS.px(200), CSS.px(10)],
+  });
+
+  scroller.scrollTop = 250;
+  await waitForNextFrame();
+  assert_times_equal(
+    scrollTimeline.currentTime, 0,
+    "current time calculation when scroll = " + scroller.scrollTop);
+
+  scroller.scrollTop = 400;
+  await waitForNextFrame();
+  assert_times_equal(
+    scrollTimeline.currentTime, scrollerSize,
+    "current time calculation when scroll = " + scroller.scrollTop);
+
+  scrollTimeline = new ScrollTimeline({
+    scrollSource: scroller,
+    timeRange: scrollerSize,
+    orientation: 'block',
+    scrollOffsets: [CSS.px(0), CSS.px(400), CSS.px(200)],
+  });
+
+  scroller.scrollTop = 100;
+  await waitForNextFrame();
+  assert_times_equal(
+    scrollTimeline.currentTime, 0.25 * 0.5 * scrollerSize,
+    "current time calculation when scroll = " + scroller.scrollTop);
+
+  scrollTimeline = new ScrollTimeline({
+    scrollSource: scroller,
+    timeRange: scrollerSize,
+    orientation: 'block',
+    scrollOffsets: [CSS.px(200), CSS.px(0), CSS.px(400)],
+  });
+
+  scroller.scrollTop = 200;
+  await waitForNextFrame();
+  assert_times_equal(
+    scrollTimeline.currentTime, 0.5 * scrollerSize + 0.5 * 0.5 * scrollerSize,
+    "current time calculation when scroll = " + scroller.scrollTop);
+}, 'currentTime calculations when overlapping scroll offsets are specified');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.any.js
index 70110a7..a636723 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.any.js
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.any.js
@@ -15,6 +15,55 @@
   });
 }
 
+const invalidConfigs = [
+  {
+    comment: 'Emtpy codec',
+    config: {codec: ''},
+  },
+  {
+    comment: 'Unrecognized codec',
+    config: {codec: 'bogus'},
+  },
+  {
+    comment: 'Video codec',
+    config: {codec: 'vp8'},
+  },
+  {
+    comment: 'Ambiguous codec',
+    config: {codec: 'vp9'},
+  },
+  {
+    comment: 'Codec with MIME type',
+    config: {codec: 'audio/webm; codecs="opus"'},
+  },
+];
+
+invalidConfigs.forEach(entry => {
+  promise_test(t => {
+    return promise_rejects_js(t, TypeError, AudioDecoder.isConfigSupported(entry.config));
+  }, 'Test that AudioDecoder.isConfigSupported() rejects invalid config:' + entry.comment);
+});
+
+
+invalidConfigs.forEach(entry => {
+  async_test(t => {
+    let codec = new AudioDecoder(getDefaultCodecInit(t));
+    assert_throws_js(TypeError, () => { codec.configure(entry.config); });
+    t.done();
+  }, 'Test that AudioDecoder.configure() rejects invalid config:' + entry.comment);
+});
+
+promise_test(t => {
+  return AudioDecoder.isConfigSupported({
+    codec: 'opus',
+    sampleRate: 48000,
+    numberOfChannels: 2,
+    // Opus header extradata.
+    description: new Uint8Array([0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64,
+        0x01, 0x02, 0x38, 0x01, 0x80, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00])
+  });
+}, 'Test AudioDecoder.isConfigSupported() with a valid config');
+
 promise_test(t => {
   // AudioDecoderInit lacks required fields.
   assert_throws_js(TypeError, () => { new AudioDecoder({}); });
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt
index 0a2db58..23e0520 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='fixed lime box'",
-      "position": [10, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#00FF00"
+      "backgroundColor": "#00FF00",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 100, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
index 93f980f..b945d37 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
@@ -17,10 +17,10 @@
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='fixed lime box'",
-      "position": [10, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#00FF00"
+      "backgroundColor": "#00FF00",
+      "transform": 2
     }
   ],
   "transforms": [
@@ -32,6 +32,15 @@
         [0, 0, 1, 0],
         [10, 100, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 100, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
index 63df1d96..09264d2 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
@@ -15,9 +15,23 @@
       "transform": 1
     },
     {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed lime box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FF00",
+      "transform": 3
+    },
+    {
       "name": "LayoutIFrame (positioned) IFRAME id='iframe2' class='composited'",
       "bounds": [154, 154],
-      "transform": 2
+      "transform": 4
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed lime box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FF00",
+      "transform": 6
     },
     {
       "name": "LayoutIFrame (positioned) IFRAME id='iframe3'",
@@ -25,6 +39,13 @@
       "bounds": [154, 154]
     },
     {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed lime box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FF00",
+      "transform": 8
+    },
+    {
       "name": "VerticalScrollbar",
       "position": [785, 0],
       "bounds": [15, 600]
@@ -46,8 +67,66 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [12, 32, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [10, 200, 0, 1]
       ]
+    },
+    {
+      "id": 5,
+      "parent": 4,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [2, 2, 0, 1]
+      ]
+    },
+    {
+      "id": 6,
+      "parent": 5,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 7,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [12, 382, 0, 1]
+      ]
+    },
+    {
+      "id": 8,
+      "parent": 7,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
index b8299e9..bf1b2f6 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
@@ -9,8 +9,19 @@
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
-      "position": [10, 10],
-      "bounds": [39, 20]
+      "bounds": [39, 20],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt
index 029cb61..2a59cfd 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt
@@ -9,10 +9,10 @@
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='fixed green'",
-      "position": [8, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 2
     }
   ],
   "transforms": [
@@ -24,6 +24,15 @@
         [0, 0, 1, 0],
         [0, -100, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 100, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt
index 10650af..23f92dd 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt
@@ -4,9 +4,31 @@
       "name": "Scrolling background of LayoutView #document",
       "bounds": [804, 604],
       "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "invalidations": [
+        [787, 2, 15, 600],
+        [779, 2, 15, 592]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='overlay'",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
       "backgroundColor": "#008000",
       "invalidations": [
-        [2, 2, 800, 600]
+        [0, 0, 800, 600]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [2, 2, 0, 1]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt
new file mode 100644
index 0000000..118b301
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 4021],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='fixed lime box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FF00",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
index 161eb9d..80260cc 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
@@ -17,10 +17,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='fixed lime box'",
-      "position": [10, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#00FF00"
+      "backgroundColor": "#00FF00",
+      "transform": 2
     }
   ],
   "transforms": [
@@ -32,6 +32,15 @@
         [0, 0, 1, 0],
         [10, 100, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 100, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
index 81e7129..32458f0 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
@@ -15,9 +15,33 @@
       "transform": 1
     },
     {
+      "name": "LayoutView #document",
+      "bounds": [150, 150],
+      "transform": 2
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='fixed lime box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FF00",
+      "transform": 3
+    },
+    {
       "name": "LayoutIFrame (positioned) IFRAME id='iframe2' class='composited'",
       "bounds": [154, 154],
-      "transform": 2
+      "transform": 4
+    },
+    {
+      "name": "LayoutView #document",
+      "bounds": [150, 150],
+      "transform": 5
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='fixed lime box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FF00",
+      "transform": 6
     },
     {
       "name": "LayoutIFrame (positioned) IFRAME id='iframe3'",
@@ -25,6 +49,18 @@
       "bounds": [154, 154]
     },
     {
+      "name": "LayoutView #document",
+      "bounds": [150, 150],
+      "transform": 7
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='fixed lime box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FF00",
+      "transform": 8
+    },
+    {
       "name": "ContentsLayer for Vertical Scrollbar Layer",
       "position": [785, 0],
       "bounds": [15, 600],
@@ -47,8 +83,66 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [12, 32, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [10, 200, 0, 1]
       ]
+    },
+    {
+      "id": 5,
+      "parent": 4,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [2, 2, 0, 1]
+      ]
+    },
+    {
+      "id": 6,
+      "parent": 5,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 7,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [12, 382, 0, 1]
+      ]
+    },
+    {
+      "id": 8,
+      "parent": 7,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
new file mode 100644
index 0000000..a110cb2
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
@@ -0,0 +1,28 @@
+TEST
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 5021],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [39, 20],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt
new file mode 100644
index 0000000..6315b15e
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt
@@ -0,0 +1,39 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 2016],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='fixed green'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, -100, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/http/tests/mediasession/callback-alive-after-gc.html b/third_party/blink/web_tests/http/tests/mediasession/callback-alive-after-gc.html
new file mode 100644
index 0000000..f17bc2d
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/mediasession/callback-alive-after-gc.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Test that setting MediaSession callbacks are alive after garbage-collection</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script type="module">
+import '/js-test-resources/gc.js';
+
+import {MediaSessionServiceMock} from './resources/mediasessionservice-mock.js';
+import {MediaSessionAction} from '/gen/services/media_session/public/mojom/media_session.mojom.m.js';
+
+async_test(t => {
+  const mock = new MediaSessionServiceMock;
+  mock.setClientCallback(_ => {
+    gc();
+    setTimeout(_ => {
+      mock.getClient().didReceiveAction(MediaSessionAction.kPlay);
+    });
+  });
+  window.navigator.mediaSession.setActionHandler("play", _ => { t.done(); });
+});
+</script>
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/file-image-removed.html b/third_party/blink/web_tests/http/tests/mediasession/file-image-removed.html
similarity index 65%
rename from third_party/blink/web_tests/media/mediasession/mojo/file-image-removed.html
rename to third_party/blink/web_tests/http/tests/mediasession/file-image-removed.html
index f2e2484..c1b26e7 100644
--- a/third_party/blink/web_tests/media/mediasession/mojo/file-image-removed.html
+++ b/third_party/blink/web_tests/http/tests/mediasession/file-image-removed.html
@@ -2,14 +2,12 @@
 <title>MediaSession Mojo Test</title>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/mediasession/media_session.mojom.js"></script>
-<script src="resources/mediasessionservice-mock.js"></script>
-<script src="resources/utils.js"></script>
-<script>
+<script type="module">
+import {MediaSessionServiceMock} from './resources/mediasessionservice-mock.js';
+import {assert_metadata_equals} from './resources/utils.js';
 
 async_test(function(t) {
-  let m = mediaSessionServiceMock;
+  let m = new MediaSessionServiceMock();
   var metadata = new MediaMetadata({
     artwork: [
       { src: "file:///foo/bar.jpg", type: "image/jpeg"}
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/media-control-action-reaches-client.html b/third_party/blink/web_tests/http/tests/mediasession/media-control-action-reaches-client.html
similarity index 64%
rename from third_party/blink/web_tests/media/mediasession/mojo/media-control-action-reaches-client.html
rename to third_party/blink/web_tests/http/tests/mediasession/media-control-action-reaches-client.html
index b9f5349..1d1e63d 100644
--- a/third_party/blink/web_tests/media/mediasession/mojo/media-control-action-reaches-client.html
+++ b/third_party/blink/web_tests/http/tests/mediasession/media-control-action-reaches-client.html
@@ -2,12 +2,9 @@
 <title>MediaSession Mojo Test</title>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/services/media_session/public/mojom/media_session.mojom.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/mediasession/media_session.mojom.js"></script>
-<script src="resources/mediasessionservice-mock.js"></script>
-<script src="resources/utils.js"></script>
-<script>
+<script type="module">
+import {MediaSessionServiceMock} from './resources/mediasessionservice-mock.js';
+import {MediaSessionAction} from '/gen/services/media_session/public/mojom/media_session.mojom.m.js';
 
 var mock;
 
@@ -45,18 +42,18 @@
   window.navigator.mediaSession.setActionHandler(
       "seekforward", t.step_func(checkExpectation.bind(null, t, "seekforward")));
 
-  mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kPlay);
-  mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kPause);
-  mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kPreviousTrack);
-  mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kNextTrack);
-  mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kSeekBackward);
-  mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kSeekForward);
+  mock.getClient().didReceiveAction(MediaSessionAction.kPlay);
+  mock.getClient().didReceiveAction(MediaSessionAction.kPause);
+  mock.getClient().didReceiveAction(MediaSessionAction.kPreviousTrack);
+  mock.getClient().didReceiveAction(MediaSessionAction.kNextTrack);
+  mock.getClient().didReceiveAction(MediaSessionAction.kSeekBackward);
+  mock.getClient().didReceiveAction(MediaSessionAction.kSeekForward);
 }
 
 // Use async_test to do asynchronous setup since setup() only works for
 // synchronous setup.
 async_test(function(t) {
-  mock = mediaSessionServiceMock;
+  mock = new MediaSessionServiceMock();
   mock.setClientCallback(t.step_func(runTests.bind(null, t)));
   // Touch window.navigator.mediaSession to start the service.
   window.navigator.mediaSession.metadata = null;
diff --git a/third_party/blink/web_tests/http/tests/mediasession/media-control-seek-to-action-reaches-client.html b/third_party/blink/web_tests/http/tests/mediasession/media-control-seek-to-action-reaches-client.html
new file mode 100644
index 0000000..67aa738
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/mediasession/media-control-seek-to-action-reaches-client.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>MediaSession Mojo Test</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script type="module">
+import {MediaSessionServiceMock} from './resources/mediasessionservice-mock.js';
+import {MediaSessionAction} from '/gen/services/media_session/public/mojom/media_session.mojom.m.js';
+
+async_test((t) => {
+  const mediaSessionServiceMock = new MediaSessionServiceMock();
+  mediaSessionServiceMock.setClientCallback(t.step_func(() => {
+    mediaSessionServiceMock.getClient().didReceiveAction(
+        MediaSessionAction.kSeekTo,
+        {seekTo: {seekTime: {microseconds: 10000000}, fastSeek: true}});
+  }));
+
+  window.navigator.mediaSession.setActionHandler("seekto", t.step_func_done((e) => {
+    assert_equals(e.action, "seekto");
+    assert_equals(e.seekTime, 10);
+    assert_true(e.fastSeek);
+  }));
+}, "test that the seek to action reaches client");
+</script>
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/media-control-set-handler-notifies-service.html b/third_party/blink/web_tests/http/tests/mediasession/media-control-set-handler-notifies-service.html
similarity index 66%
rename from third_party/blink/web_tests/media/mediasession/mojo/media-control-set-handler-notifies-service.html
rename to third_party/blink/web_tests/http/tests/mediasession/media-control-set-handler-notifies-service.html
index 8937197c..2abbfa2 100644
--- a/third_party/blink/web_tests/media/mediasession/mojo/media-control-set-handler-notifies-service.html
+++ b/third_party/blink/web_tests/http/tests/mediasession/media-control-set-handler-notifies-service.html
@@ -2,12 +2,9 @@
 <title>Test that setting MediaSession event handler should notify the service</title>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/services/media_session/public/mojom/media_session.mojom.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/mediasession/media_session.mojom.js"></script>
-<script src="resources/mediasessionservice-mock.js"></script>
-<script src="resources/utils.js"></script>
-<script>
+<script type="module">
+import {MediaSessionServiceMock} from './resources/mediasessionservice-mock.js';
+import {MediaSessionAction} from '/gen/services/media_session/public/mojom/media_session.mojom.m.js';
 
 var expectations;
 
@@ -17,34 +14,34 @@
 function getExpectations() {
   if (!expectations) {
     expectations = [
-      [ mediaSession.mojom.MediaSessionAction.kPlay, true ],
-      [ mediaSession.mojom.MediaSessionAction.kPause, true ],
-      [ mediaSession.mojom.MediaSessionAction.kPreviousTrack, true ],
-      [ mediaSession.mojom.MediaSessionAction.kNextTrack, true ],
-      [ mediaSession.mojom.MediaSessionAction.kSeekBackward, true ],
-      [ mediaSession.mojom.MediaSessionAction.kSeekForward, true ],
-      [ mediaSession.mojom.MediaSessionAction.kSeekTo, true ],
-      [ mediaSession.mojom.MediaSessionAction.kPlay, false ],
-      [ mediaSession.mojom.MediaSessionAction.kPause, false ],
-      [ mediaSession.mojom.MediaSessionAction.kPreviousTrack, false ],
-      [ mediaSession.mojom.MediaSessionAction.kNextTrack, false ],
-      [ mediaSession.mojom.MediaSessionAction.kSeekBackward, false ],
-      [ mediaSession.mojom.MediaSessionAction.kSeekForward, false ],
-      [ mediaSession.mojom.MediaSessionAction.kSeekTo, false ],
-      [ mediaSession.mojom.MediaSessionAction.kPlay, true ],
-      [ mediaSession.mojom.MediaSessionAction.kPause, true ],
-      [ mediaSession.mojom.MediaSessionAction.kPreviousTrack, true ],
-      [ mediaSession.mojom.MediaSessionAction.kNextTrack, true ],
-      [ mediaSession.mojom.MediaSessionAction.kSeekBackward, true ],
-      [ mediaSession.mojom.MediaSessionAction.kSeekForward, true ],
-      [ mediaSession.mojom.MediaSessionAction.kSeekTo, true ],
+      [ MediaSessionAction.kPlay, true ],
+      [ MediaSessionAction.kPause, true ],
+      [ MediaSessionAction.kPreviousTrack, true ],
+      [ MediaSessionAction.kNextTrack, true ],
+      [ MediaSessionAction.kSeekBackward, true ],
+      [ MediaSessionAction.kSeekForward, true ],
+      [ MediaSessionAction.kSeekTo, true ],
+      [ MediaSessionAction.kPlay, false ],
+      [ MediaSessionAction.kPause, false ],
+      [ MediaSessionAction.kPreviousTrack, false ],
+      [ MediaSessionAction.kNextTrack, false ],
+      [ MediaSessionAction.kSeekBackward, false ],
+      [ MediaSessionAction.kSeekForward, false ],
+      [ MediaSessionAction.kSeekTo, false ],
+      [ MediaSessionAction.kPlay, true ],
+      [ MediaSessionAction.kPause, true ],
+      [ MediaSessionAction.kPreviousTrack, true ],
+      [ MediaSessionAction.kNextTrack, true ],
+      [ MediaSessionAction.kSeekBackward, true ],
+      [ MediaSessionAction.kSeekForward, true ],
+      [ MediaSessionAction.kSeekTo, true ],
     ];
   }
   return expectations;
 }
 
 async_test(function(t) {
-  let m = mediaSessionServiceMock;
+  const m = new MediaSessionServiceMock();
   m.setEnableDisableActionCallback(t.step_func(function(action, isEnabled) {
     var expectedAction = getExpectations()[nextExpectation][0];
     var expectedIsEnabled = getExpectations()[nextExpectation][1];
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/metadata-async.html b/third_party/blink/web_tests/http/tests/mediasession/metadata-async.html
similarity index 85%
rename from third_party/blink/web_tests/media/mediasession/mojo/metadata-async.html
rename to third_party/blink/web_tests/http/tests/mediasession/metadata-async.html
index 5a726b0..5b3421e 100644
--- a/third_party/blink/web_tests/media/mediasession/mojo/metadata-async.html
+++ b/third_party/blink/web_tests/http/tests/mediasession/metadata-async.html
@@ -2,11 +2,9 @@
 <title>MediaMetadata Mojo Test</title>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/mediasession/media_session.mojom.js"></script>
-<script src="resources/mediasessionservice-mock.js"></script>
-<script src="resources/utils.js"></script>
-<script>
+<script type="module">
+import {MediaSessionServiceMock} from './resources/mediasessionservice-mock.js';
+import {assert_metadata_equals} from './resources/utils.js';
 
 async_test(t => {
   // The following are expected results.
@@ -39,7 +37,7 @@
   ];
   var resultId = 0;
 
-  let m = mediaSessionServiceMock;
+  const m = new MediaSessionServiceMock();
   m.setMetadataCallback(t.step_func(receivedMetadata => {
     assert_metadata_equals(receivedMetadata, results[resultId]);
     ++resultId;
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/metadata-propagated-twice.html b/third_party/blink/web_tests/http/tests/mediasession/metadata-propagated-twice.html
similarity index 71%
rename from third_party/blink/web_tests/media/mediasession/mojo/metadata-propagated-twice.html
rename to third_party/blink/web_tests/http/tests/mediasession/metadata-propagated-twice.html
index dbdff47..ab720fba 100644
--- a/third_party/blink/web_tests/media/mediasession/mojo/metadata-propagated-twice.html
+++ b/third_party/blink/web_tests/http/tests/mediasession/metadata-propagated-twice.html
@@ -2,14 +2,12 @@
 <title>MediaSession Mojo Test</title>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/mediasession/media_session.mojom.js"></script>
-<script src="resources/mediasessionservice-mock.js"></script>
-<script src="resources/utils.js"></script>
-<script>
+<script type="module">
+import {MediaSessionServiceMock} from './resources/mediasessionservice-mock.js';
+import {assert_metadata_equals} from './resources/utils.js';
 
 async_test(function(t) {
-  let m = mediaSessionServiceMock;
+  const m = new MediaSessionServiceMock();
   var dontCareMetadata = new MediaMetadata({});
 
   m.setMetadataCallback(t.step_func(function() {
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/metadata-propagated.html b/third_party/blink/web_tests/http/tests/mediasession/metadata-propagated.html
similarity index 66%
rename from third_party/blink/web_tests/media/mediasession/mojo/metadata-propagated.html
rename to third_party/blink/web_tests/http/tests/mediasession/metadata-propagated.html
index 31fa8bf8..321420e 100644
--- a/third_party/blink/web_tests/media/mediasession/mojo/metadata-propagated.html
+++ b/third_party/blink/web_tests/http/tests/mediasession/metadata-propagated.html
@@ -2,14 +2,12 @@
 <title>MediaSession Mojo Test</title>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/mediasession/media_session.mojom.js"></script>
-<script src="resources/mediasessionservice-mock.js"></script>
-<script src="resources/utils.js"></script>
-<script>
+<script type="module">
+import {MediaSessionServiceMock} from './resources/mediasessionservice-mock.js';
+import {assert_metadata_equals} from './resources/utils.js';
 
 async_test(function(t) {
-  let m = mediaSessionServiceMock;
+  const m = new MediaSessionServiceMock();
   var metadata = new MediaMetadata({
     title: "title1",
     artist: "artist1",
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/metadata-session-link.html b/third_party/blink/web_tests/http/tests/mediasession/metadata-session-link.html
similarity index 82%
rename from third_party/blink/web_tests/media/mediasession/mojo/metadata-session-link.html
rename to third_party/blink/web_tests/http/tests/mediasession/metadata-session-link.html
index 6d6fe39..59cd24e 100644
--- a/third_party/blink/web_tests/media/mediasession/mojo/metadata-session-link.html
+++ b/third_party/blink/web_tests/http/tests/mediasession/metadata-session-link.html
@@ -2,11 +2,9 @@
 <title>MediaMetadata / MediaSession link Mojo Test</title>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/mediasession/media_session.mojom.js"></script>
-<script src="resources/mediasessionservice-mock.js"></script>
-<script src="resources/utils.js"></script>
-<script>
+<script type="module">
+import {MediaSessionServiceMock} from './resources/mediasessionservice-mock.js';
+import {assert_metadata_equals} from './resources/utils.js';
 
 async_test(t => {
   // The following are expected results.
@@ -24,7 +22,7 @@
   ];
   var resultId = 0;
 
-  let m = mediaSessionServiceMock;
+  const m = new MediaSessionServiceMock();
   m.setMetadataCallback(t.step_func(receivedMetadata => {
     assert_metadata_equals(receivedMetadata, results[resultId]);
     ++resultId;
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/playback-state-propagated.html b/third_party/blink/web_tests/http/tests/mediasession/playback-state-propagated.html
similarity index 75%
rename from third_party/blink/web_tests/media/mediasession/mojo/playback-state-propagated.html
rename to third_party/blink/web_tests/http/tests/mediasession/playback-state-propagated.html
index 918aa46..2e97ca0 100644
--- a/third_party/blink/web_tests/media/mediasession/mojo/playback-state-propagated.html
+++ b/third_party/blink/web_tests/http/tests/mediasession/playback-state-propagated.html
@@ -2,11 +2,10 @@
 <title>MediaSession Mojo Test</title>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/mediasession/media_session.mojom.js"></script>
-<script src="resources/mediasessionservice-mock.js"></script>
-<script src="resources/utils.js"></script>
-<script>
+<script type="module">
+import {MediaSessionServiceMock} from './resources/mediasessionservice-mock.js';
+
+import {MediaSessionPlaybackState} from '/gen/third_party/blink/public/mojom/mediasession/media_session.mojom.m.js';
 
 var inputStates = ["none", "paused", "playing", "invalid", "none"];
 var expectations;
@@ -26,7 +25,7 @@
 }
 
 async_test(function(t) {
-  let m = mediaSessionServiceMock;
+  const m = new MediaSessionServiceMock();
   m.setPlaybackStateCallback(t.step_func(function(state) {
     assert_equals(state, getExpectations()[nextExpectation++]);
     if (nextExpectation == getExpectations().length)
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/position-state-propagated.html b/third_party/blink/web_tests/http/tests/mediasession/position-state-propagated.html
similarity index 70%
rename from third_party/blink/web_tests/media/mediasession/mojo/position-state-propagated.html
rename to third_party/blink/web_tests/http/tests/mediasession/position-state-propagated.html
index 6573d6b..572d820 100644
--- a/third_party/blink/web_tests/media/mediasession/mojo/position-state-propagated.html
+++ b/third_party/blink/web_tests/http/tests/mediasession/position-state-propagated.html
@@ -2,11 +2,9 @@
 <title>MediaSession Mojo Test</title>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/mediasession/media_session.mojom.js"></script>
-<script src="resources/mediasessionservice-mock.js"></script>
-<script src="resources/utils.js"></script>
-<script>
+<script type="module">
+import {MediaSessionServiceMock} from './resources/mediasessionservice-mock.js';
+
 const inputPositions = [
   { duration: 10 },
   { duration: 10, playbackRate: 2.0, position: 5 },
@@ -20,14 +18,14 @@
 ];
 
 function toSecs(timeDelta) {
-  return timeDelta.microseconds / 1000000;
+  return Number(timeDelta.microseconds / 1000000n);
 }
 
 async_test((t) => {
-  let m = mediaSessionServiceMock;
+  const m = new MediaSessionServiceMock();
   let nextExpectation = 0;
 
-  mediaSessionServiceMock.setPositionStateCallback(t.step_func((position) => {
+  m.setPositionStateCallback(t.step_func((position) => {
     const expectation = expectations[nextExpectation++];
 
     if (expectation === null) {
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/resources/mediasessionservice-mock.js b/third_party/blink/web_tests/http/tests/mediasession/resources/mediasessionservice-mock.js
similarity index 71%
rename from third_party/blink/web_tests/media/mediasession/mojo/resources/mediasessionservice-mock.js
rename to third_party/blink/web_tests/http/tests/mediasession/resources/mediasessionservice-mock.js
index 5220d05a..d4aed5c 100644
--- a/third_party/blink/web_tests/media/mediasession/mojo/resources/mediasessionservice-mock.js
+++ b/third_party/blink/web_tests/http/tests/mediasession/resources/mediasessionservice-mock.js
@@ -1,8 +1,9 @@
 /*
- * mediasessionservice-mock contains a mock implementation of MediaSessionService.
+ * mediasessionservice-mock contains a mock implementation of
+ * MediaSessionService.
  */
 
-"use strict";
+import {MediaSessionService, MediaSessionServiceReceiver} from '/gen/third_party/blink/public/mojom/mediasession/media_session.mojom.m.js';
 
 function mojoString16ToJS(mojoString16) {
   return String.fromCharCode.apply(null, mojoString16.data);
@@ -11,15 +12,15 @@
 function mojoImageToJS(mojoImage) {
   var src = mojoImage.src.url;
   var type = mojoString16ToJS(mojoImage.type);
-  var sizes = "";
+  var sizes = '';
   for (var i = 0; i < mojoImage.sizes.length; i++) {
     if (i > 0)
-      sizes += " ";
+      sizes += ' ';
 
     var mojoSize = mojoImage.sizes[i];
-    sizes += mojoSize.width.toString() + "x" + mojoSize.height.toString();
+    sizes += mojoSize.width.toString() + 'x' + mojoSize.height.toString();
   }
-  return { src: src, type: type, sizes: sizes };
+  return {src, type, sizes};
 }
 
 function mojoMetadataToJS(mojoMetadata) {
@@ -33,22 +34,18 @@
   for (var i = 0; i < mojoMetadata.artwork.length; i++)
     artwork.push(mojoImageToJS(mojoMetadata.artwork[i]));
 
-  return new MediaMetadata({title: title, artist: artist, album: album, artwork: artwork});
+  return new MediaMetadata({title, artist, album, artwork});
 }
 
-var MediaSessionAction = blink.mojom.MediaSessionAction;
-var MediaSessionPlaybackState = blink.mojom.MediaSessionPlaybackState;
-
-class MediaSessionServiceMock {
+export class MediaSessionServiceMock {
   constructor() {
     this.pendingResponse_ = null;
-    this.bindingSet_ = new mojo.BindingSet(
-        blink.mojom.MediaSessionService);
+    this.receiver_ = new MediaSessionServiceReceiver(this);
 
     this.interceptor_ =
-        new MojoInterfaceInterceptor(blink.mojom.MediaSessionService.name);
-    this.interceptor_.oninterfacerequest =
-        e => this.bindingSet_.addBinding(this, e.handle);
+        new MojoInterfaceInterceptor(MediaSessionService.$interfaceName);
+    this.interceptor_.oninterfacerequest = e =>
+        this.receiver_.$.bindHandle(e.handle);
     this.interceptor_.start();
   }
 
@@ -107,5 +104,3 @@
     return this.client_;
   }
 }
-
-let mediaSessionServiceMock = new MediaSessionServiceMock();
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/resources/utils.js b/third_party/blink/web_tests/http/tests/mediasession/resources/utils.js
similarity index 75%
rename from third_party/blink/web_tests/media/mediasession/mojo/resources/utils.js
rename to third_party/blink/web_tests/http/tests/mediasession/resources/utils.js
index f8947a60..db7f5493 100644
--- a/third_party/blink/web_tests/media/mediasession/mojo/resources/utils.js
+++ b/third_party/blink/web_tests/http/tests/mediasession/resources/utils.js
@@ -4,12 +4,13 @@
   assert_equals(expected.sizes, observed.sizes);
 }
 
-function assert_metadata_equals(expected, observed) {
+export function assert_metadata_equals(expected, observed) {
   assert_equals(expected.title, observed.title, 'metadata.title');
   assert_equals(expected.artist, observed.artist, 'metadata.artist');
   assert_equals(expected.album, observed.album, 'metadata.album');
-  assert_equals(expected.artwork.length, observed.artwork.length,
-                'metadata.artwork.length');
+  assert_equals(
+      expected.artwork.length, observed.artwork.length,
+      'metadata.artwork.length');
   for (var i = 0; i < expected.artwork.length; i++)
     assert_image_equals(expected.artwork[i], observed.artwork[i]);
 }
diff --git a/third_party/blink/web_tests/http/tests/mediasession/set-null-metadata.html b/third_party/blink/web_tests/http/tests/mediasession/set-null-metadata.html
new file mode 100644
index 0000000..28620e50
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/mediasession/set-null-metadata.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>MediaSession Mojo Test</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script type="module">
+import {MediaSessionServiceMock} from './resources/mediasessionservice-mock.js';
+
+async_test(function(t) {
+  const m = new MediaSessionServiceMock();
+  m.setMetadataCallback(t.step_func(function(receivedMetadata) {
+    assert_equals(receivedMetadata, null);
+    t.done();
+  }));
+  window.navigator.mediaSession.metadata = null;
+}, "test that null MediaMetadata is correctly propagated");
+
+</script>
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/callback-alive-after-gc.html b/third_party/blink/web_tests/media/mediasession/mojo/callback-alive-after-gc.html
deleted file mode 100644
index fa105d5c..0000000
--- a/third_party/blink/web_tests/media/mediasession/mojo/callback-alive-after-gc.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!DOCTYPE html>
-<title>Test that setting MediaSession callbacks are alive after garbage-collection</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/gc.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/services/media_session/public/mojom/media_session.mojom.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/mediasession/media_session.mojom.js"></script>
-<script src="resources/mediasessionservice-mock.js"></script>
-<script src="resources/utils.js"></script>
-<script>
-
-var mock;
-
-async_test(function(t) {
-  let mock = mediaSessionServiceMock;
-  mock.setClientCallback(_ => {
-    gc();
-    setTimeout(_ => {
-      mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kPlay);
-    });
-  });
-  window.navigator.mediaSession.setActionHandler("play", _ => { t.done(); });
-});
-</script>
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/media-control-seek-to-action-reaches-client.html b/third_party/blink/web_tests/media/mediasession/mojo/media-control-seek-to-action-reaches-client.html
deleted file mode 100644
index f9a27f8..0000000
--- a/third_party/blink/web_tests/media/mediasession/mojo/media-control-seek-to-action-reaches-client.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<title>MediaSession Mojo Test</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/gen/mojo/public/mojom/base/time.mojom.js"></script>
-<script src="file:///gen/services/media_session/public/mojom/media_session.mojom.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/mediasession/media_session.mojom.js"></script>
-<script src="resources/mediasessionservice-mock.js"></script>
-<script src="resources/utils.js"></script>
-<script>
-async_test((t) => {
-  mediaSessionServiceMock.setClientCallback(t.step_func(() => {
-    mediaSessionServiceMock.getClient().didReceiveAction(
-        mediaSession.mojom.MediaSessionAction.kSeekTo,
-            new blink.mojom.MediaSessionActionDetails({
-                seekTo: new blink.mojom.MediaSessionSeekToDetails({
-                    seekTime: new mojoBase.mojom.TimeDelta({microseconds: 10000000}),
-                    fastSeek: true
-                })
-            }));
-  }));
-
-  window.navigator.mediaSession.setActionHandler("seekto", t.step_func_done((e) => {
-    assert_equals(e.action, "seekto");
-    assert_equals(e.seekTime, 10);
-    assert_true(e.fastSeek);
-  }));
-}, "test that the seek to action reaches client");
-</script>
diff --git a/third_party/blink/web_tests/media/mediasession/mojo/set-null-metadata.html b/third_party/blink/web_tests/media/mediasession/mojo/set-null-metadata.html
deleted file mode 100644
index db3479a..0000000
--- a/third_party/blink/web_tests/media/mediasession/mojo/set-null-metadata.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<title>MediaSession Mojo Test</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/mediasession/media_session.mojom.js"></script>
-<script src="resources/mediasessionservice-mock.js"></script>
-<script src="resources/utils.js"></script>
-<script>
-
-async_test(function(t) {
-  let m = mediaSessionServiceMock;
-  m.setMetadataCallback(t.step_func(function(receivedMetadata) {
-    assert_equals(receivedMetadata, null);
-    t.done();
-  }));
-  window.navigator.mediaSession.metadata = null;
-}, "test that null MediaMetadata is correctly propagated");
-
-</script>
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt b/third_party/blink/web_tests/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt
index 2b89c39a..9b2c380 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt
@@ -5,11 +5,14 @@
       "bounds": [800, 2016],
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
-      "invalidations": [
-        [8, 200, 100, 100],
-        [8, 100, 100, 100]
-      ],
       "transform": 1
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed green'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 2
     }
   ],
   "transforms": [
@@ -21,6 +24,15 @@
         [0, 0, 1, 0],
         [0, -100, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 100, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/linux/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt b/third_party/blink/web_tests/platform/linux/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
new file mode 100644
index 0000000..01f5301
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
@@ -0,0 +1,28 @@
+TEST
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 5021],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [39, 20],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/html/details_summary/details-position-expected.png b/third_party/blink/web_tests/platform/linux/html/details_summary/details-position-expected.png
index 9255b8f..480abbf 100644
--- a/third_party/blink/web_tests/platform/linux/html/details_summary/details-position-expected.png
+++ b/third_party/blink/web_tests/platform/linux/html/details_summary/details-position-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll-expected.txt
deleted file mode 100644
index a9b7884..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-FAIL Tests that the target_div gets scrollend event when dragging scroll on target. promise_test: Unhandled rejection with value: "target_div did not receive scrollend event after dragging scroll on target."
-FAIL Tests that the target_div gets scrollend event when click scrollbar on target. promise_test: Unhandled rejection with value: "target_div did not receive scrollend event after clicking scrollbar on target."
-FAIL Tests that the target_div gets scrollend event when drag the thumb of target. promise_test: Unhandled rejection with value: "target_div did not receive scrollend event after dragging the thumb of target."
-PASS Tests that the target_div gets scrollend event when send DOWN key to target.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/52776-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/52776-expected.png
deleted file mode 100644
index 8ada27b..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/52776-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-boundary-values-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-boundary-values-expected.png
deleted file mode 100644
index 88c11f3..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-boundary-values-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-clone-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-clone-expected.txt
deleted file mode 100644
index f840958..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-clone-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-PASS cloned.value is target.value
-PASS internals.shadowPseudoId(clonedShadowRoot.firstChild) is internals.shadowPseudoId(targetShadowRoot.firstChild)
-PASS internals.shadowPseudoId(clonedShadowRoot.firstChild.firstChild) is internals.shadowPseudoId(targetShadowRoot.firstChild.firstChild)
-PASS internals.shadowPseudoId(clonedShadowRoot.firstChild.firstChild.firstChild) is internals.shadowPseudoId(targetShadowRoot.firstChild.firstChild.firstChild)
-PASS clonedShadowRoot.firstChild.firstChild.firstChild.style.width is "70%"
-PASS targetShadowRoot.firstChild.firstChild.firstChild.style.width is "50%"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-element-markup-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-element-markup-expected.txt
deleted file mode 100644
index a39a6a3..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-element-markup-expected.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-
-Both meter elements should have a nested shadow box with a width specified:
-| "
-    "
-| <meter>
-|   max="100"
-|   value="70"
-|   <shadow:root>
-|     <div>
-|       pseudo="-webkit-meter-inner-element"
-|       shadow:pseudoId="-webkit-meter-inner-element"
-|       <div>
-|         pseudo="-webkit-meter-bar"
-|         shadow:pseudoId="-webkit-meter-bar"
-|         <div>
-|           pseudo="-webkit-meter-optimum-value"
-|           style="width: 70%;"
-|           shadow:pseudoId="-webkit-meter-optimum-value"
-|     <div>
-|       pseudo="-internal-fallback"
-|       shadow:pseudoId="-internal-fallback"
-|       <slot>
-|         name="user-agent-default-slot"
-| "
-    "
-| <meter>
-|   high="6"
-|   low="3"
-|   max="10"
-|   min="0"
-|   optimum="5"
-|   value="10"
-|   <shadow:root>
-|     <div>
-|       pseudo="-webkit-meter-inner-element"
-|       shadow:pseudoId="-webkit-meter-inner-element"
-|       <div>
-|         pseudo="-webkit-meter-bar"
-|         shadow:pseudoId="-webkit-meter-bar"
-|         <div>
-|           pseudo="-webkit-meter-suboptimum-value"
-|           style="width: 100%;"
-|           shadow:pseudoId="-webkit-meter-suboptimum-value"
-|     <div>
-|       pseudo="-internal-fallback"
-|       shadow:pseudoId="-internal-fallback"
-|       <slot>
-|         name="user-agent-default-slot"
-| "
-    "
-| <meter>
-|   high="6"
-|   low="3"
-|   max="10"
-|   min="0"
-|   optimum="0"
-|   value="10"
-|   <shadow:root>
-|     <div>
-|       pseudo="-webkit-meter-inner-element"
-|       shadow:pseudoId="-webkit-meter-inner-element"
-|       <div>
-|         pseudo="-webkit-meter-bar"
-|         shadow:pseudoId="-webkit-meter-bar"
-|         <div>
-|           pseudo="-webkit-meter-even-less-good-value"
-|           style="width: 100%;"
-|           shadow:pseudoId="-webkit-meter-even-less-good-value"
-|     <div>
-|       pseudo="-internal-fallback"
-|       shadow:pseudoId="-internal-fallback"
-|       <slot>
-|         name="user-agent-default-slot"
-| "
-  "
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-optimums-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-optimums-expected.png
deleted file mode 100644
index 8c9f4b6a..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-optimums-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-styles-changing-pseudo-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-styles-changing-pseudo-expected.png
deleted file mode 100644
index 7d479d7..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-styles-changing-pseudo-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-styles-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-styles-expected.png
deleted file mode 100644
index a7a9b52..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-styles-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/focus-contenteditable-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/focus-contenteditable-expected.png
deleted file mode 100644
index 2300b2f..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/dom/focus-contenteditable-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/parser/bad-xml-slash-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/parser/bad-xml-slash-expected.png
deleted file mode 100644
index 0e12fae0..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/parser/bad-xml-slash-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/parser/entity-comment-in-textarea-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/parser/entity-comment-in-textarea-expected.png
deleted file mode 100644
index ddf3ae17..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/parser/entity-comment-in-textarea-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/parser/open-comment-in-textarea-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/parser/open-comment-in-textarea-expected.png
deleted file mode 100644
index b342357..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/fast/parser/open-comment-in-textarea-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-1-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-1-and-click-expected.png
deleted file mode 100644
index f45d7fb..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-1-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-10-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-10-and-click-expected.png
deleted file mode 100644
index 35222a8..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-10-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-2-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-2-and-click-expected.png
deleted file mode 100644
index c15fc31..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-2-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-3-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-3-and-click-expected.png
deleted file mode 100644
index 77c04f9..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-3-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-4-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-4-and-click-expected.png
deleted file mode 100644
index 8353760a..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-4-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-5-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-5-and-click-expected.png
deleted file mode 100644
index f3699d6..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-5-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-6-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-6-and-click-expected.png
deleted file mode 100644
index 35222a8..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-6-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-7-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-7-and-click-expected.png
deleted file mode 100644
index 35222a8..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-7-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-8-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-8-and-click-expected.png
deleted file mode 100644
index 9e084c7..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-8-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-9-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-9-and-click-expected.png
deleted file mode 100644
index 7d65c180..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-9-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-no-summary4-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-no-summary4-expected.png
deleted file mode 100644
index a6e52ea8..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-no-summary4-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-open-javascript-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-open-javascript-expected.png
deleted file mode 100644
index e9b8fe2..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-open-javascript-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-open2-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-open2-expected.png
deleted file mode 100644
index 6ec7c82..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-open2-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-open4-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-open4-expected.png
deleted file mode 100644
index 6ec7c82..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-open4-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-1-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-1-and-click-expected.png
deleted file mode 100644
index 358d84c..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-1-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-2-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-2-and-click-expected.png
deleted file mode 100644
index 88fb71b..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-2-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-3-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-3-and-click-expected.png
deleted file mode 100644
index b30847f..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-3-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-4-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-4-and-click-expected.png
deleted file mode 100644
index 2a25f58..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-4-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-5-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-5-and-click-expected.png
deleted file mode 100644
index d3bdec4c..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-5-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-6-and-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-6-and-click-expected.png
deleted file mode 100644
index b79c81a..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-6-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-replace-summary-child-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-replace-summary-child-expected.png
deleted file mode 100644
index d235912b..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-replace-summary-child-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-replace-text-expected.png b/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-replace-text-expected.png
deleted file mode 100644
index 64203ed7..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/web-components-v0-disabled/html/details_summary/details-replace-text-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt
new file mode 100644
index 0000000..8f94fa3b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac-arm11.0/compositing/layer-creation/fixed-position-nonscrollable-body-expected.txt
@@ -0,0 +1,11 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 4021],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
new file mode 100644
index 0000000..00639270
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac-arm11.0/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
@@ -0,0 +1,38 @@
+Even though we can opt-out of fixed-position compositing for unscrollable fixed-position containers, we still need to composite fixed-position layers that need compositing for other reasons such as overlap.
+
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 4024],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='absolute composited red box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed lime box'",
+      "position": [10, 100],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FF00"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
new file mode 100644
index 0000000..21627b6
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac-arm11.0/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
@@ -0,0 +1,56 @@
+In all iframes, the green fixed-position element should not be composited.
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 4016],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='composited box'",
+      "bounds": [300, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FFFF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutIFrame (positioned) IFRAME id='iframe2' class='composited'",
+      "bounds": [154, 154],
+      "transform": 2
+    },
+    {
+      "name": "LayoutIFrame (positioned) IFRAME id='iframe3'",
+      "position": [10, 380],
+      "bounds": [154, 154]
+    },
+    {
+      "name": "ContentsLayer for Vertical Scrollbar Layer",
+      "position": [785, 0],
+      "bounds": [15, 600],
+      "contentsOpaque": true
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 360, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 200, 0, 1]
+      ]
+    }
+  ]
+}
+
+Composited box underneath iframe.
diff --git a/third_party/blink/web_tests/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
rename to third_party/blink/web_tests/platform/mac-mac-arm11.0/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt
new file mode 100644
index 0000000..2b89c39a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac-arm11.0/paint/invalidation/scroll/fixed-scroll-viewport-scroll-hidden-expected.txt
@@ -0,0 +1,27 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 2016],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [8, 200, 100, 100],
+        [8, 100, 100, 100]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, -100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt
rename to third_party/blink/web_tests/platform/mac-mac-arm11.0/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/web-components-v0-disabled/external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/web-components-v0-disabled/external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll-expected.txt
deleted file mode 100644
index 9a27f97f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/web-components-v0-disabled/external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Content-Type: text/plain
-This is a testharness.js-based test.
-FAIL Tests that the target_div gets scrollend event when dragging scroll on target. promise_test: Unhandled rejection with value: "target_div did not receive scrollend event after dragging scroll on target."
-FAIL Tests that the target_div gets scrollend event when click scrollbar on target. promise_test: Unhandled rejection with value: "target_div did not receive scrollend event after clicking scrollbar on target."
-FAIL Tests that the target_div gets scrollend event when drag the thumb of target. promise_test: Unhandled rejection with value: "target_div did not receive scrollend event after dragging the thumb of target."
-FAIL Tests that the target_div gets scrollend event when send DOWN key to target. assert_true: expected true got false
-Harness: the test ran to completion.
-
-#EOF
-#EOF
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-optimums-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-optimums-expected.png
deleted file mode 100644
index 0384a07..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-optimums-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt
new file mode 100644
index 0000000..ccc7ffc
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt
@@ -0,0 +1,39 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [804, 604],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [10, 2, 769, 592],
+        [787, 2, 15, 600]
+      ]
+    },
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='overlay'",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [2, 2, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/web-components-v0-disabled/fast/dom/52776-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/web-components-v0-disabled/fast/dom/52776-expected.png
deleted file mode 100644
index b9fc423..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/web-components-v0-disabled/fast/dom/52776-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/web-components-v0-disabled/fast/dom/title-text-property-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/web-components-v0-disabled/fast/dom/title-text-property-expected.txt
deleted file mode 100644
index 0a1ece0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/web-components-v0-disabled/fast/dom/title-text-property-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-TITLE CHANGED: 'This is the new title'
-Original title is: 'Original Title'
-Setting new title to: 'This is the new title'
-New title is: 'This is the new title'
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-3-and-click-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-3-and-click-expected.png
deleted file mode 100644
index dedfc7dc..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-3-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt b/third_party/blink/web_tests/platform/mac/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
new file mode 100644
index 0000000..763b4de
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
@@ -0,0 +1,28 @@
+TEST
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 5021],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [38, 18],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/html/semantics/forms/the-input-element/selection-pointer-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/html/semantics/forms/the-input-element/selection-pointer-expected.txt
deleted file mode 100644
index cd5b61f6..0000000
--- a/third_party/blink/web_tests/platform/mac/external/wpt/html/semantics/forms/the-input-element/selection-pointer-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-Content-Type: text/plain
-This is a testharness.js-based test.
-PASS Selecting texts across <input type=button> should not cancel selection
-PASS Selecting texts across <input type=checkbox> should not cancel selection
-PASS Selecting texts across <input type=color> should not cancel selection
-PASS Selecting texts across <input type=date> should not cancel selection
-PASS Selecting texts across <input type=datetime-local> should not cancel selection
-PASS Selecting texts across <input type=email> should not cancel selection
-PASS Selecting texts across <input type=file> should not cancel selection
-FAIL Selecting texts across <input type=image> should not cancel selection promise_test: Unhandled rejection with value: object "Error: element click intercepted error"
-PASS Selecting texts across <input type=month> should not cancel selection
-PASS Selecting texts across <input type=number> should not cancel selection
-PASS Selecting texts across <input type=password> should not cancel selection
-PASS Selecting texts across <input type=radio> should not cancel selection
-PASS Selecting texts across <input type=range> should not cancel selection
-PASS Selecting texts across <input type=reset> should not cancel selection
-PASS Selecting texts across <input type=search> should not cancel selection
-PASS Selecting texts across <input type=submit> should not cancel selection
-PASS Selecting texts across <input type=tel> should not cancel selection
-PASS Selecting texts across <input type=text> should not cancel selection
-PASS Selecting texts across <input type=time> should not cancel selection
-PASS Selecting texts across <input type=url> should not cancel selection
-PASS Selecting texts across <input type=week> should not cancel selection
-Harness: the test ran to completion.
-
-#EOF
-#EOF
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt
new file mode 100644
index 0000000..4023ca0
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt
@@ -0,0 +1,35 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [804, 604],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='overlay'",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [2, 2, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/34176-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/34176-expected.png
deleted file mode 100644
index 20b3422..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/34176-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/52776-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/52776-expected.png
deleted file mode 100644
index 70be8ec..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/52776-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-boundary-values-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-boundary-values-expected.png
deleted file mode 100644
index fe9477b..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-boundary-values-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-optimums-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-optimums-expected.png
deleted file mode 100644
index 14f92fd1..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-optimums-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-styles-changing-pseudo-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-styles-changing-pseudo-expected.png
deleted file mode 100644
index 6dd9d55..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-styles-changing-pseudo-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-styles-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-styles-expected.png
deleted file mode 100644
index 49be4ce6..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-styles-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/focus-contenteditable-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/focus-contenteditable-expected.png
deleted file mode 100644
index 3e25dad..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/focus-contenteditable-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/title-text-property-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/title-text-property-expected.txt
deleted file mode 100644
index 9add247..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/dom/title-text-property-expected.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Original title is: 'Original Title'
-Setting new title to: 'This is the new title'
-New title is: 'This is the new title'
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/parser/bad-xml-slash-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/parser/bad-xml-slash-expected.png
deleted file mode 100644
index d7fb6c4..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/parser/bad-xml-slash-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/parser/entity-comment-in-textarea-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/parser/entity-comment-in-textarea-expected.png
deleted file mode 100644
index 330ea6b..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/parser/entity-comment-in-textarea-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/parser/open-comment-in-textarea-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/parser/open-comment-in-textarea-expected.png
deleted file mode 100644
index b0c2aba..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/fast/parser/open-comment-in-textarea-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-1-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-1-and-click-expected.png
deleted file mode 100644
index 6f5b21ef..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-1-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-10-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-10-and-click-expected.png
deleted file mode 100644
index 632a04b6..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-10-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-2-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-2-and-click-expected.png
deleted file mode 100644
index 7fb98033..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-2-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-3-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-3-and-click-expected.png
deleted file mode 100644
index 41bfc61..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-3-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-4-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-4-and-click-expected.png
deleted file mode 100644
index 73766a5..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-4-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-5-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-5-and-click-expected.png
deleted file mode 100644
index 12f40610..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-5-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-6-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-6-and-click-expected.png
deleted file mode 100644
index 632a04b6..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-6-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-7-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-7-and-click-expected.png
deleted file mode 100644
index 632a04b6..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-7-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-8-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-8-and-click-expected.png
deleted file mode 100644
index 80cc312c..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-8-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-9-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-9-and-click-expected.png
deleted file mode 100644
index 435885f..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-add-summary-9-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-no-summary4-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-no-summary4-expected.png
deleted file mode 100644
index 392e562..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-no-summary4-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-open-javascript-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-open-javascript-expected.png
deleted file mode 100644
index f4f31f0..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-open-javascript-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-open2-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-open2-expected.png
deleted file mode 100644
index 3adf8e66..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-open2-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-open4-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-open4-expected.png
deleted file mode 100644
index 3adf8e66..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-open4-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-1-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-1-and-click-expected.png
deleted file mode 100644
index e64c030..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-1-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-2-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-2-and-click-expected.png
deleted file mode 100644
index f6139448..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-2-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-3-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-3-and-click-expected.png
deleted file mode 100644
index 7c2db47..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-3-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-4-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-4-and-click-expected.png
deleted file mode 100644
index 4a74fdf..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-4-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-5-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-5-and-click-expected.png
deleted file mode 100644
index cb1ccd7..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-5-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-6-and-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-6-and-click-expected.png
deleted file mode 100644
index 7d1c03c..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-remove-summary-6-and-click-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-replace-summary-child-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-replace-summary-child-expected.png
deleted file mode 100644
index 27be22e..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-replace-summary-child-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-replace-text-expected.png b/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-replace-text-expected.png
deleted file mode 100644
index da1dc78..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/web-components-v0-disabled/html/details_summary/details-replace-text-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt b/third_party/blink/web_tests/platform/win/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
new file mode 100644
index 0000000..a7f17797a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/compositing/layer-creation/main-thread-scrolling-non-composited-fixed-overflow-hidden-expected.txt
@@ -0,0 +1,28 @@
+TEST
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 5021],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [36, 20],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt
new file mode 100644
index 0000000..ccc7ffc
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/scrolled-iframe-scrollbar-change-expected.txt
@@ -0,0 +1,39 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [804, 604],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [10, 2, 769, 592],
+        [787, 2, 15, 600]
+      ]
+    },
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='overlay'",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [2, 2, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/win/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-clone-expected.txt b/third_party/blink/web_tests/platform/win/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-clone-expected.txt
deleted file mode 100644
index f840958..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-clone-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-PASS cloned.value is target.value
-PASS internals.shadowPseudoId(clonedShadowRoot.firstChild) is internals.shadowPseudoId(targetShadowRoot.firstChild)
-PASS internals.shadowPseudoId(clonedShadowRoot.firstChild.firstChild) is internals.shadowPseudoId(targetShadowRoot.firstChild.firstChild)
-PASS internals.shadowPseudoId(clonedShadowRoot.firstChild.firstChild.firstChild) is internals.shadowPseudoId(targetShadowRoot.firstChild.firstChild.firstChild)
-PASS clonedShadowRoot.firstChild.firstChild.firstChild.style.width is "70%"
-PASS targetShadowRoot.firstChild.firstChild.firstChild.style.width is "50%"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/win/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-element-markup-expected.txt b/third_party/blink/web_tests/platform/win/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-element-markup-expected.txt
deleted file mode 100644
index a39a6a3..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-element-markup-expected.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-
-Both meter elements should have a nested shadow box with a width specified:
-| "
-    "
-| <meter>
-|   max="100"
-|   value="70"
-|   <shadow:root>
-|     <div>
-|       pseudo="-webkit-meter-inner-element"
-|       shadow:pseudoId="-webkit-meter-inner-element"
-|       <div>
-|         pseudo="-webkit-meter-bar"
-|         shadow:pseudoId="-webkit-meter-bar"
-|         <div>
-|           pseudo="-webkit-meter-optimum-value"
-|           style="width: 70%;"
-|           shadow:pseudoId="-webkit-meter-optimum-value"
-|     <div>
-|       pseudo="-internal-fallback"
-|       shadow:pseudoId="-internal-fallback"
-|       <slot>
-|         name="user-agent-default-slot"
-| "
-    "
-| <meter>
-|   high="6"
-|   low="3"
-|   max="10"
-|   min="0"
-|   optimum="5"
-|   value="10"
-|   <shadow:root>
-|     <div>
-|       pseudo="-webkit-meter-inner-element"
-|       shadow:pseudoId="-webkit-meter-inner-element"
-|       <div>
-|         pseudo="-webkit-meter-bar"
-|         shadow:pseudoId="-webkit-meter-bar"
-|         <div>
-|           pseudo="-webkit-meter-suboptimum-value"
-|           style="width: 100%;"
-|           shadow:pseudoId="-webkit-meter-suboptimum-value"
-|     <div>
-|       pseudo="-internal-fallback"
-|       shadow:pseudoId="-internal-fallback"
-|       <slot>
-|         name="user-agent-default-slot"
-| "
-    "
-| <meter>
-|   high="6"
-|   low="3"
-|   max="10"
-|   min="0"
-|   optimum="0"
-|   value="10"
-|   <shadow:root>
-|     <div>
-|       pseudo="-webkit-meter-inner-element"
-|       shadow:pseudoId="-webkit-meter-inner-element"
-|       <div>
-|         pseudo="-webkit-meter-bar"
-|         shadow:pseudoId="-webkit-meter-bar"
-|         <div>
-|           pseudo="-webkit-meter-even-less-good-value"
-|           style="width: 100%;"
-|           shadow:pseudoId="-webkit-meter-even-less-good-value"
-|     <div>
-|       pseudo="-internal-fallback"
-|       shadow:pseudoId="-internal-fallback"
-|       <slot>
-|         name="user-agent-default-slot"
-| "
-  "
diff --git a/third_party/blink/web_tests/platform/win7/virtual/web-components-v0-disabled/external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/web-components-v0-disabled/external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll-expected.txt
deleted file mode 100644
index a9b7884..0000000
--- a/third_party/blink/web_tests/platform/win7/virtual/web-components-v0-disabled/external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-FAIL Tests that the target_div gets scrollend event when dragging scroll on target. promise_test: Unhandled rejection with value: "target_div did not receive scrollend event after dragging scroll on target."
-FAIL Tests that the target_div gets scrollend event when click scrollbar on target. promise_test: Unhandled rejection with value: "target_div did not receive scrollend event after clicking scrollbar on target."
-FAIL Tests that the target_div gets scrollend event when drag the thumb of target. promise_test: Unhandled rejection with value: "target_div did not receive scrollend event after dragging the thumb of target."
-PASS Tests that the target_div gets scrollend event when send DOWN key to target.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/win7/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-clone-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-clone-expected.txt
deleted file mode 100644
index f840958..0000000
--- a/third_party/blink/web_tests/platform/win7/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-clone-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-PASS cloned.value is target.value
-PASS internals.shadowPseudoId(clonedShadowRoot.firstChild) is internals.shadowPseudoId(targetShadowRoot.firstChild)
-PASS internals.shadowPseudoId(clonedShadowRoot.firstChild.firstChild) is internals.shadowPseudoId(targetShadowRoot.firstChild.firstChild)
-PASS internals.shadowPseudoId(clonedShadowRoot.firstChild.firstChild.firstChild) is internals.shadowPseudoId(targetShadowRoot.firstChild.firstChild.firstChild)
-PASS clonedShadowRoot.firstChild.firstChild.firstChild.style.width is "70%"
-PASS targetShadowRoot.firstChild.firstChild.firstChild.style.width is "50%"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/win7/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-element-markup-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-element-markup-expected.txt
deleted file mode 100644
index a39a6a3..0000000
--- a/third_party/blink/web_tests/platform/win7/virtual/web-components-v0-disabled/fast/dom/HTMLMeterElement/meter-element-markup-expected.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-
-Both meter elements should have a nested shadow box with a width specified:
-| "
-    "
-| <meter>
-|   max="100"
-|   value="70"
-|   <shadow:root>
-|     <div>
-|       pseudo="-webkit-meter-inner-element"
-|       shadow:pseudoId="-webkit-meter-inner-element"
-|       <div>
-|         pseudo="-webkit-meter-bar"
-|         shadow:pseudoId="-webkit-meter-bar"
-|         <div>
-|           pseudo="-webkit-meter-optimum-value"
-|           style="width: 70%;"
-|           shadow:pseudoId="-webkit-meter-optimum-value"
-|     <div>
-|       pseudo="-internal-fallback"
-|       shadow:pseudoId="-internal-fallback"
-|       <slot>
-|         name="user-agent-default-slot"
-| "
-    "
-| <meter>
-|   high="6"
-|   low="3"
-|   max="10"
-|   min="0"
-|   optimum="5"
-|   value="10"
-|   <shadow:root>
-|     <div>
-|       pseudo="-webkit-meter-inner-element"
-|       shadow:pseudoId="-webkit-meter-inner-element"
-|       <div>
-|         pseudo="-webkit-meter-bar"
-|         shadow:pseudoId="-webkit-meter-bar"
-|         <div>
-|           pseudo="-webkit-meter-suboptimum-value"
-|           style="width: 100%;"
-|           shadow:pseudoId="-webkit-meter-suboptimum-value"
-|     <div>
-|       pseudo="-internal-fallback"
-|       shadow:pseudoId="-internal-fallback"
-|       <slot>
-|         name="user-agent-default-slot"
-| "
-    "
-| <meter>
-|   high="6"
-|   low="3"
-|   max="10"
-|   min="0"
-|   optimum="0"
-|   value="10"
-|   <shadow:root>
-|     <div>
-|       pseudo="-webkit-meter-inner-element"
-|       shadow:pseudoId="-webkit-meter-inner-element"
-|       <div>
-|         pseudo="-webkit-meter-bar"
-|         shadow:pseudoId="-webkit-meter-bar"
-|         <div>
-|           pseudo="-webkit-meter-even-less-good-value"
-|           style="width: 100%;"
-|           shadow:pseudoId="-webkit-meter-even-less-good-value"
-|     <div>
-|       pseudo="-internal-fallback"
-|       shadow:pseudoId="-internal-fallback"
-|       <slot>
-|         name="user-agent-default-slot"
-| "
-  "
diff --git a/third_party/blink/web_tests/virtual/dark-mode-default/dark-mode/images/image-as-shader-expected.png b/third_party/blink/web_tests/virtual/dark-mode-default/dark-mode/images/image-as-shader-expected.png
new file mode 100644
index 0000000..30310d5
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/dark-mode-default/dark-mode/images/image-as-shader-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/image-as-shader-expected.png b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/image-as-shader-expected.png
new file mode 100644
index 0000000..30310d5
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/image-as-shader-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-none/dark-mode/images/image-as-shader-expected.png b/third_party/blink/web_tests/virtual/dark-mode-images-filter-none/dark-mode/images/image-as-shader-expected.png
new file mode 100644
index 0000000..30310d5
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/dark-mode-images-filter-none/dark-mode/images/image-as-shader-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-grayscale/dark-mode/images/image-as-shader-expected.png b/third_party/blink/web_tests/virtual/dark-mode-images-grayscale/dark-mode/images/image-as-shader-expected.png
new file mode 100644
index 0000000..30310d5
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/dark-mode-images-grayscale/dark-mode/images/image-as-shader-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/text-antialias/hyphens/hyphens-auto-nowrap-expected.html b/third_party/blink/web_tests/virtual/text-antialias/hyphens/hyphens-auto-nowrap-expected.html
index 618993e..8054af3 100644
--- a/third_party/blink/web_tests/virtual/text-antialias/hyphens/hyphens-auto-nowrap-expected.html
+++ b/third_party/blink/web_tests/virtual/text-antialias/hyphens/hyphens-auto-nowrap-expected.html
@@ -8,6 +8,6 @@
 }
 </style>
 <div lang="en-us">
-  <div>hy-<br>phenation</div>
+  <div>hy-<br>phen-<br>ation</div>
   <div>hyphenation</div>
 </div>
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 88d9550..7e49a267 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -16,6 +16,7 @@
 [Worker]     method constructor
 [Worker]     setter onabort
 [Worker] interface AudioDecoder
+[Worker]     static method isConfigSupported
 [Worker]     attribute @@toStringTag
 [Worker]     getter decodeQueueSize
 [Worker]     getter state
@@ -25,6 +26,16 @@
 [Worker]     method decode
 [Worker]     method flush
 [Worker]     method reset
+[Worker] interface AudioEncoder
+[Worker]     attribute @@toStringTag
+[Worker]     getter encodeQueueSize
+[Worker]     getter state
+[Worker]     method close
+[Worker]     method configure
+[Worker]     method constructor
+[Worker]     method encode
+[Worker]     method flush
+[Worker]     method reset
 [Worker] interface AudioFrame
 [Worker]     attribute @@toStringTag
 [Worker]     getter buffer
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 f0c175c..efef17d 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
@@ -271,6 +271,7 @@
     method resume
     method suspend
 interface AudioDecoder
+    static method isConfigSupported
     attribute @@toStringTag
     getter decodeQueueSize
     getter state
@@ -284,6 +285,16 @@
     attribute @@toStringTag
     getter maxChannelCount
     method constructor
+interface AudioEncoder
+    attribute @@toStringTag
+    getter encodeQueueSize
+    getter state
+    method close
+    method configure
+    method constructor
+    method encode
+    method flush
+    method reset
 interface AudioFrame
     attribute @@toStringTag
     getter buffer
diff --git a/third_party/farmhash/BUILD.gn b/third_party/farmhash/BUILD.gn
index 3af22438..722f95e 100644
--- a/third_party/farmhash/BUILD.gn
+++ b/third_party/farmhash/BUILD.gn
@@ -6,6 +6,14 @@
   include_dirs = [ "src/src" ]
 }
 
+config("farmhash-warnings") {
+  cflags = []
+
+  # The reduction of farmhash files to a minimum triggers -Wunused-function
+  # warnings in farmhash.c
+  cflags += [ "-Wno-unused-function" ]
+}
+
 source_set("farmhash") {
   public = [ "src/src/farmhash.h" ]
 
@@ -13,6 +21,7 @@
 
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
+  configs += [ ":farmhash-warnings" ]
 
   public_configs = [ ":farmhash_include" ]
 }
diff --git a/tools/clang/rewrite_raw_ptr_fields/RewriteRawPtrFields.cpp b/tools/clang/rewrite_raw_ptr_fields/RewriteRawPtrFields.cpp
index dd196f1..0ae7637 100644
--- a/tools/clang/rewrite_raw_ptr_fields/RewriteRawPtrFields.cpp
+++ b/tools/clang/rewrite_raw_ptr_fields/RewriteRawPtrFields.cpp
@@ -1086,7 +1086,7 @@
   // TODO(lukasza): It is unclear why |traverse| below is needed.  Maybe it can
   // be removed if https://bugs.llvm.org/show_bug.cgi?id=46287 is fixed.
   match_finder.addMatcher(
-      traverse(clang::ast_type_traits::TK_AsIs,
+      traverse(clang::TraversalKind::TK_AsIs,
                cxxConstructExpr(templated_function_arg_matcher)),
       &affected_expr_rewriter);
 
diff --git a/tools/ipc_fuzzer/fuzzer/fuzzer.cc b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
index e458d00e..7319b4e 100644
--- a/tools/ipc_fuzzer/fuzzer/fuzzer.cc
+++ b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
@@ -1670,7 +1670,7 @@
       case 0: {
         // network::DataElement::Type::TYPE_BYTES
         if (RandEvent(2)) {
-          p->SetToEmptyBytes();
+          p->SetToBytes(nullptr, 0);
         } else {
           char data[256];
           int data_len = RandInRange(sizeof(data));
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 3a944c31..a30a4f5 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -284,8 +284,6 @@
       'Libfuzzer Upload Mac ASan': 'libfuzzer_mac_asan_shared_release_bot',
       'Libfuzzer Upload Windows ASan': 'libfuzzer_windows_asan_release_bot',
 
-      'Linux remote_run Builder': 'release_bot',
-      'Linux remote_run Tester': 'release_bot',
       'Linux Viz': 'release_trybot',
       'linux-ash-chromium-builder-fyi-rel': 'chromeos_with_codecs_release_bot',
       'linux-lacros-builder-fyi-rel': 'lacros_on_linux_release_bot',
@@ -1521,7 +1519,7 @@
     ],
 
     'angle_specific_release_trybot_ios': [
-      'angle_specific_tests', 'shared_release_trybot', 'ios', 'ios_simulator', 'ios_cpu_x64', 'xctest',
+      'angle_specific_tests', 'release_trybot', 'ios', 'ios_simulator', 'ios_cpu_x64', 'xctest',
     ],
 
     'angle_specific_release_trybot_x86': [
diff --git a/tools/mb/mb_config_expectations/chromium.angle.json b/tools/mb/mb_config_expectations/chromium.angle.json
index f30b87c3..e796400c9 100644
--- a/tools/mb/mb_config_expectations/chromium.angle.json
+++ b/tools/mb/mb_config_expectations/chromium.angle.json
@@ -96,7 +96,7 @@
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
       "enable_run_ios_unittests_with_xctest": true,
-      "is_component_build": true,
+      "is_component_build": false,
       "is_debug": false,
       "symbol_level": 1,
       "target_cpu": "x64",
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json
index f0f86cf4..fd37073 100644
--- a/tools/mb/mb_config_expectations/chromium.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -130,20 +130,6 @@
       "use_goma": true
     }
   },
-  "Linux remote_run Builder": {
-    "gn_args": {
-      "is_component_build": false,
-      "is_debug": false,
-      "use_goma": true
-    }
-  },
-  "Linux remote_run Tester": {
-    "gn_args": {
-      "is_component_build": false,
-      "is_debug": false,
-      "use_goma": true
-    }
-  },
   "Mac Builder Next": {
     "gn_args": {
       "ffmpeg_branding": "Chrome",
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.angle.json b/tools/mb/mb_config_expectations/tryserver.chromium.angle.json
index 9f5a6096..d855b9d 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.angle.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.angle.json
@@ -113,7 +113,7 @@
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
       "enable_run_ios_unittests_with_xctest": true,
-      "is_component_build": true,
+      "is_component_build": false,
       "is_debug": false,
       "symbol_level": 1,
       "target_cpu": "x64",
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 5c1dd3ed..66e6316 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -9148,6 +9148,16 @@
   </description>
 </action>
 
+<action name="IOS.DefaultBrowserFullscreenPromo.RemindMeTapped">
+  <owner>thegreenfrog@chromium.org</owner>
+  <owner>rohitrao@chromium.org</owner>
+  <description>
+    The user tapped the &quot;Remind Me Later&quot; button in the default
+    browser fullscreen promo modal that takes the user to the Settings app. iOS
+    only.
+  </description>
+</action>
+
 <action name="IOS.DefaultBrowserNTPPromoTapped">
   <owner>thegreenfrog@chromium.org</owner>
   <owner>rohitrao@chromium.org</owner>
@@ -12037,6 +12047,7 @@
 </action>
 
 <action name="ManagedUsers_Chromeos_Sync_Recovered">
+  <obsolete>Legacy supervised users are deprecated</obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
 </action>
@@ -22095,6 +22106,15 @@
   </description>
 </action>
 
+<action name="Signin_Impression_FromUserManager" not_user_triggered="true">
+  <owner>jkrcal@chromium.org</owner>
+  <owner>droger@chromium.org</owner>
+  <description>
+    Recorded when showing sign in entry in the profile creation flow (as part of
+    the profile picker).
+  </description>
+</action>
+
 <action name="Signin_ImpressionWithAccount_FromAvatarBubbleSignin"
     not_user_triggered="true">
   <owner>msarda@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a4a28730..bd1c8c4 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -21831,6 +21831,7 @@
   <int value="7" label="File IO error"/>
   <int value="8" label="Dlp interruption"/>
   <int value="9" label="Low disk space"/>
+  <int value="10" label="HDCP interruption"/>
 </enum>
 
 <enum name="EnhancedBookmarkViewMode">
@@ -30824,6 +30825,7 @@
   <int value="3748" label="SamePartyCookieInclusionOverruledSameSite"/>
   <int value="3749" label="EmbedElementWithoutTypeSrcChanged"/>
   <int value="3750" label="PaymentHandlerStandardizedPaymentMethodIdentifier"/>
+  <int value="3751" label="WebCodecsAudioEncoder"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -39351,6 +39353,7 @@
 <enum name="IOSDefaultBrowserFullscreenPromoAction">
   <int value="0" label="Action Button"/>
   <int value="1" label="Cancel"/>
+  <int value="2" label="Remind Me Later"/>
 </enum>
 
 <enum name="IOSDeviceThermalState">
@@ -41067,6 +41070,10 @@
   <int value="8" label="HandleUpdateOrigins Invalid"/>
 </enum>
 
+<enum name="LaunchCause">
+  <int value="0" label="Other"/>
+</enum>
+
 <enum name="LauncherRankingItemType">
   <summary>
     The type of a result in the Chrome OS launcher, simplified to fewer
@@ -55774,6 +55781,7 @@
   <int value="1704" label="Get Help with Chrome OS"/>
   <int value="1705" label="Report an Issue"/>
   <int value="1706" label="View Terms of Service"/>
+  <int value="1707" label="Open Diagnostics App"/>
   <int value="1800" label="View Add Kerberos Ticket V2"/>
   <int value="1801" label="Remove Kerberos Ticket V2"/>
   <int value="1802" label="Set Active Kerberos Ticket V2"/>
diff --git a/tools/metrics/histograms/histograms_xml/android/histograms.xml b/tools/metrics/histograms/histograms_xml/android/histograms.xml
index 3b549d0..89fc3e4 100644
--- a/tools/metrics/histograms/histograms_xml/android/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/android/histograms.xml
@@ -1188,6 +1188,18 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Android.IsolatedSplits.ClassLoaderReplaced"
+    enum="BooleanYesNo" expires_after="2022-01-10">
+<!-- Name completed by histogram_suffixes name="AndroidFeatureModuleName" -->
+
+  <owner>cduvall@chromium.org</owner>
+  <owner>agrieve@chromium.org</owner>
+  <summary>
+    Whether a split Context has had its ClassLoader replaced due to b/172602571.
+    This is recorded every time a split Context is created.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Android.IsolatedSplits.ContextCreateTime"
     units="ms" expires_after="2021-12-07">
 <!-- Name completed by histogram_suffixes name="AndroidFeatureModuleName" -->
diff --git a/tools/metrics/histograms/histograms_xml/ash/histograms.xml b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
index 332b005..d2421b86 100644
--- a/tools/metrics/histograms/histograms_xml/ash/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
@@ -336,6 +336,26 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.Clipboard.ConsecutiveCopies" units="times"
+    expires_after="2021-09-01">
+  <owner>newcomer@chromium.org</owner>
+  <owner>multipaste@google.com</owner>
+  <summary>
+    The number of consecutive copies in the user session, recorded when a paste
+    occurs.
+  </summary>
+</histogram>
+
+<histogram name="Ash.Clipboard.ConsecutivePastes" units="times"
+    expires_after="2021-09-01">
+  <owner>newcomer@chromium.org</owner>
+  <owner>multipaste@google.com</owner>
+  <summary>
+    The number of consecutive pastes in the user session, recorded when a copy
+    occurs. Includes pastes from Clipboard History.
+  </summary>
+</histogram>
+
 <histogram name="Ash.ClipboardHistory.ContextMenu.DisplayFormatDeleted"
     enum="ClipboardHistoryDisplayFormat" expires_after="2021-09-01">
   <owner>andrewxu@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/blink/histograms.xml b/tools/metrics/histograms/histograms_xml/blink/histograms.xml
index 784c786..f4bb1a74 100644
--- a/tools/metrics/histograms/histograms_xml/blink/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/blink/histograms.xml
@@ -33,7 +33,7 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Animate.UpdateTime" units="microseconds"
+<histogram base="true" name="Blink.Animate.UpdateTime" units="microseconds"
     expires_after="2021-05-23">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -432,8 +432,8 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.CompositingAssignments.UpdateTime" units="microseconds"
-    expires_after="2021-07-04">
+<histogram base="true" name="Blink.CompositingAssignments.UpdateTime"
+    units="microseconds" expires_after="2021-07-04">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -451,8 +451,8 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.CompositingCommit.UpdateTime" units="microseconds"
-    expires_after="2021-07-04">
+<histogram base="true" name="Blink.CompositingCommit.UpdateTime"
+    units="microseconds" expires_after="2021-07-04">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -474,8 +474,8 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.CompositingInputs.UpdateTime" units="microseconds"
-    expires_after="2021-07-04">
+<histogram base="true" name="Blink.CompositingInputs.UpdateTime"
+    units="microseconds" expires_after="2021-07-04">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -591,7 +591,7 @@
   <summary>Image codec inferred during decode.</summary>
 </histogram>
 
-<histogram name="Blink.DisplayLockIntersectionObserver.UpdateTime"
+<histogram base="true" name="Blink.DisplayLockIntersectionObserver.UpdateTime"
     units="microseconds" expires_after="2021-06-06">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -843,8 +843,8 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.ForcedStyleAndLayout.UpdateTime" units="microseconds"
-    expires_after="2021-05-23">
+<histogram base="true" name="Blink.ForcedStyleAndLayout.UpdateTime"
+    units="microseconds" expires_after="2021-05-23">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -861,8 +861,8 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.HandleInputEvents.UpdateTime" units="microseconds"
-    expires_after="2021-07-04">
+<histogram base="true" name="Blink.HandleInputEvents.UpdateTime"
+    units="microseconds" expires_after="2021-07-04">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -879,8 +879,8 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.HitTestDocumentUpdate.UpdateTime" units="microseconds"
-    expires_after="2021-07-04">
+<histogram base="true" name="Blink.HitTestDocumentUpdate.UpdateTime"
+    units="microseconds" expires_after="2021-07-04">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -1089,8 +1089,8 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.ImplCompositorCommit.UpdateTime" units="microseconds"
-    expires_after="2021-07-04">
+<histogram base="true" name="Blink.ImplCompositorCommit.UpdateTime"
+    units="microseconds" expires_after="2021-07-04">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -1117,8 +1117,8 @@
   <summary>Records if a GestureScrollBegin is for cursor control.</summary>
 </histogram>
 
-<histogram name="Blink.IntersectionObservation.UpdateTime" units="microseconds"
-    expires_after="2021-05-23">
+<histogram base="true" name="Blink.IntersectionObservation.UpdateTime"
+    units="microseconds" expires_after="2021-05-23">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -1136,7 +1136,7 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.JavascriptIntersectionObserver.UpdateTime"
+<histogram base="true" name="Blink.JavascriptIntersectionObserver.UpdateTime"
     units="microseconds" expires_after="2021-06-06">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -1195,7 +1195,7 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Layout.UpdateTime" units="microseconds"
+<histogram base="true" name="Blink.Layout.UpdateTime" units="microseconds"
     expires_after="2021-07-04">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -1249,7 +1249,7 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.LazyLoadIntersectionObserver.UpdateTime"
+<histogram base="true" name="Blink.LazyLoadIntersectionObserver.UpdateTime"
     units="microseconds" expires_after="2021-06-06">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -1513,7 +1513,7 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.MainFrame.UpdateTime" units="microseconds"
+<histogram base="true" name="Blink.MainFrame.UpdateTime" units="microseconds"
     expires_after="2021-06-20">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -1546,7 +1546,7 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.MediaIntersectionObserver.UpdateTime"
+<histogram base="true" name="Blink.MediaIntersectionObserver.UpdateTime"
     units="microseconds" expires_after="2021-06-06">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -1700,7 +1700,7 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Paint.UpdateTime" units="microseconds"
+<histogram base="true" name="Blink.Paint.UpdateTime" units="microseconds"
     expires_after="2021-06-20">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -1718,7 +1718,7 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.PrePaint.UpdateTime" units="microseconds"
+<histogram base="true" name="Blink.PrePaint.UpdateTime" units="microseconds"
     expires_after="2021-06-27">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -1843,8 +1843,8 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.ScrollingCoordinator.UpdateTime" units="microseconds"
-    expires_after="M88">
+<histogram base="true" name="Blink.ScrollingCoordinator.UpdateTime"
+    units="microseconds" expires_after="M88">
   <obsolete>
     Merged into Blink.CompositingCommit.UpdateTime in http://crrev.com/815947 in
     M88.
@@ -2030,7 +2030,7 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Style.UpdateTime" units="microseconds"
+<histogram base="true" name="Blink.Style.UpdateTime" units="microseconds"
     expires_after="2021-05-23">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -2393,8 +2393,8 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.WaitForCommit.UpdateTime" units="microseconds"
-    expires_after="2021-07-04">
+<histogram base="true" name="Blink.WaitForCommit.UpdateTime"
+    units="microseconds" expires_after="2021-07-04">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index c66e24a..c491856e 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -775,6 +775,7 @@
   <suffix name="chrome" label="Chrome Module"/>
   <suffix name="dev_ui" label="Developer UI Module"/>
   <suffix name="extra_icu" label="Extra ICU Module"/>
+  <suffix name="feedv2" label="Feed V2 Module"/>
   <suffix name="image_editor" label="Image Editor Module"/>
   <suffix name="stack_unwinder" label="Stack Unwinder Module"/>
   <suffix name="tab_ui" label="Tab Management Module"/>
@@ -828,6 +829,7 @@
       name="Android.FeatureModules.UncachedInstallDuration.PendingDownload"/>
   <affected-histogram
       name="Android.FeatureModules.UncachedInstallDuration.PendingInstall"/>
+  <affected-histogram name="Android.IsolatedSplits.ClassLoaderReplaced"/>
   <affected-histogram name="Android.IsolatedSplits.ContextCreateTime"/>
   <affected-histogram name="Android.IsolatedSplits.PreloadWaitTime"/>
 </histogram_suffixes>
@@ -8211,6 +8213,8 @@
   </suffix>
   <suffix name="FeedJournalDatabase"
       label="Database for Feed journal storage."/>
+  <suffix name="FeedKeyValueDatabase"
+      label="Database for key value cache used in feed rendering."/>
   <suffix name="FeedStorageDatabase" label="Databases for Feed Storage.">
     <obsolete>
       Deprecated since 08/18.
diff --git a/tools/metrics/histograms/histograms_xml/ios/histograms.xml b/tools/metrics/histograms/histograms_xml/ios/histograms.xml
index 292c252..79de568a 100644
--- a/tools/metrics/histograms/histograms_xml/ios/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/ios/histograms.xml
@@ -272,6 +272,26 @@
   </summary>
 </histogram>
 
+<histogram name="IOS.DefaultBrowserFullscreenPromoRemindMe"
+    enum="IOSDefaultBrowserFullscreenPromoAction" expires_after="2021-03-01">
+  <owner>thegreenfrog@chromium.org</owner>
+  <owner>rohitrao@chromium.org</owner>
+  <summary>
+    The action taken by the user in response to the default browser promo with
+    the Remind Me Later button.
+  </summary>
+</histogram>
+
+<histogram name="IOS.DefaultBrowserFullscreenPromoRemindMeSecondPromo"
+    enum="IOSDefaultBrowserFullscreenPromoAction" expires_after="2021-03-01">
+  <owner>thegreenfrog@chromium.org</owner>
+  <owner>rohitrao@chromium.org</owner>
+  <summary>
+    The action taken by the user in response to the second default browser promo
+    after tapping on the Remind Me Later button.
+  </summary>
+</histogram>
+
 <histogram name="IOS.Dialogs.JavaScriptDialogClosed"
     enum="IOSJavaScriptDialogDismissalCause" expires_after="M80">
   <owner>kkhorimoto@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/mobile/histograms.xml b/tools/metrics/histograms/histograms_xml/mobile/histograms.xml
index c8037a9..2beb33a8 100644
--- a/tools/metrics/histograms/histograms_xml/mobile/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/mobile/histograms.xml
@@ -1081,6 +1081,19 @@
   </summary>
 </histogram>
 
+<histogram name="MobileStartup.Experimental.LaunchCause" enum="LaunchCause"
+    expires_after="2022-01-07">
+  <owner>mthiesse@chromium.org</owner>
+  <owner>tedchoc@chromium.org</owner>
+  <owner>yfriedman@chromium.org</owner>
+  <summary>
+    Records what caused Chrome to be launched.
+
+    Recorded for all types of ChromeActivity, in all cases where Chrome becomes
+    visible to the user.
+  </summary>
+</histogram>
+
 <histogram name="MobileStartup.IntentToCreationTime" units="ms"
     expires_after="2021-07-04">
   <owner>tedchoc@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index afac0ce9..9219e87 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -8440,7 +8440,7 @@
 </histogram>
 
 <histogram name="LiteVideo.HintAgent.ActiveThrottleSize" units="count"
-    expires_after="M90">
+    expires_after="M94">
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -8470,7 +8470,7 @@
 </histogram>
 
 <histogram name="LiteVideo.NavigationMetrics.FrameRebufferMapSize"
-    units="count" expires_after="M90">
+    units="count" expires_after="M94">
   <owner>mcrouse@chromium.org</owner>
   <owner>rajendrant@chromium.org</owner>
   <summary>
@@ -8480,7 +8480,7 @@
 </histogram>
 
 <histogram name="LiteVideo.OriginHints.ParseResult" enum="BooleanSuccess"
-    expires_after="M90">
+    expires_after="M94">
   <owner>mcrouse@chromium.org</owner>
   <owner>rajendrant@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/page/histograms.xml b/tools/metrics/histograms/histograms_xml/page/histograms.xml
index 02c82732..c2b19fd 100644
--- a/tools/metrics/histograms/histograms_xml/page/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/page/histograms.xml
@@ -1231,9 +1231,10 @@
 </histogram>
 
 <histogram name="PageLoad.FrameCounts.AdFrames.PerFrame.CreativeOriginStatus"
-    enum="CrossOriginCreativeStatus" expires_after="2020-12-31">
+    enum="CrossOriginCreativeStatus" expires_after="2021-06-30">
   <owner>cammie@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
+  <owner>chrome-ads-histograms@google.com</owner>
   <summary>
     For each identified ad frame, whether the origin of the ad creative frame
     matches or differs from the origin of the main frame.
@@ -1247,9 +1248,10 @@
 
 <histogram
     name="PageLoad.FrameCounts.AdFrames.PerFrame.CreativeOriginStatusWithThrottling"
-    enum="CrossOriginCreativeStatusWithThrottling" expires_after="2020-12-31">
+    enum="CrossOriginCreativeStatusWithThrottling" expires_after="2021-06-30">
   <owner>cammie@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
+  <owner>chrome-ads-histograms@google.com</owner>
   <summary>
     For each identified ad frame, whether the origin of the ad creative frame
     matches or differs from the origin of the main frame, further split by
diff --git a/tools/metrics/histograms/histograms_xml/stability/histograms.xml b/tools/metrics/histograms/histograms_xml/stability/histograms.xml
index 47b81204b..666cac41 100644
--- a/tools/metrics/histograms/histograms_xml/stability/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/stability/histograms.xml
@@ -436,7 +436,7 @@
   </summary>
 </histogram>
 
-<histogram name="Stability.iOS.UTE.MobileSessionAppWillTerminateReceived"
+<histogram name="Stability.iOS.UTE.MobileSessionAppWillTerminateWasReceived"
     enum="AppWillTerminateReceived" expires_after="2021-04-29">
   <owner>eugenebut@chromium.org</owner>
   <owner>olivierrobin@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/subresource/histograms.xml b/tools/metrics/histograms/histograms_xml/subresource/histograms.xml
index 542b42a..893de4e 100644
--- a/tools/metrics/histograms/histograms_xml/subresource/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/subresource/histograms.xml
@@ -32,6 +32,87 @@
   </summary>
 </histogram>
 
+<histogram name="SubresourceFilter.CnameAlias.Browser.HadAliases"
+    units="BooleanHadAliases" expires_after="2021-06-18">
+  <owner>cammie@chromium.org</owner>
+  <owner>chrome-ads-histograms@google.com</owner>
+  <summary>
+    Records whether or not a given NavigationRequest has a nonempty vector of
+    CNAME aliases. Only recorded if
+    features::kSendCnameAliasesToSubresourceFilterFromBrowser is enabled.
+    Recorded in a method called in the SubframeNavigationFilteringThrottle's
+    destructor.
+  </summary>
+</histogram>
+
+<histogram name="SubresourceFilter.CnameAlias.Browser.InvalidCount"
+    units="count" expires_after="2021-06-18">
+  <owner>cammie@chromium.org</owner>
+  <owner>chrome-ads-histograms@google.com</owner>
+  <summary>
+    Counts the number of invalid CNAME aliases seen for a NavigationRequest by
+    the SubframeNavigationFilteringThrottle. Only recorded if
+    features::kSendCnameAliasesToSubresourceFilterFromBrowser is enabled and the
+    request has a nonempty vector of CNAME aliases. Recorded in a method called
+    in the SubframeNavigationFilteringThrottle's destructor.
+  </summary>
+</histogram>
+
+<histogram name="SubresourceFilter.CnameAlias.Browser.ListLength"
+    units="length" expires_after="2021-06-18">
+  <owner>cammie@chromium.org</owner>
+  <owner>chrome-ads-histograms@google.com</owner>
+  <summary>
+    Records the lengths of each CNAME aliases vector passed to the
+    SubframeNavigationFilteringThrottle, not including empty alias vectors. Only
+    recorded if features::kSendCnameAliasesToSubresourceFilterFromBrowser is
+    enabled and the request has a nonempty vector of CNAME aliases. Recorded in
+    a method called in the SubframeNavigationFilteringThrottle's destructor.
+  </summary>
+</histogram>
+
+<histogram name="SubresourceFilter.CnameAlias.Browser.RedundantCount"
+    units="count" expires_after="2021-06-18">
+  <owner>cammie@chromium.org</owner>
+  <owner>chrome-ads-histograms@google.com</owner>
+  <summary>
+    Counts the number of CNAME aliases seen by the
+    SubframeNavigationFilteringThrottle that match the host of the requested
+    URL. Only recorded if
+    features::kSendCnameAliasesToSubresourceFilterFromBrowser is enabled and the
+    request has a nonempty vector of CNAME aliases. Recorded in a method called
+    in the SubframeNavigationFilteringThrottle's destructor.
+  </summary>
+</histogram>
+
+<histogram
+    name="SubresourceFilter.CnameAlias.Browser.WasAdTaggedBasedOnAliasCount"
+    units="count" expires_after="2021-06-18">
+  <owner>cammie@chromium.org</owner>
+  <owner>chrome-ads-histograms@google.com</owner>
+  <summary>
+    Counts the number of CNAME aliases seen for a given NavigationRequest for
+    which LoadPolicy::kWouldDisallow was returned due to an alias match. Only
+    recorded if features::kSendCnameAliasesToSubresourceFilterFromBrowser is
+    enabled and the request has a nonempty vector of CNAME aliases. Recorded in
+    a method called in the SubframeNavigationFilteringThrottle's destructor.
+  </summary>
+</histogram>
+
+<histogram
+    name="SubresourceFilter.CnameAlias.Browser.WasBlockedBasedOnAliasCount"
+    units="count" expires_after="2021-06-18">
+  <owner>cammie@chromium.org</owner>
+  <owner>chrome-ads-histograms@google.com</owner>
+  <summary>
+    Counts the number of CNAME aliases seen for a given NavigationRequest for
+    which LoadPolicy::kDisallow was returned due to an alias match. Only
+    recorded if features::kSendCnameAliasesToSubresourceFilterFromBrowser is
+    enabled and the request has a nonempty vector of CNAME aliases. Recorded in
+    a method called in the SubframeNavigationFilteringThrottle's destructor.
+  </summary>
+</histogram>
+
 <histogram name="SubresourceFilter.CnameAlias.Renderer.HadAliases"
     units="BooleanHadAliases" expires_after="2021-06-18">
   <owner>cammie@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/translate/histograms.xml b/tools/metrics/histograms/histograms_xml/translate/histograms.xml
index 32d8fb9..07ae711 100644
--- a/tools/metrics/histograms/histograms_xml/translate/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/translate/histograms.xml
@@ -48,7 +48,7 @@
   <summary>Tracks UI events related to the translate bubble.</summary>
 </histogram>
 
-<histogram name="Translate.CaptureText" units="ms" expires_after="M88">
+<histogram name="Translate.CaptureText" units="ms" expires_after="M99">
   <owner>sclittle@google.com</owner>
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py
index 5e16cd0..fbab7b5 100644
--- a/tools/perf/core/bot_platforms.py
+++ b/tools/perf/core/bot_platforms.py
@@ -444,6 +444,8 @@
 ])
 _CHROMEOS_KEVIN_FYI_BENCHMARK_CONFIGS = PerfSuite([
     _GetBenchmarkConfig('rendering.desktop')])
+_LACROS_EVE_FYI_BENCHMARK_CONFIGS = PerfSuite(['loading.desktop'
+                                               ]).Abridge(['loading.desktop'])
 _LINUX_PERF_FYI_BENCHMARK_CONFIGS = PerfSuite([
     _GetBenchmarkConfig('power.desktop'),
     _GetBenchmarkConfig('rendering.desktop'),
@@ -564,6 +566,12 @@
                                        4,
                                        'chromeos',
                                        is_fyi=True)
+LACROS_EVE_PERF_FYI = PerfPlatform('lacros-eve-perf-fyi',
+                                   '',
+                                   _LACROS_EVE_FYI_BENCHMARK_CONFIGS,
+                                   1,
+                                   'chromeos',
+                                   is_fyi=True)
 LINUX_PERF_FYI = PerfPlatform('linux-perf-fyi',
                               '',
                               _LINUX_PERF_FYI_BENCHMARK_CONFIGS,
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 44c516f..00e4763e 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -264,6 +264,30 @@
             'device_type': 'kevin',
         },
     },
+    'lacros-eve-perf-fyi': {
+        'tests': [
+            {
+                'isolate':
+                'performance_test_suite',
+                'extra_args': [
+                    # The magic hostname that resolves to a CrOS device in the test lab
+                    '--remote=variable_chromeos_device_hostname',
+                ],
+            },
+        ],
+        'platform':
+        'lacros',
+        'target_bits':
+        64,
+        'dimension': {
+            'pool': 'chrome.tests',
+            # TODO(crbug.com/971204): Explicitly set the gpu to None to make
+            # chromium_swarming recipe_module ignore this dimension.
+            'gpu': None,
+            'os': 'ChromeOS',
+            'device_type': 'eve',
+        },
+    },
 }
 
 # These configurations are taken from chromium_perf.py in
@@ -1157,6 +1181,8 @@
     browser_name = tester_config['platform']
   elif tester_config['platform'] == 'chromeos':
     browser_name = 'cros-chrome'
+  elif tester_config['platform'] == 'lacros':
+    browser_name = 'lacros-chrome'
   elif (tester_config['platform'] == 'win'
     and tester_config['target_bits'] == 64):
     browser_name = 'release_x64'
diff --git a/tools/perf/core/perf_json_config_validator.py b/tools/perf/core/perf_json_config_validator.py
index 3c1496c..6b2d08b 100644
--- a/tools/perf/core/perf_json_config_validator.py
+++ b/tools/perf/core/perf_json_config_validator.py
@@ -28,6 +28,7 @@
     'chromeos-kevin-perf-fyi': {'chrome.tests'},
     'chromeos-amd64-generic-lacros-builder-perf': {'chrome.tests'},
     'fuchsia-perf-fyi': {'chrome.tests'},
+    'lacros-eve-perf-fyi': {'chrome.tests'},
 }
 
 
@@ -111,6 +112,10 @@
     if browser_options.browser != 'cros-chrome':
       raise ValueError("%s must use 'cros-chrome' browser type" %
                        builder_name)
+  elif 'lacros' in builder_name:
+    if browser_options.browser != 'lacros-chrome':
+      raise ValueError("%s must use 'lacros-chrome' browser type" %
+                       builder_name)
   elif builder_name in ('win-10-perf', 'Win 7 Nvidia GPU Perf',
                         'win-10_laptop_low_end-perf_HP-Candidate',
                         'win-10_laptop_low_end-perf'):
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 5587a52b..79b6faa 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -1,16 +1,16 @@
 {
     "trace_processor_shell": {
         "win": {
-            "hash": "6ca5855918a82bfcb01db2253203a17b87808902",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/3bf3659d351a4ca6c07900f6de1a4c8dbbc4c9d0/trace_processor_shell.exe"
+            "hash": "26aa7f0361bf6a3fbf5627c8c8699301ab51c6e6",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/026943fd29be9ac85975ddb8afdd2853bb03b3da/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "9dd9c289783e601e61c7d680c3e7aec33a24f650",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/eddc7d0df5d1b1fc6a35162da39375cbaa809fe7/trace_processor_shell"
+            "hash": "f0efd147e6c4c6f1b295bb5755a0e927fd94da5b",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/026943fd29be9ac85975ddb8afdd2853bb03b3da/trace_processor_shell"
         },
         "linux": {
-            "hash": "5fc878a0256dfd62f3cac5b7384536e0902e23bd",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/3bf3659d351a4ca6c07900f6de1a4c8dbbc4c9d0/trace_processor_shell"
+            "hash": "9de25afa2969d17640815fe3b83e4427b0526564",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/53a231c0ae868366179bd560b68a2f5f4b166541/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/core/shard_maps/lacros-eve-perf-fyi_map.json b/tools/perf/core/shard_maps/lacros-eve-perf-fyi_map.json
new file mode 100644
index 0000000..f23488cf
--- /dev/null
+++ b/tools/perf/core/shard_maps/lacros-eve-perf-fyi_map.json
@@ -0,0 +1,17 @@
+{
+    "0": {
+        "benchmarks": {
+            "loading.desktop": {
+                "abridged": true
+            }
+        }
+    },
+    "extra_infos": {
+        "num_stories": 10,
+        "predicted_min_shard_time": 100,
+        "predicted_min_shard_index": 0,
+        "predicted_max_shard_time": 100,
+        "predicted_max_shard_index": 0,
+        "shard #0": 100
+    }
+}
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/timing_data/lacros-eve-perf-fyi_timing.json b/tools/perf/core/shard_maps/timing_data/lacros-eve-perf-fyi_timing.json
new file mode 100644
index 0000000..0637a08
--- /dev/null
+++ b/tools/perf/core/shard_maps/timing_data/lacros-eve-perf-fyi_timing.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/ui/accessibility/accessibility_features.cc b/ui/accessibility/accessibility_features.cc
index 396aca9..7ff8967 100644
--- a/ui/accessibility/accessibility_features.cc
+++ b/ui/accessibility/accessibility_features.cc
@@ -117,7 +117,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 const base::Feature kSelectToSpeakNavigationControl{
-    "SelectToSpeakNavigationControl", base::FEATURE_DISABLED_BY_DEFAULT};
+    "SelectToSpeakNavigationControl", base::FEATURE_ENABLED_BY_DEFAULT};
 
 bool IsSelectToSpeakNavigationControlEnabled() {
   return base::FeatureList::IsEnabled(
diff --git a/ui/android/java/res/values-v17/styles.xml b/ui/android/java/res/values-v17/styles.xml
index f3829c7..7b31946 100644
--- a/ui/android/java/res/values-v17/styles.xml
+++ b/ui/android/java/res/values-v17/styles.xml
@@ -235,7 +235,7 @@
         <item name="android:textColor">@color/default_text_color_light_list</item>
     </style>
     <style name="TextAppearance.TextMediumThick.Secondary.Light" tools:ignore="UnusedResources">
-        <item name="android:textColor">@color/default_text_color_light_list</item>
+        <item name="android:textColor">@color/default_text_color_secondary_light_list</item>
     </style>
 
     <style name="TextAppearance.TextLarge.Secondary.Light" tools:ignore="UnusedResources">
diff --git a/ui/android/java/src/org/chromium/ui/widget/ChipView.java b/ui/android/java/src/org/chromium/ui/widget/ChipView.java
index 2a518dc..49f2119d 100644
--- a/ui/android/java/src/org/chromium/ui/widget/ChipView.java
+++ b/ui/android/java/src/org/chromium/ui/widget/ChipView.java
@@ -275,6 +275,14 @@
     }
 
     /**
+     * Returns the {@link RectProvider} that contains the start icon for the chip view.
+     * @return A {@link RectProvider}
+     */
+    public RectProvider getStartIconViewRect() {
+        return new ViewRectProvider(mStartIcon);
+    }
+
+    /**
      * Sets the correct tinting on the Chip's image view.
      * @param tintWithTextColor If true then the image view will be tinted with the primary text
      *      color. If not, the tint will be cleared.
diff --git a/ui/android/window_android.cc b/ui/android/window_android.cc
index e37dbeb..ae0f86f 100644
--- a/ui/android/window_android.cc
+++ b/ui/android/window_android.cc
@@ -147,9 +147,6 @@
 }
 
 void WindowAndroid::SetPreferredRefreshRate(float refresh_rate) {
-  if (force_60hz_refresh_rate_)
-    return;
-
   if (test_hooks_) {
     test_hooks_->SetPreferredRate(refresh_rate);
     return;
@@ -204,7 +201,6 @@
     float refresh_rate) {
   if (compositor_)
     compositor_->OnUpdateRefreshRate(refresh_rate);
-  Force60HzRefreshRateIfNeeded();
 }
 
 void WindowAndroid::OnSupportedRefreshRatesUpdated(
@@ -218,8 +214,6 @@
   }
   if (compositor_)
     compositor_->OnUpdateSupportedRefreshRates(supported_refresh_rates);
-
-  Force60HzRefreshRateIfNeeded();
 }
 
 void WindowAndroid::SetWideColorEnabled(bool enabled) {
@@ -227,22 +221,6 @@
   Java_WindowAndroid_setWideColorEnabled(env, GetJavaObject(), enabled);
 }
 
-void WindowAndroid::SetForce60HzRefreshRate() {
-  if (force_60hz_refresh_rate_)
-    return;
-
-  force_60hz_refresh_rate_ = true;
-  Force60HzRefreshRateIfNeeded();
-}
-
-void WindowAndroid::Force60HzRefreshRateIfNeeded() {
-  if (!force_60hz_refresh_rate_)
-    return;
-
-  JNIEnv* env = AttachCurrentThread();
-  Java_WindowAndroid_setPreferredRefreshRate(env, GetJavaObject(), 60.f);
-}
-
 bool WindowAndroid::HasPermission(const std::string& permission) {
   JNIEnv* env = AttachCurrentThread();
   return Java_WindowAndroid_hasPermission(
diff --git a/ui/android/window_android.h b/ui/android/window_android.h
index dd82231..658e85f 100644
--- a/ui/android/window_android.h
+++ b/ui/android/window_android.h
@@ -104,8 +104,6 @@
 
   void SetWideColorEnabled(bool enabled);
 
-  void SetForce60HzRefreshRate();
-
   class TestHooks {
    public:
     virtual ~TestHooks() = default;
@@ -148,7 +146,6 @@
   bool vsync_paused_ = false;
 
   TestHooks* test_hooks_ = nullptr;
-  bool force_60hz_refresh_rate_ = false;
 
   int selection_handles_active_count_ = 0;
 
diff --git a/ui/aura/native_window_occlusion_tracker_win.cc b/ui/aura/native_window_occlusion_tracker_win.cc
index 746cb68..0f177987 100644
--- a/ui/aura/native_window_occlusion_tracker_win.cc
+++ b/ui/aura/native_window_occlusion_tracker_win.cc
@@ -22,8 +22,59 @@
 #include "base/win/scoped_gdi_object.h"
 #include "base/win/windows_version.h"
 #include "ui/aura/window_tree_host.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/win/hwnd_util.h"
 
+const CLSID CLSID_ImmersiveShell = {
+    0xC2F03A33,
+    0x21F5,
+    0x47FA,
+    {0xB4, 0xBB, 0x15, 0x63, 0x62, 0xA2, 0xF2, 0x39}};
+
+const CLSID CLSID_VirtualDesktopAPI_Unknown = {
+    0xC5E0CDCA,
+    0x7B6E,
+    0x41B2,
+    {0x9F, 0xC4, 0xD9, 0x39, 0x75, 0xCC, 0x46, 0x7B}};
+
+struct IApplicationView : public IUnknown {
+ public:
+};
+
+MIDL_INTERFACE("FF72FFDD-BE7E-43FC-9C03-AD81681E88E4")
+IVirtualDesktop : public IUnknown {
+ public:
+  virtual HRESULT STDMETHODCALLTYPE IsViewVisible(IApplicationView * pView,
+                                                  int* pfVisible) = 0;
+  virtual HRESULT STDMETHODCALLTYPE GetID(GUID * pGuid) = 0;
+};
+
+enum AdjacentDesktop { LeftDirection = 3, RightDirection = 4 };
+
+MIDL_INTERFACE("F31574D6-B682-4cdc-BD56-1827860ABEC6")
+IVirtualDesktopManagerInternal : public IUnknown {
+ public:
+  virtual HRESULT STDMETHODCALLTYPE GetCount(UINT * pCount) = 0;
+  virtual HRESULT STDMETHODCALLTYPE MoveViewToDesktop(
+      IApplicationView * pView, IVirtualDesktop * pDesktop) = 0;
+  virtual HRESULT STDMETHODCALLTYPE CanViewMoveDesktops(
+      IApplicationView * pView, int* pfCanViewMoveDesktops) = 0;
+  virtual HRESULT STDMETHODCALLTYPE GetCurrentDesktop(IVirtualDesktop *
+                                                      *desktop) = 0;
+  virtual HRESULT STDMETHODCALLTYPE GetDesktops(IObjectArray * *ppDesktops) = 0;
+  virtual HRESULT STDMETHODCALLTYPE GetAdjacentDesktop(
+      IVirtualDesktop * pDesktopReference, AdjacentDesktop uDirection,
+      IVirtualDesktop * *ppAdjacentDesktop) = 0;
+  virtual HRESULT STDMETHODCALLTYPE SwitchDesktop(IVirtualDesktop *
+                                                  pDesktop) = 0;
+  virtual HRESULT STDMETHODCALLTYPE CreateDesktopW(IVirtualDesktop *
+                                                   *ppNewDesktop) = 0;
+  virtual HRESULT STDMETHODCALLTYPE RemoveDesktop(
+      IVirtualDesktop * pRemove, IVirtualDesktop * pFallbackDesktop) = 0;
+  virtual HRESULT STDMETHODCALLTYPE FindDesktop(
+      GUID * desktopId, IVirtualDesktop * *ppDesktop) = 0;
+};
+
 namespace aura {
 
 namespace {
@@ -32,6 +83,9 @@
 const base::TimeDelta kUpdateOcclusionDelay =
     base::TimeDelta::FromMilliseconds(16);
 
+const base::TimeDelta kUpdateVirtualDesktopDelay =
+    base::TimeDelta::FromMilliseconds(1000);
+
 // This global variable can be accessed only on main thread.
 NativeWindowOcclusionTrackerWin* g_tracker = nullptr;
 
@@ -313,6 +367,19 @@
   if (base::win::GetVersion() >= base::win::Version::WIN10) {
     ::CoCreateInstance(__uuidof(VirtualDesktopManager), nullptr, CLSCTX_ALL,
                        IID_PPV_ARGS(&virtual_desktop_manager_));
+
+    Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
+    if (base::FeatureList::IsEnabled(
+            features::kCalculateNativeWinOcclusionCheckVirtualDesktopUsed) &&
+        SUCCEEDED(::CoCreateInstance(CLSID_ImmersiveShell, NULL,
+                                     CLSCTX_LOCAL_SERVER,
+                                     IID_PPV_ARGS(&service_provider)))) {
+      service_provider->QueryService(
+          CLSID_VirtualDesktopAPI_Unknown,
+          IID_PPV_ARGS(&virtual_desktop_manager_internal_));
+      if (virtual_desktop_manager_internal_)
+        ComputeVirtualDesktopUsed();
+    }
   }
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
@@ -362,6 +429,8 @@
     UnregisterEventHooks();
     if (occlusion_update_timer_.IsRunning())
       occlusion_update_timer_.Stop();
+    if (virtual_desktop_update_timer_.IsRunning())
+      virtual_desktop_update_timer_.Stop();
   }
 }
 
@@ -508,12 +577,25 @@
 }
 
 void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
+    ComputeVirtualDesktopUsed() {
+  UINT count = 0;
+  if (SUCCEEDED(virtual_desktop_manager_internal_->GetCount(&count)))
+    virtual_desktops_used_ = count > 1;
+}
+
+void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
     ScheduleOcclusionCalculationIfNeeded() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!occlusion_update_timer_.IsRunning())
+  if (!occlusion_update_timer_.IsRunning()) {
     occlusion_update_timer_.Start(
         FROM_HERE, kUpdateOcclusionDelay, this,
         &WindowOcclusionCalculator::ComputeNativeWindowOcclusionStatus);
+    if (virtual_desktop_manager_internal_) {
+      virtual_desktop_update_timer_.Start(
+          FROM_HERE, kUpdateVirtualDesktopDelay, this,
+          &WindowOcclusionCalculator::ComputeVirtualDesktopUsed);
+    }
+  }
 }
 
 void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
@@ -769,7 +851,7 @@
 
 base::Optional<bool> NativeWindowOcclusionTrackerWin::
     WindowOcclusionCalculator::IsWindowOnCurrentVirtualDesktop(HWND hwnd) {
-  if (!virtual_desktop_manager_)
+  if (!virtual_desktop_manager_ || !virtual_desktops_used_)
     return true;
 
   BOOL on_current_desktop;
diff --git a/ui/aura/native_window_occlusion_tracker_win.h b/ui/aura/native_window_occlusion_tracker_win.h
index 4934e8b..7c64593 100644
--- a/ui/aura/native_window_occlusion_tracker_win.h
+++ b/ui/aura/native_window_occlusion_tracker_win.h
@@ -34,6 +34,8 @@
 class Rect;
 }
 
+struct IVirtualDesktopManagerInternal;
+
 namespace aura {
 
 // This class keeps track of whether any HWNDs are occluding any app windows.
@@ -134,6 +136,10 @@
     // their occlusion status has changed.
     void ComputeNativeWindowOcclusionStatus();
 
+    // Computes if virtual desktops are used. This is used as an optimization
+    // since IsWindowOnCurrentVirtualDesktop is a slow call.
+    void ComputeVirtualDesktopUsed();
+
     // Schedules an occlusion calculation |update_occlusion_delay_| time in the
     // future, if one isn't already scheduled.
     void ScheduleOcclusionCalculationIfNeeded();
@@ -217,6 +223,9 @@
     // Timer to delay occlusion update.
     base::OneShotTimer occlusion_update_timer_;
 
+    // Timer to check how many virtual desktops are present.
+    base::OneShotTimer virtual_desktop_update_timer_;
+
     // Used to keep track of whether we're in the middle of getting window move
     // events, in order to wait until the window move is complete before
     // calculating window occlusion.
@@ -244,9 +253,18 @@
     // ignore windows occluded by the dragged window.
     HWND moving_window_ = 0;
 
+    // By caching if virtual desktops are in use or not we can avoid calling
+    // IsWindowOnCurrentVirtualDesktop which is slow. Start with an initial
+    // value of true so that we only optimize after we get confirmation that
+    // there are no virtual desktops.
+    bool virtual_desktops_used_ = true;
+
     // Only used on Win10+.
     Microsoft::WRL::ComPtr<IVirtualDesktopManager> virtual_desktop_manager_;
 
+    Microsoft::WRL::ComPtr<IVirtualDesktopManagerInternal>
+        virtual_desktop_manager_internal_;
+
     SEQUENCE_CHECKER(sequence_checker_);
 
     base::WeakPtrFactory<WindowOcclusionCalculator> weak_factory_{this};
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index 2d5847dd7..0193353 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -24,6 +24,10 @@
 // If enabled, calculate native window occlusion - Windows-only.
 const base::Feature kCalculateNativeWinOcclusion{
     "CalculateNativeWinOcclusion", base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kCalculateNativeWinOcclusionCheckVirtualDesktopUsed{
+    "CalculateNativeWinOcclusionCheckVirtualDesktopUsed",
+    base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // OW_WIN
 
 // Whether or not to delegate color queries to the color provider.
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index 4eeda91..e12abf7 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -48,6 +48,8 @@
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 extern const base::Feature kCalculateNativeWinOcclusion;
 COMPONENT_EXPORT(UI_BASE_FEATURES)
+extern const base::Feature kCalculateNativeWinOcclusionCheckVirtualDesktopUsed;
+COMPONENT_EXPORT(UI_BASE_FEATURES)
 extern const base::Feature kElasticOverscrollWin;
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 extern const base::Feature kInputPaneOnScreenKeyboard;
diff --git a/ui/compositor/animation_throughput_reporter.cc b/ui/compositor/animation_throughput_reporter.cc
index 893a539..043a349f 100644
--- a/ui/compositor/animation_throughput_reporter.cc
+++ b/ui/compositor/animation_throughput_reporter.cc
@@ -9,10 +9,8 @@
 
 #include "base/bind.h"
 #include "base/check.h"
-#include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
-#include "base/scoped_observation.h"
 #include "cc/animation/animation.h"
 #include "ui/compositor/callback_layer_animation_observer.h"
 #include "ui/compositor/compositor.h"
@@ -20,7 +18,6 @@
 #include "ui/compositor/layer_animation_delegate.h"
 #include "ui/compositor/layer_animation_sequence.h"
 #include "ui/compositor/layer_animator.h"
-#include "ui/compositor/layer_observer.h"
 #include "ui/compositor/throughput_tracker.h"
 
 namespace ui {
@@ -33,45 +30,27 @@
 // is going away, it needs to have the same lifetime of the animations to track
 // the performance. In such case, the owner reporter would drop the ownership
 // and set set_should_delete() to let the tracker manages its own lifetime
-// based on LayerDetroyed and LayerAnimationObserver signals. On the other hand,
-// if there are no animations to track, the tracker is released with its owner
-// reporter.
+// based on LayerAnimationObserver signals. On the other hand, if there are no
+// animations to track, the tracker is released with its owner reporter.
 class AnimationThroughputReporter::AnimationTracker
-    : public CallbackLayerAnimationObserver,
-      public LayerObserver {
+    : public CallbackLayerAnimationObserver {
  public:
-  AnimationTracker(Layer* layer, ReportCallback report_callback)
+  AnimationTracker(LayerAnimator* animator, ReportCallback report_callback)
       : CallbackLayerAnimationObserver(
             base::BindRepeating(&AnimationTracker::OnAnimationEnded,
                                 base::Unretained(this))),
-        animator_(layer->GetAnimator()),
+        animator_(animator),
         report_callback_(std::move(report_callback)) {
     DCHECK(report_callback_);
-    layer_observation_.Observe(layer);
   }
 
   AnimationTracker(const AnimationTracker& other) = delete;
   AnimationTracker& operator=(const AnimationTracker& other) = delete;
 
-  ~AnimationTracker() override {
-    // No auto delete in the observer callbacks since `this` is being
-    // destructed.
-    should_delete_ = false;
+  ~AnimationTracker() override = default;
 
-    // Cancels existing tracking if any.
-    throughput_tracker_.reset();
-
-    // Stops observing animations so that `animator` destruction does not call
-    // back into half destructed `this` if `this` holds the last reference of
-    // `animator_`.
-    StopObserving();
-  }
-
-  // Whether there are/will be animations to track. That is, there is an
-  // underlying layer and there are attached animation sequences.
-  bool HasAnimationsToTrack() const {
-    return layer_observation_.IsObserving() && !attached_sequences().empty();
-  }
+  // Whether there are/will be animations to track.
+  bool HasAnimationsToTrack() const { return !attached_sequences().empty(); }
 
   void set_should_delete(bool should_delete) { should_delete_ = should_delete; }
 
@@ -115,17 +94,6 @@
     CallbackLayerAnimationObserver::OnLayerAnimationAborted(sequence);
   }
 
-  // LayerObserver:
-  void LayerDestroyed(Layer* layer) override {
-    DCHECK(layer_observation_.IsObservingSource(layer));
-
-    layer_observation_.Reset();
-
-    // No more tracking needed when underlying layer is gone.
-    if (should_delete_)
-      delete this;
-  }
-
   void MaybeStartTracking() {
     // No tracking if no layer animation sequence is started.
     if (!first_animation_group_id_.has_value())
@@ -133,13 +101,12 @@
 
     // No tracking if |animator_| is not attached to a timeline. Layer animation
     // sequence would not tick without a timeline.
-    if (!AnimationThroughputReporter::IsAnimatorAttachedToTimeline(
-            animator_.get())) {
+    if (!AnimationThroughputReporter::IsAnimatorAttachedToTimeline(animator_)) {
       return;
     }
 
     ui::Compositor* compositor =
-        AnimationThroughputReporter::GetCompositor(animator_.get());
+        AnimationThroughputReporter::GetCompositor(animator_);
     throughput_tracker_ = compositor->RequestNewThroughputTracker();
     throughput_tracker_->Start(report_callback_);
   }
@@ -162,8 +129,7 @@
   // Whether this class should delete itself on animation ended.
   bool should_delete_ = false;
 
-  base::ScopedObservation<Layer, LayerObserver> layer_observation_{this};
-  scoped_refptr<LayerAnimator> animator_;
+  LayerAnimator* const animator_;
 
   base::Optional<ThroughputTracker> throughput_tracker_;
 
@@ -174,11 +140,11 @@
 };
 
 AnimationThroughputReporter::AnimationThroughputReporter(
-    LayerAnimator* animator,
+    scoped_refptr<LayerAnimator> animator,
     ReportCallback report_callback)
-    : animator_(animator),
+    : animator_(std::move(animator)),
       animation_tracker_(
-          std::make_unique<AnimationTracker>(animator_->delegate()->GetLayer(),
+          std::make_unique<AnimationTracker>(animator_.get(),
                                              std::move(report_callback))) {
   animator_->AddObserver(animation_tracker_.get());
 }
@@ -189,6 +155,15 @@
   // from the scheduled animation sequences.
   animator_->observers_.RemoveObserver(animation_tracker_.get());
 
+  // Drop the animator reference. If this is the last reference, the animator
+  // will be destroyed. When the animator destruction happens, it destroys its
+  // LayerAnimationSequences and detach observers from them. As a result,
+  // AnimationTracker::OnAnimationEnded would be called after all animation
+  // sequences are detached. After this, animator will no longer be accessed
+  // by AnimationTracker and HasAnimationsToTrack() would correctly report
+  // that there are no animations to track.
+  animator_.reset();
+
   // |animation_tracker_| deletes itself when its tracked animations finish.
   if (animation_tracker_->HasAnimationsToTrack())
     animation_tracker_.release()->set_should_delete(true);
diff --git a/ui/compositor/animation_throughput_reporter.h b/ui/compositor/animation_throughput_reporter.h
index 334a9ed..59b728d 100644
--- a/ui/compositor/animation_throughput_reporter.h
+++ b/ui/compositor/animation_throughput_reporter.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/callback_forward.h"
+#include "base/memory/scoped_refptr.h"
 #include "cc/metrics/frame_sequence_metrics.h"
 #include "ui/compositor/compositor_export.h"
 
@@ -37,7 +38,7 @@
  public:
   using ReportCallback = base::RepeatingCallback<void(
       const cc::FrameSequenceMetrics::CustomReportData&)>;
-  AnimationThroughputReporter(LayerAnimator* animator,
+  AnimationThroughputReporter(scoped_refptr<LayerAnimator> animator,
                               ReportCallback report_callback);
   AnimationThroughputReporter(const AnimationThroughputReporter&) = delete;
   AnimationThroughputReporter& operator=(const AnimationThroughputReporter&) =
@@ -57,7 +58,7 @@
   // List here to access LayerAnimation's private |anmation_| member.
   static bool IsAnimatorAttachedToTimeline(LayerAnimator* animator);
 
-  LayerAnimator* const animator_;
+  scoped_refptr<LayerAnimator> animator_;
   std::unique_ptr<AnimationTracker> animation_tracker_;
 };
 
diff --git a/ui/file_manager/audio_player/manifest.json b/ui/file_manager/audio_player/manifest.json
index 113b4da..f92d71c 100644
--- a/ui/file_manager/audio_player/manifest.json
+++ b/ui/file_manager/audio_player/manifest.json
@@ -25,7 +25,6 @@
       "fileSystem": ["requestFileSystem", "write"]
     },
     "fullscreen",
-    "mediaPlayerPrivate",
     "power",
     "storage",
     "chrome://resources/",
diff --git a/ui/file_manager/file_manager/manifest.json b/ui/file_manager/file_manager/manifest.json
index 5d06b97..b724030 100644
--- a/ui/file_manager/file_manager/manifest.json
+++ b/ui/file_manager/file_manager/manifest.json
@@ -36,7 +36,6 @@
     "https://drive.google.com/",
     "https://www.google-analytics.com/",
     "launcherSearchProvider",
-    "mediaPlayerPrivate",
     "metricsPrivate",
     "notifications",
     "power",
diff --git a/ui/file_manager/video_player/manifest.json b/ui/file_manager/video_player/manifest.json
index c5c1781..8aa42d81 100644
--- a/ui/file_manager/video_player/manifest.json
+++ b/ui/file_manager/video_player/manifest.json
@@ -25,7 +25,6 @@
       "fileSystem": ["requestFileSystem", "write"]
     },
     "fullscreen",
-    "mediaPlayerPrivate",
     "metricsPrivate",
     "power",
     "storage",
diff --git a/ui/gtk/gtk_ui.cc b/ui/gtk/gtk_ui.cc
index 63f4dbc..9bd557d3 100644
--- a/ui/gtk/gtk_ui.cc
+++ b/ui/gtk/gtk_ui.cc
@@ -932,19 +932,14 @@
     const std::string header_selector_inactive = header_selector + ":backdrop";
     const SkColor frame_color =
         SkColorSetA(GetBgColor(header_selector), SK_AlphaOPAQUE);
-    const SkColor frame_color_incognito =
-        color_utils::HSLShift(frame_color, kDefaultTintFrameIncognito);
     const SkColor frame_color_inactive =
         SkColorSetA(GetBgColor(header_selector_inactive), SK_AlphaOPAQUE);
-    const SkColor frame_color_incognito_inactive =
-        color_utils::HSLShift(frame_color_inactive, kDefaultTintFrameIncognito);
 
     color_map[ThemeProperties::COLOR_FRAME_ACTIVE] = frame_color;
     color_map[ThemeProperties::COLOR_FRAME_INACTIVE] = frame_color_inactive;
-    color_map[ThemeProperties::COLOR_FRAME_ACTIVE_INCOGNITO] =
-        frame_color_incognito;
+    color_map[ThemeProperties::COLOR_FRAME_ACTIVE_INCOGNITO] = frame_color;
     color_map[ThemeProperties::COLOR_FRAME_INACTIVE_INCOGNITO] =
-        frame_color_incognito_inactive;
+        frame_color_inactive;
 
     // Compose the window color on the frame color to ensure the resulting tab
     // color is opaque.
@@ -969,20 +964,12 @@
         background_tab_text_color;
     color_map[ThemeProperties::
                   COLOR_TAB_FOREGROUND_INACTIVE_FRAME_ACTIVE_INCOGNITO] =
-        color_utils::BlendForMinContrast(
-            color_utils::HSLShift(background_tab_text_color,
-                                  kDefaultTintFrameIncognito),
-            frame_color_incognito)
-            .color;
+        background_tab_text_color;
     color_map[ThemeProperties::COLOR_TAB_FOREGROUND_INACTIVE_FRAME_INACTIVE] =
         background_tab_text_color_inactive;
     color_map[ThemeProperties::
                   COLOR_TAB_FOREGROUND_INACTIVE_FRAME_INACTIVE_INCOGNITO] =
-        color_utils::BlendForMinContrast(
-            color_utils::HSLShift(background_tab_text_color_inactive,
-                                  kDefaultTintFrameIncognito),
-            frame_color_incognito_inactive)
-            .color;
+        background_tab_text_color_inactive;
 
     color_map[ThemeProperties::COLOR_OMNIBOX_TEXT] =
         native_theme_->GetSystemColor(
diff --git a/ui/gtk/gtk_util.cc b/ui/gtk/gtk_util.cc
index b5c93b6..c2ea5d2 100644
--- a/ui/gtk/gtk_util.cc
+++ b/ui/gtk/gtk_util.cc
@@ -116,11 +116,6 @@
 
 namespace gtk {
 
-// TODO(thomasanderson): ThemeService has a whole interface just for reading
-// default constants. Figure out what to do with that more long term; for now,
-// just copy the constant itself here.
-const color_utils::HSL kDefaultTintFrameIncognito = {-1, 0.2f, 0.35f};
-
 void GtkInitFromCommandLine(const base::CommandLine& command_line) {
   CommonInitFromCommandLine(command_line);
 }
diff --git a/ui/gtk/gtk_util.h b/ui/gtk/gtk_util.h
index 1512a55..2625cac 100644
--- a/ui/gtk/gtk_util.h
+++ b/ui/gtk/gtk_util.h
@@ -26,18 +26,12 @@
 class CommandLine;
 }
 
-namespace color_utils {
-struct HSL;
-}
-
 namespace ui {
 class KeyEvent;
 }
 
 namespace gtk {
 
-extern const color_utils::HSL kDefaultTintFrameIncognito;
-
 void GtkInitFromCommandLine(const base::CommandLine& command_line);
 
 // Sets |dialog| as transient for |parent|, which will keep it on top and center
diff --git a/ui/gtk/native_theme_gtk.cc b/ui/gtk/native_theme_gtk.cc
index 5964c64..6a864eb 100644
--- a/ui/gtk/native_theme_gtk.cc
+++ b/ui/gtk/native_theme_gtk.cc
@@ -9,7 +9,6 @@
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/skbitmap_operations.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/gtk/gtk_util.h"
 
@@ -711,13 +710,7 @@
 
   SkBitmap bitmap =
       GetWidgetBitmap(rect.size(), context, BG_RENDER_RECURSIVE, false);
-
-  if (frame_top_area.incognito) {
-    bitmap = SkBitmapOperations::CreateHSLShiftedBitmap(
-        bitmap, kDefaultTintFrameIncognito);
-    bitmap.setImmutable();
-  }
-
+  bitmap.setImmutable();
   canvas->drawImage(cc::PaintImage::CreateFromBitmap(std::move(bitmap)),
                     rect.x(), rect.y());
 }
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h
index 3df3fb35..d254838cfb 100644
--- a/ui/native_theme/native_theme.h
+++ b/ui/native_theme/native_theme.h
@@ -172,7 +172,6 @@
     // Distinguishes between active (foreground) and inactive
     // (background) window frame styles.
     bool is_active;
-    bool incognito;
     // True when Chromium renders the titlebar.  False when the window
     // manager renders the titlebar.
     bool use_custom_frame;
diff --git a/ui/views/controls/menu/menu_host.cc b/ui/views/controls/menu/menu_host.cc
index 19bb2bf..10612078 100644
--- a/ui/views/controls/menu/menu_host.cc
+++ b/ui/views/controls/menu/menu_host.cc
@@ -20,6 +20,7 @@
 #include "ui/views/controls/menu/menu_scroll_view_container.h"
 #include "ui/views/controls/menu/submenu_view.h"
 #include "ui/views/round_rect_painter.h"
+#include "ui/views/views_delegate.h"
 #include "ui/views/widget/native_widget_private.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
@@ -235,6 +236,10 @@
 void MenuHost::OnMouseCaptureLost() {
   if (destroying_ || ignore_capture_lost_)
     return;
+
+  if (!ViewsDelegate::GetInstance()->ShouldCloseMenuIfMouseCaptureLost())
+    return;
+
   MenuController* menu_controller =
       submenu_->GetMenuItem()->GetMenuController();
   if (menu_controller && !menu_controller->drag_in_progress())
diff --git a/ui/views/views_delegate.cc b/ui/views/views_delegate.cc
index e9e7458..6e5bd4f 100644
--- a/ui/views/views_delegate.cc
+++ b/ui/views/views_delegate.cc
@@ -73,6 +73,10 @@
   return ProcessMenuAcceleratorResult::LEAVE_MENU_OPEN;
 }
 
+bool ViewsDelegate::ShouldCloseMenuIfMouseCaptureLost() const {
+  return true;
+}
+
 #if defined(OS_WIN)
 HICON ViewsDelegate::GetDefaultWindowIcon() const {
   return nullptr;
diff --git a/ui/views/views_delegate.h b/ui/views/views_delegate.h
index a28f8caf..87328d7 100644
--- a/ui/views/views_delegate.h
+++ b/ui/views/views_delegate.h
@@ -125,6 +125,10 @@
   virtual ProcessMenuAcceleratorResult ProcessAcceleratorWhileMenuShowing(
       const ui::Accelerator& accelerator);
 
+  // If a menu is showing and its window loses mouse capture, it will close if
+  // this returns true.
+  virtual bool ShouldCloseMenuIfMouseCaptureLost() const;
+
 #if defined(OS_WIN)
   // Retrieves the default window icon to use for windows if none is specified.
   virtual HICON GetDefaultWindowIcon() const;
diff --git a/ui/views/widget/root_view.cc b/ui/views/widget/root_view.cc
index 3911450..073d0b1 100644
--- a/ui/views/widget/root_view.cc
+++ b/ui/views/widget/root_view.cc
@@ -13,6 +13,7 @@
 #include "build/build_config.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
+#include "ui/accessibility/platform/ax_platform_node.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
 #include "ui/base/ui_base_switches_util.h"
@@ -21,6 +22,7 @@
 #include "ui/events/event_utils.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/canvas.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/drag_controller.h"
 #include "ui/views/metadata/metadata_impl_macros.h"
 #include "ui/views/view_class_properties.h"
@@ -261,9 +263,10 @@
 
 void RootView::AnnounceText(const base::string16& text) {
 #if defined(OS_APPLE)
-  // MacOSX has its own API for making announcements; see AnnounceText()
-  // override in ax_platform_node_mac.[h|mm]
-  NOTREACHED();
+  gfx::NativeViewAccessible native = GetViewAccessibility().GetNativeObject();
+  auto* ax_node = ui::AXPlatformNode::FromNativeViewAccessible(native);
+  if (ax_node)
+    ax_node->AnnounceText(text);
 #else
   DCHECK(GetWidget());
   DCHECK(GetContentsView());
diff --git a/ui/views/window/custom_frame_view.cc b/ui/views/window/custom_frame_view.cc
index e046978d..2eb97a08 100644
--- a/ui/views/window/custom_frame_view.cc
+++ b/ui/views/window/custom_frame_view.cc
@@ -192,7 +192,6 @@
   frame_background_->set_frame_color(GetFrameColor());
   frame_background_->set_use_custom_frame(true);
   frame_background_->set_is_active(ShouldPaintAsActive());
-  frame_background_->set_incognito(false);
   const gfx::ImageSkia frame_image = GetFrameImage();
   frame_background_->set_theme_image(frame_image);
   frame_background_->set_top_area_height(frame_image.height());
diff --git a/ui/views/window/frame_background.cc b/ui/views/window/frame_background.cc
index 50e11d3..6a26e3f 100644
--- a/ui/views/window/frame_background.cc
+++ b/ui/views/window/frame_background.cc
@@ -105,7 +105,6 @@
   ui::NativeTheme::ExtraParams params;
   params.frame_top_area.use_custom_frame = use_custom_frame_;
   params.frame_top_area.is_active = is_active_;
-  params.frame_top_area.incognito = incognito_;
   params.frame_top_area.default_background_color = frame_color_;
   native_theme->Paint(canvas->sk_canvas(), ui::NativeTheme::kFrameTopArea,
                       ui::NativeTheme::kNormal,
diff --git a/ui/views/window/frame_background.h b/ui/views/window/frame_background.h
index ece082a..bf5b4e0 100644
--- a/ui/views/window/frame_background.h
+++ b/ui/views/window/frame_background.h
@@ -36,9 +36,6 @@
   // Sets whether the frame to be drawn should have focus.
   void set_is_active(bool is_active) { is_active_ = is_active; }
 
-  // Sets whether the frame to be drawn is in incognito mode.
-  void set_incognito(bool incognito) { incognito_ = incognito; }
-
   // Sets the theme image for the top of the window.  May be null (empty).
   // Memory is owned by the caller.
   void set_theme_image(const gfx::ImageSkia& image) { theme_image_ = image; }
@@ -90,7 +87,6 @@
   SkColor frame_color_ = 0;
   bool use_custom_frame_ = true;
   bool is_active_ = true;
-  bool incognito_ = false;
   gfx::ImageSkia theme_image_;
   int theme_image_y_inset_ = 0;
   gfx::ImageSkia theme_overlay_image_;
diff --git a/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.html b/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.html
index 34cad29..9acf444 100644
--- a/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.html
+++ b/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.html
@@ -26,6 +26,10 @@
     <routine-group name="[[i18n('NetworkDiagnosticsDnsGroup')]]"
         routines="[[getRoutineGroup_(routines_.*, RoutineGroup_.DNS)]]">
     </routine-group>
+    <routine-group name="[[i18n('NetworkDiagnosticsGoogleServicesGroup')]]"
+        routines=
+            "[[getRoutineGroup_(routines_.*, RoutineGroup_.GOOGLE_SERVICES)]]">
+    </routine-group>
   </template>
   <script src="network_diagnostics.js"></script>
 </dom-module>
diff --git a/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.js b/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.js
index f2b97dc..a721ccb 100644
--- a/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.js
+++ b/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.js
@@ -131,6 +131,18 @@
               },
             ]
           },
+          {
+            group: RoutineGroup.GOOGLE_SERVICES,
+            routines: [
+              {
+                name: 'NetworkDiagnosticsVideoConferencing',
+                type: RoutineType.VIDEO_CONFERENCING,
+                // A null stun_server_hostname will use the routine default.
+                func: () => this.networkDiagnostics_.videoConferencing(
+                    /*stun_server_hostname=*/ null),
+              },
+            ]
+          },
         ];
         const routines = [];
 
@@ -467,6 +479,22 @@
               problemStrings.push(getString('CaptivePortalProblem_NoInternet'));
               break;
           }
+
+        case RoutineType.VIDEO_CONFERENCING:
+          switch (problem) {
+            case diagnosticsMojom.VideoConferencingProblem.kUdpFailure:
+              problemStrings.push(
+                  getString('VideoConferencingProblem_UdpFailure'));
+              break;
+            case diagnosticsMojom.VideoConferencingProblem.kTcpFailure:
+              problemStrings.push(
+                  getString('VideoConferencingProblem_TcpFailure'));
+              break;
+            case diagnosticsMojom.VideoConferencingProblem.kMediaFailure:
+              problemStrings.push(
+                  getString('VideoConferencingProblem_MediaFailure'));
+              break;
+          }
       }
     }
 
diff --git a/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics_types.js b/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics_types.js
index 3696fff..e67a463 100644
--- a/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics_types.js
+++ b/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics_types.js
@@ -55,6 +55,7 @@
   HTTPS_FIREWALL: 8,
   HTTPS_LATENCY: 9,
   CAPTIVE_PORTAL: 10,
+  VIDEO_CONFERENCING: 11,
 };
 
 /**
@@ -68,6 +69,7 @@
   GATEWAY: 3,
   FIREWALL: 4,
   DNS: 5,
+  GOOGLE_SERVICES: 6,
 };
 
 /* #export */ const Icons = {
diff --git a/ui/webui/resources/js/list_property_update_behavior.js b/ui/webui/resources/js/list_property_update_behavior.js
index 6812f541..6ac160b 100644
--- a/ui/webui/resources/js/list_property_update_behavior.js
+++ b/ui/webui/resources/js/list_property_update_behavior.js
@@ -21,44 +21,60 @@
 /* #export */ const ListPropertyUpdateBehavior = {
   /**
    * @param {string} propertyPath
-   * @param {function(!Object): string} itemUidGetter
+   * @param {function(!Object): (!Object|string)} identityGetter
    * @param {!Array<!Object>} updatedList
-   * @param {boolean} uidBasedUpdate
+   * @param {boolean=} identityBasedUpdate
    * @returns {boolean} True if notifySplices was called.
    */
-  updateList(propertyPath, itemUidGetter, updatedList, uidBasedUpdate = false) {
-    const list = this.get(propertyPath);
-    const splices = Polymer.ArraySplice.calculateSplices(
-        updatedList.map(itemUidGetter), list.map(itemUidGetter));
-
-    splices.forEach(splice => {
-      const index = splice.index;
-      const deleteCount = splice.removed.length;
-      // Transform splices to the expected format of notifySplices().
-      // Convert !Array<string> to !Array<!Object>.
-      splice.removed = list.slice(index, index + deleteCount);
-      splice.object = list;
-      splice.type = 'splice';
-
-      const added = updatedList.slice(index, index + splice.addedCount);
-      const spliceParams = [index, deleteCount].concat(added);
-      list.splice.apply(list, spliceParams);
-    });
-
-    let updated = splices.length > 0;
-    if (!uidBasedUpdate) {
-      list.forEach((item, index) => {
-        const updatedItem = updatedList[index];
-        if (JSON.stringify(item) !== JSON.stringify(updatedItem)) {
-          this.set([propertyPath, index], updatedItem);
-          updated = true;
-        }
-      });
-    }
-
-    if (splices.length > 0) {
-      this.notifySplices(propertyPath, splices);
-    }
-    return updated;
+  updateList(
+      propertyPath, identityGetter, updatedList, identityBasedUpdate = false) {
+    return updateListProperty(
+        this, propertyPath, identityGetter, updatedList, identityBasedUpdate);
   },
 };
+
+/**
+ * @param {Object} instance
+ * @param {string} propertyPath
+ * @param {function(!Object): (!Object|string)} identityGetter
+ * @param {!Array<!Object>} updatedList
+ * @param {boolean=} identityBasedUpdate
+ * @returns {boolean} True if notifySplices was called.
+ */
+/* #export */ function updateListProperty(
+    instance, propertyPath, identityGetter, updatedList,
+    identityBasedUpdate = false) {
+  const list = instance.get(propertyPath);
+  const splices = Polymer.ArraySplice.calculateSplices(
+      updatedList.map(identityGetter), list.map(identityGetter));
+
+  splices.forEach(splice => {
+    const index = splice.index;
+    const deleteCount = splice.removed.length;
+    // Transform splices to the expected format of notifySplices().
+    // Convert !Array<string> to !Array<!Object>.
+    splice.removed = list.slice(index, index + deleteCount);
+    splice.object = list;
+    splice.type = 'splice';
+
+    const added = updatedList.slice(index, index + splice.addedCount);
+    const spliceParams = [index, deleteCount].concat(added);
+    list.splice.apply(list, spliceParams);
+  });
+
+  let updated = splices.length > 0;
+  if (!identityBasedUpdate) {
+    list.forEach((item, index) => {
+      const updatedItem = updatedList[index];
+      if (JSON.stringify(item) !== JSON.stringify(updatedItem)) {
+        instance.set([propertyPath, index], updatedItem);
+        updated = true;
+      }
+    });
+  }
+
+  if (splices.length > 0) {
+    instance.notifySplices(propertyPath, splices);
+  }
+  return updated;
+}
diff --git a/url/origin_unittest.cc b/url/origin_unittest.cc
index 26f9c37..97100ef 100644
--- a/url/origin_unittest.cc
+++ b/url/origin_unittest.cc
@@ -229,8 +229,14 @@
       "local-but-nonstandar:foo",  // Prefix of registered scheme.
       "but-nonstandard:foo",       // Suffix of registered scheme.
       "local-and-standard:",       // Standard scheme needs a hostname.
-      "standard-but-noaccess:",    // Standard scheme needs a hostname.
       "blob:blob:http://www.example.com/guid-goes-here",  // Double blob.
+
+      // Scheme (registered in SetUp()) that's standard but marked as noaccess.
+      // See also SecurityOriginTest.StandardNoAccessScheme and
+      // NavigationUrlRewriteBrowserTest.RewriteToNoAccess.
+      "standard-but-noaccess:",     // Standard scheme needs a hostname.
+      "standard-but-noaccess:foo",  // Standard scheme needs a hostname.
+      "standard-but-noaccess://bar",
   };
 
   for (auto* test_url : urls) {
@@ -349,12 +355,6 @@
       {"local-but-nonstandard://bar", "local-but-nonstandard", "", 0},
       {"also-local-but-nonstandard://bar", "also-local-but-nonstandard", "", 0},
 
-      // Scheme (registered in SetUp()) that's standard but marked as noaccess.
-      // url::Origin doesn't currently take the noaccess property into account,
-      // so these aren't expected to result in opaque origins.
-      {"standard-but-noaccess:foo", "standard-but-noaccess", "foo", 0},
-      {"standard-but-noaccess://bar", "standard-but-noaccess", "bar", 0},
-
       // file: URLs
       {"file:///etc/passwd", "file", "", 0},
       {"file://example.com/etc/passwd", "file", "example.com", 0},
@@ -814,10 +814,10 @@
       {"standard-but-noaccess://a.com/foo", &regular_origin, false},
       {"standard-but-noaccess://a.com/foo", &opaque_precursor_origin, false},
       {"standard-but-noaccess://a.com/foo", &opaque_unique_origin, true},
-      {"standard-but-noaccess://a.com/foo", &no_access_origin, false},
+      {"standard-but-noaccess://a.com/foo", &no_access_origin, true},
       {"standard-but-noaccess://a.com/foo", &no_access_opaque_precursor_origin,
-       false},
-      {"standard-but-noaccess://b.com/foo", &no_access_origin, false},
+       true},
+      {"standard-but-noaccess://b.com/foo", &no_access_origin, true},
       {"standard-but-noaccess://b.com/foo", &no_access_opaque_precursor_origin,
        true},
 
diff --git a/url/scheme_host_port.cc b/url/scheme_host_port.cc
index 233c2923..61239b1 100644
--- a/url/scheme_host_port.cc
+++ b/url/scheme_host_port.cc
@@ -49,6 +49,10 @@
   return host == canon_host;
 }
 
+// Note: When changing IsValidInput, consider also updating
+// ShouldTreatAsOpaqueOrigin in Blink (there might be existing differences in
+// behavior between these 2 layers, but we should avoid introducing new
+// differences).
 bool IsValidInput(const base::StringPiece& scheme,
                   const base::StringPiece& host,
                   uint16_t port,
@@ -57,25 +61,27 @@
   if (scheme.empty())
     return false;
 
+  // about:blank and other no-access schemes translate into an opaque origin.
+  // This helps consistency with ShouldTreatAsOpaqueOrigin in Blink.
+  if (base::Contains(GetNoAccessSchemes(), scheme))
+    return false;
+
   SchemeType scheme_type = SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION;
   bool is_standard = GetStandardSchemeType(
       scheme.data(),
       Component(0, base::checked_cast<int>(scheme.length())),
       &scheme_type);
   if (!is_standard) {
-    // To be consistent with blink, local non-standard schemes are currently
-    // allowed to be tuple origins. Nonstandard schemes don't have hostnames,
-    // so their tuple is just ("protocol", "", 0).
+    // To be consistent with ShouldTreatAsOpaqueOrigin in Blink, local
+    // non-standard schemes are currently allowed to be tuple origins.
+    // Nonstandard schemes don't have hostnames, so their tuple is just
+    // ("protocol", "", 0).
     //
     // TODO: Migrate "content:" and "externalfile:" to be standard schemes, and
     // remove this local scheme exception.
     if (base::Contains(GetLocalSchemes(), scheme) && host.empty() && port == 0)
       return true;
 
-    // about:blank and other no-access schemes translate into an opaque origin.
-    if (base::Contains(GetNoAccessSchemes(), scheme))
-      return false;
-
     // Otherwise, allow non-standard schemes only if the Android WebView
     // workaround is enabled.
     return AllowNonStandardSchemesForAndroidWebView();
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl
index 9787b7e..60824cf 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl
@@ -38,5 +38,7 @@
   boolean isUserDecidingIntentLaunch() = 14;
   boolean isKnownProtocol() = 15;
   boolean isServedFromBackForwardCache() = 16;
+
+  // @since 88
   void disableNetworkErrorAutoReload() = 17;
 }
diff --git a/weblayer/public/java/org/chromium/weblayer/NavigateParams.java b/weblayer/public/java/org/chromium/weblayer/NavigateParams.java
index 0fc051ad..ea6dfed 100644
--- a/weblayer/public/java/org/chromium/weblayer/NavigateParams.java
+++ b/weblayer/public/java/org/chromium/weblayer/NavigateParams.java
@@ -64,7 +64,7 @@
 
         /**
          * Disables auto-reload for this navigation if the network is down and comes back later.
-         * Auto-reload is enabled by default. This is deprecated as of 89, instead use
+         * Auto-reload is enabled by default. This is deprecated as of 88, instead use
          * {@link Navigation#disableNetworkErrorAutoReload} which works for both embedder-initiated
          * navigations and also user-initiated navigations (such as back or forward). Auto-reload
          * is disabled if either method is called.
diff --git a/weblayer/public/java/org/chromium/weblayer/Navigation.java b/weblayer/public/java/org/chromium/weblayer/Navigation.java
index 4b11aa66..ca637951 100644
--- a/weblayer/public/java/org/chromium/weblayer/Navigation.java
+++ b/weblayer/public/java/org/chromium/weblayer/Navigation.java
@@ -261,12 +261,12 @@
      *
      * @throws IllegalStateException If not called during start.
      *
-     * @since 89
+     * @since 88
      */
     public void disableNetworkErrorAutoReload() {
         ThreadCheck.ensureOnUiThread();
         if (WebLayer.shouldPerformVersionChecks()
-                && WebLayer.getSupportedMajorVersionInternal() < 89) {
+                && WebLayer.getSupportedMajorVersionInternal() < 88) {
             throw new UnsupportedOperationException();
         }
         try {
diff --git a/weblayer/variables.gni b/weblayer/variables.gni
index d84b100..0cb57fe 100644
--- a/weblayer/variables.gni
+++ b/weblayer/variables.gni
@@ -9,7 +9,7 @@
   webview_includes_weblayer = true
 
   # Whether WebLayer will be included as a DFM.
-  weblayer_in_split = android_channel != "stable"
+  weblayer_in_split = true
 }
 
 weblayer_product_config_java_package = "org.chromium.weblayer_private"