diff --git a/.gn b/.gn
index 7fd46d2c..4025cb90 100644
--- a/.gn
+++ b/.gn
@@ -66,10 +66,11 @@
   "//extensions/browser:*",  # 20 errors
   "//extensions:*",  # 75 errors
   "//headless:*",  # 167 errors
+  "//ppapi/native_client/src/untrusted/pnacl_irt_shim:*",  # 197 errors
   "//ppapi/proxy:ipc_sources",  # 13 errors
   "//ppapi/proxy:proxy",  # 5 errors
   "//ppapi/thunk:*",  # 1071 errors
-  "//remoting/host/security_key:*",  # 68 errors
+  "//remoting/host/security_key:*",  # 10 errors
   "//remoting/host/win:*",  # 43 errors
   "//remoting/ios/app/settings:*",  # 6 errors
   "//remoting/protocol:*",  # 3 errors
diff --git a/DEPS b/DEPS
index e476285..e29e844 100644
--- a/DEPS
+++ b/DEPS
@@ -222,7 +222,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '1c467774e56e591519bfd112e517705dec1e88dc',
+  'skia_revision': 'e9ab391765c7a604a45a2c56a83ce77d3f5f4b4f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -238,7 +238,7 @@
   # 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': 'a3726e06b4f12ce30e754656c6214847f4da3864',
+  'swiftshader_revision': '69deca60e70d2992ee3f49c1a981b8432864abc0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -581,7 +581,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '1d7b5f2e794c97d6a979961dca88b6c2a6a40c69',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'be99633db632f35459756a4ee33356e1d18d7aed',
       'condition': 'checkout_ios',
   },
 
@@ -1343,7 +1343,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '3dd5b80bc4f172dd82925bb259cb7c82348409c5',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + '2e2730fe0190f72dadd631176d2f380b4a6186a8',
+    Var('chromium_git') + '/openscreen' + '@' + '223797e8b6f8e52f3b84c6e4240e2d941b46c6cb',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + 'bf21ccb1007bb531b45d9978919a56ea5059c245',
@@ -1360,7 +1360,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'b0345c864e4a2ddeeb3b5084fb6fc7b856957ac8',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f4ffdc1c0d10c444b6ca626319d6cdb20f8edae4',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1653,7 +1653,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@cf94984a3675b4f4ba8b1704caf84ddc9a64a833',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@1e4e6269e94b15d1aa0d48c1c5676060148dfc0f',
     'condition': 'checkout_src_internal',
   },
 
@@ -1683,7 +1683,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '5AGO5EZqpK0ZND-uQWyf96pSLYa3XMbKq1kjkswnThcC',
+        'version': 'd_dtMSh01ZuOaZzP6xcjbXtXOzuNoI8_MzWVrH4BQNoC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/WATCHLISTS b/WATCHLISTS
index 934082c..8d8a0fbb 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1722,6 +1722,10 @@
     'startup': {
       'filepath': 'chrome/browser/ui/startup/',
     },
+    'status_area': {
+      'filepath': 'ash/system/'\
+                  '|google_apis/calendar/',
+    },
     'storage_service' : {
       'filepath': 'components/services/storage/',
     },
@@ -2748,6 +2752,7 @@
                    'timvolodine@chromium.org'],
     'startup': ['grt+watch@chromium.org',
                 'pastarmovj+watch@chromium.org'],
+    'status_area': ['cros-status-area-eng+watchlist@google.com'],
     'storage_service': ['dmurph+watching-storageservice@chromium.org'],
     'structured_headers': ['iclelland+watch@chromium.org'],
     'styleguide': ['danakj+watch@chromium.org',
diff --git a/android_webview/browser/gfx/root_frame_sink.cc b/android_webview/browser/gfx/root_frame_sink.cc
index b3e3afd..a47c826 100644
--- a/android_webview/browser/gfx/root_frame_sink.cc
+++ b/android_webview/browser/gfx/root_frame_sink.cc
@@ -7,7 +7,6 @@
 #include "android_webview/browser/gfx/child_frame.h"
 #include "android_webview/browser/gfx/display_scheduler_webview.h"
 #include "android_webview/browser/gfx/viz_compositor_thread_runner_webview.h"
-#include "base/no_destructor.h"
 #include "base/trace_event/trace_event.h"
 #include "components/viz/common/surfaces/frame_sink_id_allocator.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
@@ -18,8 +17,8 @@
 namespace {
 
 viz::FrameSinkId AllocateParentSinkId() {
-  static base::NoDestructor<viz::FrameSinkIdAllocator> allocator(0u);
-  return allocator->NextFrameSinkId();
+  static viz::FrameSinkIdAllocator allocator(0u);
+  return allocator.NextFrameSinkId();
 }
 
 }  // namespace
diff --git a/android_webview/browser/metrics/visibility_metrics_logger.cc b/android_webview/browser/metrics/visibility_metrics_logger.cc
index 4686d59..c5cf6d7 100644
--- a/android_webview/browser/metrics/visibility_metrics_logger.cc
+++ b/android_webview/browser/metrics/visibility_metrics_logger.cc
@@ -5,13 +5,11 @@
 #include "android_webview/browser/metrics/visibility_metrics_logger.h"
 
 #include "base/metrics/histogram_macros.h"
-#include "base/no_destructor.h"
 #include "base/rand_util.h"
 #include "base/time/time.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 
-using base::NoDestructor;
 using content::BrowserThread;
 
 namespace android_webview {
@@ -27,50 +25,45 @@
 }
 
 base::HistogramBase* VisibilityMetricsLogger::GetGlobalVisibilityHistogram() {
-  static NoDestructor<base::HistogramBase*> histogram(
-      CreateHistogramForDurationTracking(
-          "Android.WebView.Visibility.Global",
-          static_cast<int>(VisibilityMetricsLogger::Visibility::kMaxValue)));
-  return *histogram;
+  static base::HistogramBase* histogram(CreateHistogramForDurationTracking(
+      "Android.WebView.Visibility.Global",
+      static_cast<int>(VisibilityMetricsLogger::Visibility::kMaxValue)));
+  return histogram;
 }
 
 base::HistogramBase*
 VisibilityMetricsLogger::GetPerWebViewVisibilityHistogram() {
-  static NoDestructor<base::HistogramBase*> histogram(
-      CreateHistogramForDurationTracking(
-          "Android.WebView.Visibility.PerWebView",
-          static_cast<int>(VisibilityMetricsLogger::Visibility::kMaxValue)));
-  return *histogram;
+  static base::HistogramBase* histogram(CreateHistogramForDurationTracking(
+      "Android.WebView.Visibility.PerWebView",
+      static_cast<int>(VisibilityMetricsLogger::Visibility::kMaxValue)));
+  return histogram;
 }
 
 base::HistogramBase*
 VisibilityMetricsLogger::GetGlobalOpenWebVisibilityHistogram() {
-  static NoDestructor<base::HistogramBase*> histogram(
-      CreateHistogramForDurationTracking(
-          "Android.WebView.WebViewOpenWebVisible.Global",
-          static_cast<int>(
-              VisibilityMetricsLogger::WebViewOpenWebVisibility::kMaxValue)));
-  return *histogram;
+  static base::HistogramBase* histogram(CreateHistogramForDurationTracking(
+      "Android.WebView.WebViewOpenWebVisible.Global",
+      static_cast<int>(
+          VisibilityMetricsLogger::WebViewOpenWebVisibility::kMaxValue)));
+  return histogram;
 }
 
 base::HistogramBase*
 VisibilityMetricsLogger::GetPerWebViewOpenWebVisibilityHistogram() {
-  static NoDestructor<base::HistogramBase*> histogram(
-      CreateHistogramForDurationTracking(
-          "Android.WebView.WebViewOpenWebVisible.PerWebView",
-          static_cast<int>(
-              VisibilityMetricsLogger::WebViewOpenWebVisibility::kMaxValue)));
-  return *histogram;
+  static base::HistogramBase* histogram(CreateHistogramForDurationTracking(
+      "Android.WebView.WebViewOpenWebVisible.PerWebView",
+      static_cast<int>(
+          VisibilityMetricsLogger::WebViewOpenWebVisibility::kMaxValue)));
+  return histogram;
 }
 
 base::HistogramBase*
 VisibilityMetricsLogger::GetOpenWebVisibileScreenPortionHistogram() {
-  static NoDestructor<base::HistogramBase*> histogram(
-      CreateHistogramForDurationTracking(
-          "Android.WebView.WebViewOpenWebVisible.ScreenPortion",
-          static_cast<int>(VisibilityMetricsLogger::
-                               WebViewOpenWebScreenPortion::kMaxValue)));
-  return *histogram;
+  static base::HistogramBase* histogram(CreateHistogramForDurationTracking(
+      "Android.WebView.WebViewOpenWebVisible.ScreenPortion",
+      static_cast<int>(
+          VisibilityMetricsLogger::WebViewOpenWebScreenPortion::kMaxValue)));
+  return histogram;
 }
 
 VisibilityMetricsLogger::VisibilityMetricsLogger() {
@@ -318,4 +311,4 @@
   }
 }
 
-}  // namespace android_webview
\ No newline at end of file
+}  // namespace android_webview
diff --git a/android_webview/common/aw_content_client.cc b/android_webview/common/aw_content_client.cc
index 41011ec..7083785 100644
--- a/android_webview/common/aw_content_client.cc
+++ b/android_webview/common/aw_content_client.cc
@@ -46,7 +46,7 @@
 
 base::StringPiece AwContentClient::GetDataResource(
     int resource_id,
-    ui::ScaleFactor scale_factor) {
+    ui::ResourceScaleFactor scale_factor) {
   // TODO(boliu): Used only by WebKit, so only bundle those resources for
   // Android WebView.
   return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
diff --git a/android_webview/common/aw_content_client.h b/android_webview/common/aw_content_client.h
index aa78102..6f103bb 100644
--- a/android_webview/common/aw_content_client.h
+++ b/android_webview/common/aw_content_client.h
@@ -27,8 +27,9 @@
   // ContentClient implementation.
   void AddAdditionalSchemes(Schemes* schemes) override;
   std::u16string GetLocalizedString(int message_id) override;
-  base::StringPiece GetDataResource(int resource_id,
-                                    ui::ScaleFactor scale_factor) override;
+  base::StringPiece GetDataResource(
+      int resource_id,
+      ui::ResourceScaleFactor scale_factor) override;
   base::RefCountedMemory* GetDataResourceBytes(int resource_id) override;
   void SetGpuInfo(const gpu::GPUInfo& gpu_info) override;
   bool UsingSynchronousCompositing() override;
diff --git a/android_webview/common/aw_resource_bundle.cc b/android_webview/common/aw_resource_bundle.cc
index 2997db6..9bddb3d 100644
--- a/android_webview/common/aw_resource_bundle.cc
+++ b/android_webview/common/aw_resource_bundle.cc
@@ -47,7 +47,7 @@
   ui::ResourceBundle::InitSharedInstanceWithPakFileRegion(base::File(pak_fd),
                                                           pak_region);
 
-  std::pair<int, ui::ScaleFactor> extra_paks[] = {
+  std::pair<int, ui::ResourceScaleFactor> extra_paks[] = {
       {kAndroidWebViewMainPakDescriptor, ui::SCALE_FACTOR_NONE},
       {kAndroidWebView100PercentPakDescriptor, ui::SCALE_FACTOR_100P}};
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/services/ComponentsProviderServiceTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/services/ComponentsProviderServiceTest.java
index ac982171..a841bc6 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/services/ComponentsProviderServiceTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/services/ComponentsProviderServiceTest.java
@@ -11,6 +11,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.ResultReceiver;
 
+import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
@@ -20,21 +21,30 @@
 import org.junit.experimental.runners.Enclosed;
 import org.junit.runner.RunWith;
 
+import org.chromium.android_webview.common.SafeModeController;
+import org.chromium.android_webview.common.services.ISafeModeService;
+import org.chromium.android_webview.nonembedded.ComponentUpdaterResetSafeModeAction;
 import org.chromium.android_webview.services.ComponentsProviderService;
+import org.chromium.android_webview.services.SafeModeService;
 import org.chromium.android_webview.test.AwActivityTestRule;
 import org.chromium.android_webview.test.AwJUnit4ClassRunner;
+import org.chromium.android_webview.variations.VariationsSeedSafeModeAction;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.FileUtils;
 import org.chromium.base.PathUtils;
 import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.Feature;
 import org.chromium.components.background_task_scheduler.TaskIds;
 import org.chromium.components.component_updater.IComponentsProviderService;
 
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -48,6 +58,7 @@
  */
 @RunWith(Enclosed.class)
 public class ComponentsProviderServiceTest {
+    public static final String TEST_WEBVIEW_PACKAGE_NAME = "org.chromium.android_webview.shell";
     private static final String TEST_FILE_NAME = "%s_%s_%s_testfile.tmp";
     private static final File sDirectory =
             new File(PathUtils.getDataDirectory(), "components/cps/");
@@ -83,6 +94,7 @@
 
         @Test
         @SmallTest
+        @Feature({"AndroidWebView"})
         public void testInvalidComponent() throws Exception {
             final String componentId = "someInvalidComponentId";
             final Bundle resultBundle = getFilesForComponentSync(componentId);
@@ -91,6 +103,7 @@
 
         @Test
         @SmallTest
+        @Feature({"AndroidWebView"})
         public void testValidComponent() throws Exception {
             final String componentId = "testComponentA";
             final String sequenceNumber = "1";
@@ -103,6 +116,7 @@
 
         @Test
         @SmallTest
+        @Feature({"AndroidWebView"})
         public void testMultipleVersions() throws Exception {
             final String componentId = "testComponentB";
             final String sequenceNumber1 = "1";
@@ -121,6 +135,75 @@
             assertBundleForValidComponent(resultBundle2, componentId, sequenceNumber2, version2);
         }
 
+        @Test
+        @MediumTest
+        @Feature({"AndroidWebView"})
+        public void testComponentUpdaterReset() throws Throwable {
+            final String componentId = "testComponentA";
+            final String sequenceNumber = "1";
+            final String version = "2.3.4";
+            createComponentFiles(componentId, sequenceNumber, version);
+            Bundle resultBundle = getFilesForComponentSync(componentId);
+            assertBundleForValidComponent(resultBundle, componentId, sequenceNumber, version);
+
+            Intent intent = new Intent(ContextUtils.getApplicationContext(), SafeModeService.class);
+            final String componentUpdaterResetActionId =
+                    new ComponentUpdaterResetSafeModeAction().getId();
+            try (ServiceConnectionHelper helper =
+                            new ServiceConnectionHelper(intent, Context.BIND_AUTO_CREATE)) {
+                ISafeModeService service = ISafeModeService.Stub.asInterface(helper.getBinder());
+                service.setSafeMode(Arrays.asList(componentUpdaterResetActionId));
+            }
+
+            Assert.assertTrue("SafeMode should be enabled",
+                    SafeModeController.getInstance().isSafeModeEnabled(TEST_WEBVIEW_PACKAGE_NAME));
+            Set<String> actions = new HashSet<>();
+            actions.add(componentUpdaterResetActionId);
+            Assert.assertEquals("Querying the ContentProvider should yield the action we set",
+                    actions,
+                    SafeModeController.getInstance().queryActions(TEST_WEBVIEW_PACKAGE_NAME));
+
+            resultBundle = getFilesForComponentSync(componentId);
+            Assert.assertNull(componentId
+                            + " must return a null result Bundle while ComponentUpdaterReset is on",
+                    resultBundle);
+        }
+
+        @Test
+        @MediumTest
+        @Feature({"AndroidWebView"})
+        public void testCPSIgnoresIrrelevantSafeModeAction() throws Throwable {
+            final String componentId = "testComponentA";
+            final String sequenceNumber = "1";
+            final String version = "2.3.4";
+            createComponentFiles(componentId, sequenceNumber, version);
+            Bundle resultBundle = getFilesForComponentSync(componentId);
+            assertBundleForValidComponent(resultBundle, componentId, sequenceNumber, version);
+
+            Intent intent = new Intent(ContextUtils.getApplicationContext(), SafeModeService.class);
+            final String variationsSeedSafeModeActionId =
+                    new VariationsSeedSafeModeAction().getId();
+
+            try (ServiceConnectionHelper helper =
+                            new ServiceConnectionHelper(intent, Context.BIND_AUTO_CREATE)) {
+                ISafeModeService service = ISafeModeService.Stub.asInterface(helper.getBinder());
+                service.setSafeMode(Arrays.asList(variationsSeedSafeModeActionId));
+            }
+
+            Assert.assertTrue("SafeMode should be enabled",
+                    SafeModeController.getInstance().isSafeModeEnabled(TEST_WEBVIEW_PACKAGE_NAME));
+            Set<String> actions = new HashSet<>();
+            actions.add(variationsSeedSafeModeActionId);
+            Assert.assertEquals("Querying the ContentProvider should yield the action we set",
+                    actions,
+                    SafeModeController.getInstance().queryActions(TEST_WEBVIEW_PACKAGE_NAME));
+
+            resultBundle = getFilesForComponentSync(componentId);
+            Assert.assertNotNull(componentId
+                            + " must return a non-null Bundle while ComponentUpdaterReset is off",
+                    resultBundle);
+        }
+
         private Bundle getFilesForComponentSync(String componentId) throws Exception {
             final CountDownLatch latch = new CountDownLatch(1);
             final Bundle result = new Bundle();
@@ -180,6 +263,7 @@
         @After
         public void tearDown() {
             cleanupFiles();
+            SafeModeService.clearSharedPrefsForTesting();
         }
 
         @Test
@@ -238,6 +322,29 @@
                     /* expected = */ sequenceNumber + "_" + version,
                     /* actual = */ files[0].getName());
         }
+
+        @Test
+        @MediumTest
+        @Feature({"AndroidWebView"})
+        public void testComponentUpdaterResetCancelsUpdaterJob() throws Throwable {
+            final String componentUpdaterResetActionId =
+                    new ComponentUpdaterResetSafeModeAction().getId();
+            Intent intent = new Intent(ContextUtils.getApplicationContext(), SafeModeService.class);
+            try (ServiceConnectionHelper helper =
+                            new ServiceConnectionHelper(intent, Context.BIND_AUTO_CREATE)) {
+                ISafeModeService safeModeService =
+                        ISafeModeService.Stub.asInterface(helper.getBinder());
+                safeModeService.setSafeMode(Arrays.asList(componentUpdaterResetActionId));
+            }
+
+            JobScheduler jobScheduler =
+                    (JobScheduler) ContextUtils.getApplicationContext().getSystemService(
+                            Context.JOB_SCHEDULER_SERVICE);
+            mService.onCreate();
+            Assert.assertFalse("Service should have no updater job scheduled",
+                    ComponentsProviderService.isJobScheduled(
+                            jobScheduler, TaskIds.WEBVIEW_COMPONENT_UPDATE_JOB_ID));
+        }
     }
 
     private static void createComponentFiles(
diff --git a/android_webview/nonembedded/BUILD.gn b/android_webview/nonembedded/BUILD.gn
index 65054fdb..2b661a8b 100644
--- a/android_webview/nonembedded/BUILD.gn
+++ b/android_webview/nonembedded/BUILD.gn
@@ -21,9 +21,11 @@
   sources = [
     "java/src/org/chromium/android_webview/nonembedded/AwComponentUpdateService.java",
     "java/src/org/chromium/android_webview/nonembedded/AwNonembeddedUmaRecorder.java",
+    "java/src/org/chromium/android_webview/nonembedded/ComponentUpdaterResetSafeModeAction.java",
     "java/src/org/chromium/android_webview/nonembedded/LicenseActivity.java",
     "java/src/org/chromium/android_webview/nonembedded/LicenseContentProvider.java",
     "java/src/org/chromium/android_webview/nonembedded/NetworkFetcherTask.java",
+    "java/src/org/chromium/android_webview/nonembedded/NonembeddedSafeModeActionsList.java",
     "java/src/org/chromium/android_webview/nonembedded/WebViewApkApplication.java",
   ]
   deps = [
@@ -91,6 +93,7 @@
     "java/src/org/chromium/android_webview/services/AwMinidumpUploadJobService.java",
     "java/src/org/chromium/android_webview/services/AwMinidumpUploaderDelegate.java",
     "java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java",
+    "java/src/org/chromium/android_webview/services/ComponentUpdaterSafeModeUtils.java",
     "java/src/org/chromium/android_webview/services/ComponentsProviderPathUtil.java",
     "java/src/org/chromium/android_webview/services/ComponentsProviderService.java",
     "java/src/org/chromium/android_webview/services/CrashReceiverService.java",
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/AwComponentUpdateService.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/AwComponentUpdateService.java
index 8fbe05f0..ba934d0 100644
--- a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/AwComponentUpdateService.java
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/AwComponentUpdateService.java
@@ -10,6 +10,7 @@
 import android.content.SharedPreferences;
 import android.os.SystemClock;
 
+import org.chromium.android_webview.services.ComponentUpdaterSafeModeUtils;
 import org.chromium.android_webview.services.ComponentsProviderPathUtil;
 import org.chromium.base.Callback;
 import org.chromium.base.FileUtils;
@@ -70,14 +71,18 @@
     public boolean onStartJob(JobParameters params) {
         assert mJobParameters == null;
         mJobParameters = params;
-        return startUpdates();
+        return maybeStartUpdates();
     }
 
     // Called by JobScheduler.
     @Override
     public boolean onStopJob(JobParameters params) {
+        ComponentUpdaterSafeModeUtils.executeSafeModeIfEnabled(
+                new File(ComponentsProviderPathUtil.getComponentUpdateServiceDirectoryPath()));
+
         // TODO(https://crbug.com/1221092): Stop native updates when onStopJob, onDestroy are
         // called.
+
         setUnexpectedExit(false);
         mJobParameters = null;
 
@@ -96,7 +101,7 @@
         // Always keep the most recent startId as this is the one that should be used to stop
         // the service.
         mServiceStartedId = startId;
-        if (!startUpdates()) {
+        if (!maybeStartUpdates()) {
             stopSelf(startId);
             mServiceStartedId = 0;
         }
@@ -109,10 +114,16 @@
      * @return {@code true} if it successfully triggers component updates or if component are
      *         already updating, {@code false} if it fails to trigger the updates.
      */
-    private boolean startUpdates() {
+    private boolean maybeStartUpdates() {
         if (mIsUpdating) {
             return true;
         }
+
+        if (ComponentUpdaterSafeModeUtils.executeSafeModeIfEnabled(new File(
+                    ComponentsProviderPathUtil.getComponentUpdateServiceDirectoryPath()))) {
+            return false;
+        }
+
         maybeRecordUnexpectedExit();
 
         // TODO(http://crbug.com/1179297) look at doing this in a task on a background thread
@@ -138,6 +149,9 @@
     // Call the appropriate stop method according to how the service is launched.
     private void stopService() {
         mIsUpdating = false;
+        ComponentUpdaterSafeModeUtils.executeSafeModeIfEnabled(
+                new File(ComponentsProviderPathUtil.getComponentUpdateServiceDirectoryPath()));
+
         // Service is launched as a started service.
         if (mServiceStartedId > 0) {
             stopSelf(mServiceStartedId);
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/ComponentUpdaterResetSafeModeAction.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/ComponentUpdaterResetSafeModeAction.java
new file mode 100644
index 0000000..9348979
--- /dev/null
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/ComponentUpdaterResetSafeModeAction.java
@@ -0,0 +1,31 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview.nonembedded;
+
+import androidx.annotation.NonNull;
+
+import org.chromium.android_webview.common.SafeModeAction;
+
+/**
+ * A {@link SafeModeAction} to reset Component Updater.
+ */
+public class ComponentUpdaterResetSafeModeAction implements SafeModeAction {
+    private static final String TAG = "WebViewSafeMode";
+
+    // This ID should not be changed or reused.
+    private static final String ID = "reset_component_updater";
+
+    @Override
+    @NonNull
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    public void execute() {
+        // Each Component Updater Service will take requisite steps on startup.
+        // Do nothing here.
+    }
+}
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/NonembeddedSafeModeActionsList.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/NonembeddedSafeModeActionsList.java
new file mode 100644
index 0000000..25cf42050
--- /dev/null
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/NonembeddedSafeModeActionsList.java
@@ -0,0 +1,24 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview.nonembedded;
+
+import org.chromium.android_webview.common.SafeModeAction;
+
+/**
+ * Exposes the SafeModeActions supported by nonembedded Component Updater services.
+ */
+public final class NonembeddedSafeModeActionsList {
+    // Do not instantiate this class.
+    private NonembeddedSafeModeActionsList() {}
+
+    /**
+     * A list of SafeModeActions supported by nonembedded WebView processes. The set of actions to
+     * be executed will be specified by the nonembedded SafeModeService, however each action (if
+     * specified by the service) will be executed in the order listed below.
+     */
+    public static final SafeModeAction[] sList = {
+            new ComponentUpdaterResetSafeModeAction(),
+    };
+}
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/WebViewApkApplication.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/WebViewApkApplication.java
index da53823..024a90b 100644
--- a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/WebViewApkApplication.java
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/WebViewApkApplication.java
@@ -15,6 +15,7 @@
 import org.chromium.android_webview.AwLocaleConfig;
 import org.chromium.android_webview.ProductConfig;
 import org.chromium.android_webview.common.CommandLineUtil;
+import org.chromium.android_webview.common.SafeModeController;
 import org.chromium.android_webview.devui.util.WebViewPackageHelper;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
@@ -62,6 +63,12 @@
         // MonochromeApplication has its own locale configuration already, so call this here
         // rather than in maybeInitProcessGlobals.
         ResourceBundle.setAvailablePakLocales(AwLocaleConfig.getWebViewSupportedPakLocales());
+
+        // Only register nonembedded SafeMode actions for webview_apk or webview_service processes.
+        if (isWebViewProcess()) {
+            SafeModeController controller = SafeModeController.getInstance();
+            controller.registerActions(NonembeddedSafeModeActionsList.sList);
+        }
     }
 
     @Override
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/services/ComponentUpdaterSafeModeUtils.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/services/ComponentUpdaterSafeModeUtils.java
new file mode 100644
index 0000000..ed498fd4
--- /dev/null
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/services/ComponentUpdaterSafeModeUtils.java
@@ -0,0 +1,53 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview.services;
+
+import org.chromium.android_webview.common.SafeModeController;
+import org.chromium.base.ContextUtils;
+import org.chromium.base.FileUtils;
+import org.chromium.base.Log;
+import org.chromium.base.metrics.RecordHistogram;
+
+import java.io.File;
+import java.util.Set;
+
+/**
+ * A util class for Component Updater Safe Mode operations.
+ */
+public class ComponentUpdaterSafeModeUtils {
+    private static final String TAG = "AwCUSafeMode";
+    private static final String HISTOGRAM_COMPONENT_UPDATER_SAFEMODE_EXECUTED =
+            "Android.WebView.ComponentUpdater.SafeModeActionExecuted";
+
+    // Keep in sync with the ID in ComponentUpdaterResetSafeModeAction.
+    private static final String RESET_COMPONENT_UPDATER_SAFEMODE_ACTION_ID =
+            "reset_component_updater";
+
+    // Don't instantiate this class.
+    private ComponentUpdaterSafeModeUtils() {}
+
+    /**
+     * Executes Component Updater Safe Mode actions, if Safe Mode is enabled.
+     * @param configDir The directory containing dynamic configs.
+     * @return true if any SafeMode actions were executed, false otherwise.
+     */
+    public static boolean executeSafeModeIfEnabled(File configDir) {
+        SafeModeController controller = SafeModeController.getInstance();
+        Set<String> actions =
+                controller.queryActions(ContextUtils.getApplicationContext().getPackageName());
+
+        if (actions.isEmpty() || !actions.contains(RESET_COMPONENT_UPDATER_SAFEMODE_ACTION_ID)) {
+            RecordHistogram.recordBooleanHistogram(
+                    HISTOGRAM_COMPONENT_UPDATER_SAFEMODE_EXECUTED, false);
+            return false;
+        }
+
+        if (!FileUtils.recursivelyDeleteFile(configDir, null)) {
+            Log.w(TAG, "Failed to delete " + configDir.getAbsolutePath());
+        }
+        RecordHistogram.recordBooleanHistogram(HISTOGRAM_COMPONENT_UPDATER_SAFEMODE_EXECUTED, true);
+        return true;
+    }
+}
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/services/ComponentsProviderService.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/services/ComponentsProviderService.java
index d56b03d..2405517 100644
--- a/android_webview/nonembedded/java/src/org/chromium/android_webview/services/ComponentsProviderService.java
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/services/ComponentsProviderService.java
@@ -66,15 +66,17 @@
     // can't be reused.
     @IntDef({GetFilesResultCode.SUCCESS, GetFilesResultCode.FAILED_NOT_INSTALLED,
             GetFilesResultCode.FAILED_NO_VERSIONS, GetFilesResultCode.FAILED_NO_FDS,
-            GetFilesResultCode.FAILED_OPENING_FDS})
+            GetFilesResultCode.FAILED_OPENING_FDS,
+            GetFilesResultCode.FAILED_COMPONENT_UPDATER_SAFEMODE_ENABLED})
     private @interface GetFilesResultCode {
         int SUCCESS = 0;
         int FAILED_NOT_INSTALLED = 1;
         int FAILED_NO_VERSIONS = 2;
         int FAILED_NO_FDS = 3;
         int FAILED_OPENING_FDS = 4;
+        int FAILED_COMPONENT_UPDATER_SAFEMODE_ENABLED = 5;
         // Keep this one at the end and increment appropriately when adding new entries.
-        int COUNT = 5;
+        int COUNT = 6;
     }
 
     private File mDirectory;
@@ -85,6 +87,16 @@
         public void getFilesForComponent(String componentId, ResultReceiver resultReceiver) {
             final long startTime = System.currentTimeMillis();
 
+            if (ComponentUpdaterSafeModeUtils.executeSafeModeIfEnabled(mDirectory)) {
+                Log.w(TAG,
+                        "Component Updater Reset Mode enabled. Not handing out configs. "
+                                + componentId);
+                resultReceiver.send(RESULT_FAILED, /* resultData = */ null);
+                recordGetFilesResultAndDuration(
+                        GetFilesResultCode.FAILED_COMPONENT_UPDATER_SAFEMODE_ENABLED, startTime);
+                return;
+            }
+
             // Note that there's no need to sanitize input because this method will check if there
             // is an existing folder under `mDirectory` with a name that equals the received
             // `componentId`. Because `mDirectory` is inside this application's data dir, only
@@ -144,6 +156,14 @@
     @Override
     public void onCreate() {
         mDirectory = new File(ComponentsProviderPathUtil.getComponentsServingDirectoryPath());
+        if (ComponentUpdaterSafeModeUtils.executeSafeModeIfEnabled(mDirectory)) {
+            JobScheduler jobScheduler =
+                    (JobScheduler) ContextUtils.getApplicationContext().getSystemService(
+                            Context.JOB_SCHEDULER_SERVICE);
+            jobScheduler.cancel(JOB_ID);
+            return;
+        }
+
         if (!mDirectory.exists() && !mDirectory.mkdirs()) {
             Log.e(TAG, "Failed to create directory " + mDirectory.getAbsolutePath());
             return;
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index ecf2b079..0a4bee22 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -998,7 +998,6 @@
     "system/holding_space/holding_space_tray_icon_preview.h",
     "system/holding_space/holding_space_util.cc",
     "system/holding_space/holding_space_util.h",
-    "system/holding_space/holding_space_view_builder.h",
     "system/holding_space/holding_space_view_delegate.cc",
     "system/holding_space/holding_space_view_delegate.h",
     "system/holding_space/pinned_files_bubble.cc",
@@ -1627,6 +1626,8 @@
     "wm/overview/overview_item.h",
     "wm/overview/overview_item_view.cc",
     "wm/overview/overview_item_view.h",
+    "wm/overview/overview_metrics.cc",
+    "wm/overview/overview_metrics.h",
     "wm/overview/overview_observer.h",
     "wm/overview/overview_session.cc",
     "wm/overview/overview_session.h",
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc
index 96b0ea7..6be4ffb 100644
--- a/ash/accelerators/accelerator_controller_impl.cc
+++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -912,9 +912,9 @@
   base::RecordAction(base::UserMetricsAction("Accel_Overview_F5"));
   OverviewController* overview_controller = Shell::Get()->overview_controller();
   if (overview_controller->InOverviewSession())
-    overview_controller->EndOverview();
+    overview_controller->EndOverview(OverviewEndAction::kAccelerator);
   else
-    overview_controller->StartOverview();
+    overview_controller->StartOverview(OverviewStartAction::kAccelerator);
 }
 
 void HandleToggleUnifiedDesktop() {
diff --git a/ash/accelerometer/accelerometer_reader.h b/ash/accelerometer/accelerometer_reader.h
index ad19703c..91f8010 100644
--- a/ash/accelerometer/accelerometer_reader.h
+++ b/ash/accelerometer/accelerometer_reader.h
@@ -10,12 +10,10 @@
 #include "ash/public/cpp/tablet_mode_observer.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 #include "base/observer_list.h"
 
 namespace base {
-template <typename T>
-class NoDestructor;
-
 class SequencedTaskRunner;
 }  // namespace base
 
diff --git a/ash/accessibility/chromevox/touch_exploration_manager.cc b/ash/accessibility/chromevox/touch_exploration_manager.cc
index 8c0c0e02..1efca099 100644
--- a/ash/accessibility/chromevox/touch_exploration_manager.cc
+++ b/ash/accessibility/chromevox/touch_exploration_manager.cc
@@ -45,7 +45,6 @@
   Shell::Get()->accessibility_controller()->AddObserver(this);
   Shell::Get()->activation_client()->AddObserver(this);
   keyboard::KeyboardUIController::Get()->AddObserver(this);
-  display::Screen::GetScreen()->AddObserver(this);
   UpdateTouchExplorationState();
 }
 
@@ -56,7 +55,6 @@
     Shell::Get()->accessibility_controller()->RemoveObserver(this);
   Shell::Get()->activation_client()->RemoveObserver(this);
   keyboard::KeyboardUIController::Get()->RemoveObserver(this);
-  display::Screen::GetScreen()->RemoveObserver(this);
   Shell::Get()->RemoveShellObserver(this);
   if (observing_window_)
     observing_window_->RemoveObserver(this);
diff --git a/ash/accessibility/chromevox/touch_exploration_manager.h b/ash/accessibility/chromevox/touch_exploration_manager.h
index 6d0f8c07..da3939d 100644
--- a/ash/accessibility/chromevox/touch_exploration_manager.h
+++ b/ash/accessibility/chromevox/touch_exploration_manager.h
@@ -96,6 +96,7 @@
   RootWindowController* root_window_controller_;
   CrasAudioHandler* audio_handler_;
   aura::Window* observing_window_;
+  display::ScopedDisplayObserver display_observer_{this};
 
   DISALLOW_COPY_AND_ASSIGN(TouchExplorationManager);
 };
diff --git a/ash/accessibility/magnifier/docked_magnifier_controller.cc b/ash/accessibility/magnifier/docked_magnifier_controller.cc
index 2216d45f..2382331 100644
--- a/ash/accessibility/magnifier/docked_magnifier_controller.cc
+++ b/ash/accessibility/magnifier/docked_magnifier_controller.cc
@@ -550,7 +550,8 @@
       split_view_controller->EndSplitView(
           SplitViewController::EndReason::kNormal);
     }
-    overview_controller->EndOverview();
+    overview_controller->EndOverview(
+        OverviewEndAction::kEnabledDockedMagnifier);
   }
 
   if (new_enabled) {
diff --git a/ash/accessibility/magnifier/docked_magnifier_controller_unittest.cc b/ash/accessibility/magnifier/docked_magnifier_controller_unittest.cc
index 45f6f25..32168fe 100644
--- a/ash/accessibility/magnifier/docked_magnifier_controller_unittest.cc
+++ b/ash/accessibility/magnifier/docked_magnifier_controller_unittest.cc
@@ -478,7 +478,7 @@
 
   // Enable overview mode followed by the magnifier.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   controller()->SetEnabled(true);
   EXPECT_TRUE(controller()->GetEnabled());
@@ -500,9 +500,8 @@
   auto window = CreateTestWindow();
   controller()->SetEnabled(true);
 
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   auto* root_window = Shell::GetPrimaryRootWindow();
   const auto* desk_bar_view = GetOverviewSession()
@@ -559,7 +558,7 @@
   // Simulate going into split view, by enabling overview mode, and snapping
   // a window to the left.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   split_view_controller()->SnapWindow(window.get(), SplitViewController::LEFT);
   EXPECT_EQ(split_view_controller()->state(),
@@ -604,7 +603,7 @@
           .Build();
 
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
 
   EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
diff --git a/ash/accessibility/ui/accessibility_panel_layout_manager.cc b/ash/accessibility/ui/accessibility_panel_layout_manager.cc
index 351c2488..b6872ec 100644
--- a/ash/accessibility/ui/accessibility_panel_layout_manager.cc
+++ b/ash/accessibility/ui/accessibility_panel_layout_manager.cc
@@ -15,7 +15,6 @@
 namespace ash {
 
 AccessibilityPanelLayoutManager::AccessibilityPanelLayoutManager() {
-  display::Screen::GetScreen()->AddObserver(this);
   Shell::Get()->activation_client()->AddObserver(this);
   Shell::Get()->AddShellObserver(this);
 }
@@ -23,7 +22,6 @@
 AccessibilityPanelLayoutManager::~AccessibilityPanelLayoutManager() {
   Shell::Get()->RemoveShellObserver(this);
   Shell::Get()->activation_client()->RemoveObserver(this);
-  display::Screen::GetScreen()->RemoveObserver(this);
 }
 
 void AccessibilityPanelLayoutManager::SetAlwaysVisible(bool always_visible) {
diff --git a/ash/accessibility/ui/accessibility_panel_layout_manager.h b/ash/accessibility/ui/accessibility_panel_layout_manager.h
index c42e3a2..18ba7d8c 100644
--- a/ash/accessibility/ui/accessibility_panel_layout_manager.h
+++ b/ash/accessibility/ui/accessibility_panel_layout_manager.h
@@ -81,6 +81,8 @@
   // Window bounds when not in fullscreen
   gfx::Rect panel_bounds_ = gfx::Rect(0, 0, 0, 0);
 
+  display::ScopedDisplayObserver display_observer_{this};
+
   // Determines whether panel is hidden when browser is in fullscreen.
   bool always_visible_ = false;
 
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index cfc364f..4600725 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -827,8 +827,10 @@
     // If overview session is active (e.g. on one side of the split view), end
     // it immediately, to prevent overview UI being visible while transitioning
     // to home screen.
-    if (overview_controller->InOverviewSession())
-      overview_controller->EndOverview(OverviewEnterExitType::kImmediateExit);
+    if (overview_controller->InOverviewSession()) {
+      overview_controller->EndOverview(OverviewEndAction::kEnterHomeLauncher,
+                                       OverviewEnterExitType::kImmediateExit);
+    }
 
     // End split view mode.
     split_view_controller->EndSplitView(
@@ -838,7 +840,8 @@
   // If overview is active (if overview was active in split view, it exited by
   // this point), just fade it out to home screen.
   if (overview_controller->InOverviewSession()) {
-    overview_controller->EndOverview(OverviewEnterExitType::kFadeOutExit);
+    overview_controller->EndOverview(OverviewEndAction::kEnterHomeLauncher,
+                                     OverviewEnterExitType::kFadeOutExit);
     return true;
   }
 
diff --git a/ash/app_list/app_list_controller_impl_unittest.cc b/ash/app_list/app_list_controller_impl_unittest.cc
index f6cdafd..b634e99 100644
--- a/ash/app_list/app_list_controller_impl_unittest.cc
+++ b/ash/app_list/app_list_controller_impl_unittest.cc
@@ -611,20 +611,20 @@
 // closed.
 TEST_F(AppListControllerImplTest,
        CloseAppListShownFromOverviewAfterTabletExit) {
+  auto* shell = Shell::Get();
+  auto* tablet_mode_controller = shell->tablet_mode_controller();
   // Move to tablet mode and back.
-  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
-  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
+  tablet_mode_controller->SetEnabledForTest(true);
+  tablet_mode_controller->SetEnabledForTest(false);
 
   std::unique_ptr<aura::Window> w(
       AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)));
-  OverviewController* const overview_controller =
-      Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
 
   // Press home button - verify overview exits and the app list is shown.
   PressHomeButton();
 
-  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_FALSE(shell->overview_controller()->InOverviewSession());
   EXPECT_EQ(AppListViewState::kPeeking, GetAppListView()->app_list_state());
   GetAppListTestHelper()->CheckVisibility(true);
   ASSERT_TRUE(GetAppListView()->GetWidget());
@@ -911,14 +911,13 @@
 TEST_F(AppListControllerImplTest,
        HomeScreenVisibleAfterDisplayUpdateInOverview) {
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
 
   // Trigger a display configuration change, this simulates screen rotation.
   Shell::Get()->app_list_controller()->OnDisplayConfigurationChanged();
 
   // End overview mode, the home launcher should be visible.
-  overview_controller->EndOverview();
+  ExitOverview();
   ShellTestApi().WaitForOverviewAnimationState(
       OverviewAnimationState::kExitAnimationComplete);
 
diff --git a/ash/app_list/app_list_presenter_impl.cc b/ash/app_list/app_list_presenter_impl.cc
index 4994e6a..0e90195 100644
--- a/ash/app_list/app_list_presenter_impl.cc
+++ b/ash/app_list/app_list_presenter_impl.cc
@@ -127,7 +127,6 @@
 AppListPresenterImpl::AppListPresenterImpl(AppListControllerImpl* controller)
     : controller_(controller) {
   DCHECK(controller_);
-  display_observation_.Observe(display::Screen::GetScreen());
 }
 
 AppListPresenterImpl::~AppListPresenterImpl() {
diff --git a/ash/app_list/app_list_presenter_impl.h b/ash/app_list/app_list_presenter_impl.h
index 6eeda62..bcecda6 100644
--- a/ash/app_list/app_list_presenter_impl.h
+++ b/ash/app_list/app_list_presenter_impl.h
@@ -21,7 +21,6 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/scoped_multi_source_observation.h"
-#include "base/scoped_observation.h"
 #include "ui/aura/client/focus_change_observer.h"
 #include "ui/aura/window_observer.h"
 #include "ui/compositor/layer_animation_observer.h"
@@ -212,8 +211,7 @@
   std::unique_ptr<AppListPresenterEventFilter> event_filter_;
 
   // An observer that notifies AppListView when the display has changed.
-  base::ScopedObservation<display::Screen, display::DisplayObserver>
-      display_observation_{this};
+  display::ScopedDisplayObserver display_observer_{this};
 
   // An observer that notifies AppListView when the shelf state has changed.
   base::ScopedMultiSourceObservation<Shelf, ShelfObserver> shelf_observation_{
diff --git a/ash/app_list/app_list_presenter_unittest.cc b/ash/app_list/app_list_presenter_unittest.cc
index 265d42a..34c18ce 100644
--- a/ash/app_list/app_list_presenter_unittest.cc
+++ b/ash/app_list/app_list_presenter_unittest.cc
@@ -4211,14 +4211,13 @@
   GetAppListTestHelper()->CheckVisibility(true);
 
   // Enable overview mode.
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
 
   // Test that the AppListView is transparent.
   EXPECT_EQ(0.0f, GetAppListView()->GetWidget()->GetLayer()->opacity());
 
   // Disable overview mode.
-  overview_controller->EndOverview();
+  ExitOverview();
 
   // Show the launcher, test that the opacity is restored.
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
@@ -4263,13 +4262,13 @@
 
   // Enable overview mode.
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   ui::Layer* layer = GetAppListView()->GetWidget()->GetNativeWindow()->layer();
   EXPECT_EQ(0.0f, layer->opacity());
 
   // Disable overview mode.
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_FALSE(overview_controller->InOverviewSession());
   EXPECT_EQ(1.0f, layer->opacity());
 }
@@ -4300,12 +4299,11 @@
        AppListShownAfterWallpaperPreviewAndExitOverviewMode) {
   EnableTabletMode(true);
   wallpaper_test_api_->StartWallpaperPreview();
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_FALSE(IsAppListVisible());
 
   // Disable overview mode.
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_TRUE(IsAppListVisible());
 }
 
@@ -4367,7 +4365,7 @@
   GetAppListTestHelper()->CheckVisibility(true);
   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
 
   GoHome();
@@ -4387,7 +4385,7 @@
   std::unique_ptr<aura::Window> dummy_window(CreateTestWindowInShellWithId(1));
 
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
 
   split_view_controller()->SnapWindow(window.get(), SplitViewController::LEFT);
diff --git a/ash/app_list/views/assistant/assistant_test_api_impl.cc b/ash/app_list/views/assistant/assistant_test_api_impl.cc
index 2b9d9ac..d399398b 100644
--- a/ash/app_list/views/assistant/assistant_test_api_impl.cc
+++ b/ash/app_list/views/assistant/assistant_test_api_impl.cc
@@ -145,7 +145,8 @@
 }
 
 void AssistantTestApiImpl::StartOverview() {
-  Shell::Get()->overview_controller()->StartOverview();
+  Shell::Get()->overview_controller()->StartOverview(
+      OverviewStartAction::kTests);
 }
 
 void AssistantTestApiImpl::SetConsentStatus(
diff --git a/ash/capture_mode/capture_mode_session.cc b/ash/capture_mode/capture_mode_session.cc
index aec3db6..c4d0c3e 100644
--- a/ash/capture_mode/capture_mode_session.cc
+++ b/ash/capture_mode/capture_mode_session.cc
@@ -567,7 +567,7 @@
 
   TabletModeController::Get()->AddObserver(this);
   current_root_->AddObserver(this);
-  display::Screen::GetScreen()->AddObserver(this);
+  display_observer_.emplace(this);
   // Our event handling code assumes the capture bar widget has been initialized
   // already. So we start handling events after everything has been setup.
   aura::Env::GetInstance()->AddPreTargetHandler(
@@ -588,7 +588,7 @@
   is_shutting_down_ = true;
 
   aura::Env::GetInstance()->RemovePreTargetHandler(this);
-  display::Screen::GetScreen()->RemoveObserver(this);
+  display_observer_.reset();
   current_root_->RemoveObserver(this);
   TabletModeController::Get()->RemoveObserver(this);
   if (input_capture_window_) {
diff --git a/ash/capture_mode/capture_mode_session.h b/ash/capture_mode/capture_mode_session.h
index 634bdce..4b8601c 100644
--- a/ash/capture_mode/capture_mode_session.h
+++ b/ash/capture_mode/capture_mode_session.h
@@ -359,6 +359,9 @@
   // False only when we end the session to start recording.
   bool a11y_alert_on_session_exit_ = true;
 
+  // The display observer between init/shutdown.
+  absl::optional<display::ScopedDisplayObserver> display_observer_;
+
   // True once Shutdown() is called.
   bool is_shutting_down_ = false;
 
diff --git a/ash/capture_mode/capture_mode_unittests.cc b/ash/capture_mode/capture_mode_unittests.cc
index 319068c..7d6cdc4 100644
--- a/ash/capture_mode/capture_mode_unittests.cc
+++ b/ash/capture_mode/capture_mode_unittests.cc
@@ -1719,7 +1719,7 @@
   // Activation changes (such as opening overview) should not terminate the
   // countdown.
   start_countdown();
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(controller->IsActive());
   EXPECT_FALSE(controller->is_recording_in_progress());
 
diff --git a/ash/capture_mode/video_recording_watcher.cc b/ash/capture_mode/video_recording_watcher.cc
index 69d46f3..25f8607 100644
--- a/ash/capture_mode/video_recording_watcher.cc
+++ b/ash/capture_mode/video_recording_watcher.cc
@@ -203,7 +203,6 @@
   if (recording_source_ == CaptureModeSource::kRegion)
     partial_region_bounds_ = controller_->user_capture_region();
 
-  display::Screen::GetScreen()->AddObserver(this);
   window_being_recorded_->AddObserver(this);
   TabletModeController::Get()->AddObserver(this);
 
@@ -233,7 +232,6 @@
         ->cursor_window_controller()
         ->RemoveObserver(this);
   }
-  display::Screen::GetScreen()->RemoveObserver(this);
   window_being_recorded_->RemoveObserver(this);
 }
 
diff --git a/ash/capture_mode/video_recording_watcher.h b/ash/capture_mode/video_recording_watcher.h
index 0846beb..6f90467f 100644
--- a/ash/capture_mode/video_recording_watcher.h
+++ b/ash/capture_mode/video_recording_watcher.h
@@ -252,6 +252,9 @@
   // If |window_being_recorded_| is not a root window, we must make a request to
   // make it capturable by the |FrameSinkVideoCapturer|.
   aura::ScopedWindowCaptureRequest non_root_window_capture_request_;
+
+  // Register for DisplayObserver callbacks.
+  display::ScopedDisplayObserver display_observer_{this};
 };
 
 }  // namespace ash
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 7729e88..1224a5c 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -418,6 +418,10 @@
 const base::Feature kEnableNetworkingInDiagnosticsApp{
     "EnableNetworkingInDiagnosticsApp", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables OAuth support when printing via the IPP protocol.
+const base::Feature kEnableOAuthIpp{"EnableOAuthIpp",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables the OOBE ChromeVox hint dialog and announcement feature.
 const base::Feature kEnableOobeChromeVoxHint{"EnableOobeChromeVoxHint",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
@@ -1289,6 +1293,10 @@
          base::FeatureList::IsEnabled(kDiagnosticsAppNavigation);
 }
 
+bool IsOAuthIppEnabled() {
+  return base::FeatureList::IsEnabled(kEnableOAuthIpp);
+}
+
 bool IsNewOobeLayoutEnabled() {
   return base::FeatureList::IsEnabled(kNewOobeLayout);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 2715a4f..011d0d80 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -168,6 +168,7 @@
 extern const base::Feature kEnableLocalSearchService;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kEnableNetworkingInDiagnosticsApp;
+COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kEnableOAuthIpp;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kEnableOobeChromeVoxHint;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kEnablePciguardUi;
@@ -464,6 +465,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsNotificationScrollBarEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsNotificationsInContextMenuEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsNotificationsRefreshEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOAuthIppEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOobeChromeVoxHintEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPciguardUiEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPerDeskShelfEnabled();
diff --git a/ash/display/cros_display_config.cc b/ash/display/cros_display_config.cc
index 6f2834f..b4214e1 100644
--- a/ash/display/cros_display_config.cc
+++ b/ash/display/cros_display_config.cc
@@ -515,7 +515,6 @@
       public ScreenOrientationController::Observer {
  public:
   explicit ObserverImpl() {
-    display::Screen::GetScreen()->AddObserver(this);
     Shell::Get()->tablet_mode_controller()->AddObserver(this);
     Shell::Get()->screen_orientation_controller()->AddObserver(this);
   }
@@ -523,7 +522,6 @@
   ~ObserverImpl() override {
     Shell::Get()->screen_orientation_controller()->RemoveObserver(this);
     Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
-    display::Screen::GetScreen()->RemoveObserver(this);
   }
 
   void AddObserver(
@@ -564,6 +562,7 @@
   }
 
   mojo::AssociatedRemoteSet<mojom::CrosDisplayConfigObserver> observers_;
+  display::ScopedDisplayObserver display_observer_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ObserverImpl);
 };
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index 851a1d70..3111c9e 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -83,12 +83,12 @@
 
   void SetUp() override {
     AshTestBase::SetUp();
-    display::Screen::GetScreen()->AddObserver(this);
+    display_observer_.emplace(this);
     Shell::GetPrimaryRootWindow()->AddObserver(this);
   }
   void TearDown() override {
     Shell::GetPrimaryRootWindow()->RemoveObserver(this);
-    display::Screen::GetScreen()->RemoveObserver(this);
+    display_observer_.reset();
     AshTestBase::TearDown();
   }
 
@@ -182,6 +182,8 @@
   bool root_window_destroyed_ = false;
   uint32_t changed_metrics_ = 0u;
 
+  absl::optional<display::ScopedDisplayObserver> display_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(DisplayManagerTest);
 };
 
diff --git a/ash/display/output_protection_delegate.cc b/ash/display/output_protection_delegate.cc
index cc0bf1eb..aa90a2c 100644
--- a/ash/display/output_protection_delegate.cc
+++ b/ash/display/output_protection_delegate.cc
@@ -49,14 +49,12 @@
     return;
 
   window_->AddObserver(this);
-  display::Screen::GetScreen()->AddObserver(this);
 }
 
 OutputProtectionDelegate::~OutputProtectionDelegate() {
   if (!window_)
     return;
 
-  display::Screen::GetScreen()->RemoveObserver(this);
   window_->RemoveObserver(this);
   MaybeSetCaptureModeWindowProtection(window_,
                                       display::CONTENT_PROTECTION_METHOD_NONE);
@@ -86,7 +84,7 @@
 
 void OutputProtectionDelegate::OnWindowDestroying(aura::Window* window) {
   DCHECK_EQ(window, window_);
-  display::Screen::GetScreen()->RemoveObserver(this);
+  display_observer_.reset();
   window_->RemoveObserver(this);
   MaybeSetCaptureModeWindowProtection(window_,
                                       display::CONTENT_PROTECTION_METHOD_NONE);
diff --git a/ash/display/output_protection_delegate.h b/ash/display/output_protection_delegate.h
index 7bde0d1..e62fb71 100644
--- a/ash/display/output_protection_delegate.h
+++ b/ash/display/output_protection_delegate.h
@@ -11,6 +11,7 @@
 
 #include "ash/ash_export.h"
 #include "base/macros.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
 #include "ui/display/display_observer.h"
@@ -61,6 +62,8 @@
   struct ClientIdHolder;
   std::unique_ptr<ClientIdHolder> client_;
 
+  absl::optional<display::ScopedDisplayObserver> display_observer_{this};
+
   DISALLOW_COPY_AND_ASSIGN(OutputProtectionDelegate);
 };
 
diff --git a/ash/display/overscan_calibrator.cc b/ash/display/overscan_calibrator.cc
index b2148726..afb834e6 100644
--- a/ash/display/overscan_calibrator.cc
+++ b/ash/display/overscan_calibrator.cc
@@ -20,7 +20,6 @@
 #include "ui/compositor/paint_recorder.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/manager/managed_display_info.h"
-#include "ui/display/screen.h"
 #include "ui/gfx/canvas.h"
 
 namespace ash {
@@ -125,11 +124,9 @@
   Shell::Get()->window_tree_host_manager()->SetOverscanInsets(display_.id(),
                                                               gfx::Insets());
   UpdateUILayer();
-  display::Screen::GetScreen()->AddObserver(this);
 }
 
 OverscanCalibrator::~OverscanCalibrator() {
-  display::Screen::GetScreen()->RemoveObserver(this);
   // Overscan calibration has finished without commit, so the display has to
   // be the original offset.
   if (!committed_) {
diff --git a/ash/display/overscan_calibrator.h b/ash/display/overscan_calibrator.h
index 87d8dfb..0e864d0 100644
--- a/ash/display/overscan_calibrator.h
+++ b/ash/display/overscan_calibrator.h
@@ -70,6 +70,9 @@
   // The visualization layer for the current calibration region.
   std::unique_ptr<ui::Layer> calibration_layer_;
 
+  // Register for DisplayObserver callbacks.
+  display::ScopedDisplayObserver display_observer_{this};
+
   DISALLOW_COPY_AND_ASSIGN(OverscanCalibrator);
 };
 
diff --git a/ash/display/persistent_window_controller.cc b/ash/display/persistent_window_controller.cc
index 81613b9..443daf2f 100644
--- a/ash/display/persistent_window_controller.cc
+++ b/ash/display/persistent_window_controller.cc
@@ -14,7 +14,6 @@
 #include "base/containers/adapters.h"
 #include "base/metrics/histogram_macros.h"
 #include "ui/display/manager/display_manager.h"
-#include "ui/display/screen.h"
 
 namespace ash {
 
@@ -44,13 +43,9 @@
 
 constexpr char PersistentWindowController::kNumOfWindowsRestoredHistogramName[];
 
-PersistentWindowController::PersistentWindowController() {
-  display::Screen::GetScreen()->AddObserver(this);
-}
+PersistentWindowController::PersistentWindowController() = default;
 
-PersistentWindowController::~PersistentWindowController() {
-  display::Screen::GetScreen()->RemoveObserver(this);
-}
+PersistentWindowController::~PersistentWindowController() = default;
 
 void PersistentWindowController::OnWillProcessDisplayChanges() {
   if (!ShouldProcessWindowList())
diff --git a/ash/display/persistent_window_controller.h b/ash/display/persistent_window_controller.h
index 85e64bd5..f3dbb747 100644
--- a/ash/display/persistent_window_controller.h
+++ b/ash/display/persistent_window_controller.h
@@ -42,6 +42,9 @@
   // Temporary storage that stores windows that may need persistent info
   // stored on display removal. Cleared when display changes are processed.
   aura::WindowTracker need_persistent_info_windows_;
+
+  // Register for DisplayObserver callbacks.
+  display::ScopedDisplayObserver display_observer_{this};
 };
 
 }  // namespace ash
diff --git a/ash/display/resolution_notification_controller.cc b/ash/display/resolution_notification_controller.cc
index 2559a166..af5be2317 100644
--- a/ash/display/resolution_notification_controller.cc
+++ b/ash/display/resolution_notification_controller.cc
@@ -21,7 +21,6 @@
 #include "ui/display/display_features.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/manager/managed_display_info.h"
-#include "ui/display/screen.h"
 
 namespace ash {
 
@@ -67,12 +66,10 @@
 
 ResolutionNotificationController::ResolutionNotificationController() {
   Shell::Get()->window_tree_host_manager()->AddObserver(this);
-  display::Screen::GetScreen()->AddObserver(this);
 }
 
 ResolutionNotificationController::~ResolutionNotificationController() {
   Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
-  display::Screen::GetScreen()->RemoveObserver(this);
 }
 
 bool ResolutionNotificationController::PrepareNotificationAndSetDisplayMode(
diff --git a/ash/display/resolution_notification_controller.h b/ash/display/resolution_notification_controller.h
index 7de11c5..59b8100 100644
--- a/ash/display/resolution_notification_controller.h
+++ b/ash/display/resolution_notification_controller.h
@@ -93,6 +93,8 @@
 
   std::unique_ptr<ResolutionChangeInfo> change_info_;
 
+  display::ScopedDisplayObserver display_observer_{this};
+
   base::WeakPtr<DisplayChangeDialog> confirmation_dialog_;
 
   base::WeakPtrFactory<ResolutionNotificationController> weak_factory_{this};
diff --git a/ash/display/screen_orientation_controller.cc b/ash/display/screen_orientation_controller.cc
index e5b91c513..a674638 100644
--- a/ash/display/screen_orientation_controller.cc
+++ b/ash/display/screen_orientation_controller.cc
@@ -21,7 +21,6 @@
 #include "ui/display/display.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/manager/managed_display_info.h"
-#include "ui/display/screen.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/wm/public/activation_client.h"
 
@@ -221,14 +220,12 @@
       current_rotation_(display::Display::ROTATE_0) {
   Shell::Get()->tablet_mode_controller()->AddObserver(this);
   SplitViewController::Get(Shell::GetPrimaryRootWindow())->AddObserver(this);
-  display::Screen::GetScreen()->AddObserver(this);
   Shell::Get()->window_tree_host_manager()->AddObserver(this);
   AccelerometerReader::GetInstance()->AddObserver(this);
 }
 
 ScreenOrientationController::~ScreenOrientationController() {
   Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
-  display::Screen::GetScreen()->RemoveObserver(this);
   SplitViewController::Get(Shell::GetPrimaryRootWindow())->RemoveObserver(this);
   Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
   AccelerometerReader::GetInstance()->RemoveObserver(this);
diff --git a/ash/display/screen_orientation_controller.h b/ash/display/screen_orientation_controller.h
index 98283d22..355eb0a 100644
--- a/ash/display/screen_orientation_controller.h
+++ b/ash/display/screen_orientation_controller.h
@@ -297,6 +297,9 @@
   // orientation.
   std::unordered_map<aura::Window*, LockInfo> lock_info_map_;
 
+  // Register for DisplayObserver callbacks.
+  display::ScopedDisplayObserver display_observer_{this};
+
   DISALLOW_COPY_AND_ASSIGN(ScreenOrientationController);
 };
 
diff --git a/ash/display/window_tree_host_manager.cc b/ash/display/window_tree_host_manager.cc
index de388a6f..63f0305 100644
--- a/ash/display/window_tree_host_manager.cc
+++ b/ash/display/window_tree_host_manager.cc
@@ -235,7 +235,7 @@
 WindowTreeHostManager::~WindowTreeHostManager() = default;
 
 void WindowTreeHostManager::Start() {
-  display::Screen::GetScreen()->AddObserver(this);
+  display_observer_.emplace(this);
   Shell::Get()
       ->display_configurator()
       ->content_protection_manager()
@@ -269,7 +269,7 @@
       ->display_configurator()
       ->content_protection_manager()
       ->RemoveObserver(this);
-  display::Screen::GetScreen()->RemoveObserver(this);
+  display_observer_.reset();
 
   int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
 
diff --git a/ash/display/window_tree_host_manager.h b/ash/display/window_tree_host_manager.h
index 64d4365..d11c1cd 100644
--- a/ash/display/window_tree_host_manager.h
+++ b/ash/display/window_tree_host_manager.h
@@ -205,6 +205,9 @@
   // should be moved after a display configuration change.
   int64_t cursor_display_id_for_restore_;
 
+  // Receive DisplayObserver callbacks between Start and Shutdown.
+  absl::optional<display::ScopedDisplayObserver> display_observer_;
+
   // A repeating timer to trigger sending UMA metrics for primary display's
   // effective resolution at fixed intervals.
   std::unique_ptr<base::RepeatingTimer> effective_resolution_UMA_timer_;
diff --git a/ash/display/window_tree_host_manager_unittest.cc b/ash/display/window_tree_host_manager_unittest.cc
index c91454c..8547e32 100644
--- a/ash/display/window_tree_host_manager_unittest.cc
+++ b/ash/display/window_tree_host_manager_unittest.cc
@@ -70,7 +70,6 @@
  public:
   TestObserver() {
     Shell::Get()->window_tree_host_manager()->AddObserver(this);
-    display::Screen::GetScreen()->AddObserver(this);
     aura::client::GetFocusClient(Shell::GetPrimaryRootWindow())
         ->AddObserver(this);
     ::wm::GetActivationClient(Shell::GetPrimaryRootWindow())->AddObserver(this);
@@ -78,7 +77,6 @@
 
   ~TestObserver() override {
     Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
-    display::Screen::GetScreen()->RemoveObserver(this);
     aura::client::GetFocusClient(Shell::GetPrimaryRootWindow())
         ->RemoveObserver(this);
     ::wm::GetActivationClient(Shell::GetPrimaryRootWindow())
@@ -166,6 +164,8 @@
   int focus_changed_count_ = 0;
   int activation_changed_count_ = 0;
 
+  display::ScopedDisplayObserver display_observer_{this};
+
   DISALLOW_COPY_AND_ASSIGN(TestObserver);
 };
 
diff --git a/ash/drag_drop/tab_drag_drop_delegate_unittest.cc b/ash/drag_drop/tab_drag_drop_delegate_unittest.cc
index 909fdf1..44199a1 100644
--- a/ash/drag_drop/tab_drag_drop_delegate_unittest.cc
+++ b/ash/drag_drop/tab_drag_drop_delegate_unittest.cc
@@ -74,7 +74,7 @@
     // Create a dummy window and exit overview mode since drags can't be
     // initiated from overview mode.
     dummy_window_ = CreateToplevelTestWindow();
-    ASSERT_TRUE(Shell::Get()->overview_controller()->EndOverview());
+    ASSERT_TRUE(ExitOverview());
   }
 
   void TearDown() override {
diff --git a/ash/frame/wide_frame_view.cc b/ash/frame/wide_frame_view.cc
index 0f2dbfb..9100866 100644
--- a/ash/frame/wide_frame_view.cc
+++ b/ash/frame/wide_frame_view.cc
@@ -92,7 +92,6 @@
           std::make_unique<FrameContextMenuController>(target_, this)) {
   // WideFrameView is owned by its client, not by Views.
   SetOwnedByWidget(false);
-  display::Screen::GetScreen()->AddObserver(this);
 
   aura::Window* target_window = target->GetNativeWindow();
   target_window->AddObserver(this);
@@ -138,7 +137,6 @@
 WideFrameView::~WideFrameView() {
   if (widget_)
     widget_->CloseNow();
-  display::Screen::GetScreen()->RemoveObserver(this);
   if (target_) {
     HeaderView* target_header_view = GetTargetHeaderView();
     target_header_view->SetShouldPaintHeader(true);
diff --git a/ash/frame/wide_frame_view.h b/ash/frame/wide_frame_view.h
index eb0c116..73e55b9 100644
--- a/ash/frame/wide_frame_view.h
+++ b/ash/frame/wide_frame_view.h
@@ -88,6 +88,8 @@
 
   std::unique_ptr<views::Widget> widget_;
 
+  display::ScopedDisplayObserver display_observer_{this};
+
   HeaderView* header_view_ = nullptr;
 
   std::unique_ptr<FrameContextMenuController> frame_context_menu_controller_;
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 6b5a4a8..669a983 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -588,7 +588,6 @@
         std::make_unique<AutoLoginUserActivityHandler>();
 
   data_dispatcher_->AddObserver(this);
-  display_observation_.Observe(display::Screen::GetScreen());
   Shell::Get()->system_tray_notifier()->AddSystemTrayObserver(this);
   keyboard::KeyboardUIController::Get()->AddObserver(this);
 
diff --git a/ash/login/ui/lock_contents_view.h b/ash/login/ui/lock_contents_view.h
index 4e46760..dbcb47c 100644
--- a/ash/login/ui/lock_contents_view.h
+++ b/ash/login/ui/lock_contents_view.h
@@ -26,7 +26,6 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/scoped_observation.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -465,8 +464,7 @@
   // all actions are executed.
   std::vector<DisplayLayoutAction> layout_actions_;
 
-  base::ScopedObservation<display::Screen, display::DisplayObserver>
-      display_observation_{this};
+  display::ScopedDisplayObserver display_observer_{this};
 
   // All error bubbles and the tooltip view are child views of LockContentsView,
   // and will be torn down when LockContentsView is torn down.
diff --git a/ash/multi_user/user_switch_animator.cc b/ash/multi_user/user_switch_animator.cc
index 14a8ea9f..145d328 100644
--- a/ash/multi_user/user_switch_animator.cc
+++ b/ash/multi_user/user_switch_animator.cc
@@ -94,7 +94,8 @@
       animation_step_(ANIMATION_STEP_HIDE_OLD_USER),
       screen_cover_(GetScreenCover(NULL)),
       windows_by_account_id_() {
-  Shell::Get()->overview_controller()->EndOverview();
+  Shell::Get()->overview_controller()->EndOverview(
+      OverviewEndAction::kUserSwitch);
   BuildUserToWindowsListMap();
   AdvanceUserTransitionAnimation();
 
diff --git a/ash/public/cpp/app_list/app_list_config.h b/ash/public/cpp/app_list/app_list_config.h
index e10589b3..0501c75 100644
--- a/ash/public/cpp/app_list/app_list_config.h
+++ b/ash/public/cpp/app_list/app_list_config.h
@@ -7,15 +7,11 @@
 
 #include "ash/public/cpp/app_list/app_list_types.h"
 #include "ash/public/cpp/ash_public_export.h"
+#include "base/no_destructor.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/size.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 namespace gfx {
 class FontList;
 }
diff --git a/ash/public/cpp/holding_space/holding_space_constants.h b/ash/public/cpp/holding_space/holding_space_constants.h
index 66fa51a8..0386331 100644
--- a/ash/public/cpp/holding_space/holding_space_constants.h
+++ b/ash/public/cpp/holding_space/holding_space_constants.h
@@ -71,8 +71,10 @@
 // Note that this is not enforced for pinned items.
 constexpr base::TimeDelta kMaxFileAge = base::TimeDelta::FromDays(1);
 
-// The maximum allowed number of downloads to display in holding space UI.
+// The maximum allowed number of downloads to display in holding space UI in the
+// default case or in the case in-progress downloads integration is enabled.
 constexpr size_t kMaxDownloads = 2u;
+constexpr size_t kMaxDownloadsWithInProgressDownloadIntegration = 4u;
 
 // The maximum allowed number of screen captures to display in holding space UI.
 constexpr size_t kMaxScreenCaptures = 3u;
diff --git a/ash/public/cpp/holding_space/holding_space_progress.cc b/ash/public/cpp/holding_space/holding_space_progress.cc
index 67dca09..5fc06ad8 100644
--- a/ash/public/cpp/holding_space/holding_space_progress.cc
+++ b/ash/public/cpp/holding_space/holding_space_progress.cc
@@ -4,9 +4,24 @@
 
 #include "ash/public/cpp/holding_space/holding_space_progress.h"
 
+#include <limits>
+
 #include "base/check_op.h"
 
 namespace ash {
+namespace {
+
+// Helpers ---------------------------------------------------------------------
+
+// Returns whether or not the specified byte counts indicate completion.
+bool CalculateComplete(const absl::optional<int64_t>& current_bytes,
+                       const absl::optional<int64_t>& total_bytes) {
+  return current_bytes.has_value() && current_bytes == total_bytes;
+}
+
+}  // namespace
+
+// HoldingSpaceProgress --------------------------------------------------------
 
 HoldingSpaceProgress::HoldingSpaceProgress()
     : HoldingSpaceProgress(/*current_bytes=*/0,
@@ -15,11 +30,32 @@
 HoldingSpaceProgress::HoldingSpaceProgress(
     const absl::optional<int64_t>& current_bytes,
     const absl::optional<int64_t>& total_bytes)
-    : current_bytes_(current_bytes), total_bytes_(total_bytes) {
+    : HoldingSpaceProgress(current_bytes,
+                           total_bytes,
+                           /*complete=*/absl::nullopt) {}
+
+HoldingSpaceProgress::HoldingSpaceProgress(
+    const absl::optional<int64_t>& current_bytes,
+    const absl::optional<int64_t>& total_bytes,
+    const absl::optional<bool>& complete)
+    : current_bytes_(current_bytes),
+      total_bytes_(total_bytes),
+      complete_(
+          complete.value_or(CalculateComplete(current_bytes_, total_bytes_))) {
   DCHECK_GE(current_bytes_.value_or(0), 0);
   DCHECK_GE(total_bytes_.value_or(0), 0);
-  if (current_bytes_.has_value() && total_bytes_.has_value())
-    DCHECK_LE(current_bytes_.value(), total_bytes_.value());
+
+  if (!current_bytes_.has_value() || !total_bytes_.has_value()) {
+    DCHECK(!complete_);
+    return;
+  }
+
+  if (complete_) {
+    DCHECK_EQ(current_bytes_.value(), total_bytes_.value());
+    return;
+  }
+
+  DCHECK_LE(current_bytes_.value(), total_bytes_.value());
 }
 
 HoldingSpaceProgress::HoldingSpaceProgress(const HoldingSpaceProgress&) =
@@ -31,8 +67,8 @@
 HoldingSpaceProgress::~HoldingSpaceProgress() = default;
 
 bool HoldingSpaceProgress::operator==(const HoldingSpaceProgress& rhs) const {
-  return std::tie(current_bytes_, total_bytes_) ==
-         std::tie(rhs.current_bytes_, rhs.total_bytes_);
+  return std::tie(current_bytes_, total_bytes_, complete_) ==
+         std::tie(rhs.current_bytes_, rhs.total_bytes_, complete_);
 }
 
 HoldingSpaceProgress& HoldingSpaceProgress::operator+=(
@@ -42,17 +78,16 @@
 
   current_bytes_ = temp.current_bytes_;
   total_bytes_ = temp.total_bytes_;
+  complete_ = temp.complete_;
 
   return *this;
 }
 
 HoldingSpaceProgress HoldingSpaceProgress::operator+(
     const HoldingSpaceProgress& rhs) const {
-  absl::optional<int64_t> current_bytes(current_bytes_);
-  absl::optional<int64_t> total_bytes(total_bytes_);
-
   // The number of `current_bytes` should only be present if present for both
   // the lhs and `rhs` instances. Otherwise `current_bytes` is indeterminate.
+  absl::optional<int64_t> current_bytes(current_bytes_);
   if (current_bytes.has_value()) {
     current_bytes = rhs.current_bytes_.has_value()
                         ? absl::make_optional(current_bytes.value() +
@@ -62,6 +97,7 @@
 
   // The number of `total_bytes` should only be present if present for both the
   // lhs and `rhs` instances. Otherwise `total_bytes` is indeterminate.
+  absl::optional<int64_t> total_bytes(total_bytes_);
   if (total_bytes.has_value()) {
     total_bytes = rhs.total_bytes_.has_value()
                       ? absl::make_optional(total_bytes.value() +
@@ -69,20 +105,31 @@
                       : absl::nullopt;
   }
 
-  return HoldingSpaceProgress(current_bytes, total_bytes);
+  // The result of summing lhs and `rhs` instances is `complete` if and only if
+  // both the lhs and `rhs` are themselves complete.
+  const bool complete = complete_ && rhs.complete_;
+
+  return HoldingSpaceProgress(current_bytes, total_bytes, complete);
 }
 
 absl::optional<float> HoldingSpaceProgress::GetValue() const {
   if (IsComplete())
     return 1.f;
+
   if (IsIndeterminate())
     return absl::nullopt;
+
+  // If `current_bytes_` == `total_bytes_` but progress is not complete,
+  // return a value that is extremely close but not equal to `1.f`.
+  if (current_bytes_.value() == total_bytes_.value())
+    return 1.f - std::numeric_limits<float>::epsilon();
+
   return static_cast<double>(current_bytes_.value()) /
          static_cast<double>(total_bytes_.value());
 }
 
 bool HoldingSpaceProgress::IsComplete() const {
-  return !IsIndeterminate() && current_bytes_ == total_bytes_;
+  return complete_;
 }
 
 bool HoldingSpaceProgress::IsIndeterminate() const {
diff --git a/ash/public/cpp/holding_space/holding_space_progress.h b/ash/public/cpp/holding_space/holding_space_progress.h
index 8f96ca9..fa67f26 100644
--- a/ash/public/cpp/holding_space/holding_space_progress.h
+++ b/ash/public/cpp/holding_space/holding_space_progress.h
@@ -21,6 +21,14 @@
   HoldingSpaceProgress(const absl::optional<int64_t>& current_bytes,
                        const absl::optional<int64_t>& total_bytes);
 
+  // Creates an instance for the specified `current_bytes` and `total_bytes`
+  // which is explicitly `complete` or incomplete. If absent, completion will be
+  // calculated based on `current_bytes` and `total_bytes`. If `true`, then it
+  // must also be true that `current_bytes.value()` == `total_bytes.value()`.
+  HoldingSpaceProgress(const absl::optional<int64_t>& current_bytes,
+                       const absl::optional<int64_t>& total_bytes,
+                       const absl::optional<bool>& complete);
+
   HoldingSpaceProgress(const HoldingSpaceProgress&);
   HoldingSpaceProgress& operator=(const HoldingSpaceProgress&);
   ~HoldingSpaceProgress();
@@ -44,6 +52,7 @@
  private:
   absl::optional<int64_t> current_bytes_;
   absl::optional<int64_t> total_bytes_;
+  bool complete_;
 };
 
 }  // namespace ash
diff --git a/ash/public/cpp/holding_space/holding_space_progress_unittest.cc b/ash/public/cpp/holding_space/holding_space_progress_unittest.cc
index 9d92900..15b26e1 100644
--- a/ash/public/cpp/holding_space/holding_space_progress_unittest.cc
+++ b/ash/public/cpp/holding_space/holding_space_progress_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/public/cpp/holding_space/holding_space_progress.h"
 
+#include <limits>
 #include <vector>
 
 #include "testing/gtest/include/gtest/gtest.h"
@@ -20,24 +21,97 @@
   EXPECT_FALSE(progress.IsIndeterminate());
 }
 
-// Verifies that `HoldingSpaceProgress(int64_t, int64_t)` is WAI.
+// Verifies that `HoldingSpaceProgress(...)` is WAI.
 TEST_F(HoldingSpaceProgressTest, ExplicitConstructor) {
-  HoldingSpaceProgress progress(/*current_bytes=*/50, /*total_bytes=*/100);
-  EXPECT_EQ(progress.GetValue(), 0.5f);
-  EXPECT_FALSE(progress.IsComplete());
-  EXPECT_FALSE(progress.IsIndeterminate());
+  {
+    HoldingSpaceProgress progress(/*current_bytes=*/0, /*total_bytes=*/0);
+    EXPECT_EQ(progress.GetValue(), 1.f);
+    EXPECT_TRUE(progress.IsComplete());
+    EXPECT_FALSE(progress.IsIndeterminate());
+  }
+  {
+    HoldingSpaceProgress progress(/*current_bytes=*/0, /*total_bytes=*/0,
+                                  /*complete=*/false);
+    EXPECT_EQ(progress.GetValue(), 1.f - std::numeric_limits<float>::epsilon());
+    EXPECT_FALSE(progress.IsComplete());
+    EXPECT_FALSE(progress.IsIndeterminate());
+  }
+  {
+    HoldingSpaceProgress progress(/*current_bytes=*/0, /*total_bytes=*/0,
+                                  /*complete=*/true);
+    EXPECT_EQ(progress.GetValue(), 1.f);
+    EXPECT_TRUE(progress.IsComplete());
+    EXPECT_FALSE(progress.IsIndeterminate());
+  }
+  {
+    HoldingSpaceProgress progress(/*current_bytes=*/50, /*total_bytes=*/100);
+    EXPECT_EQ(progress.GetValue(), 0.5f);
+    EXPECT_FALSE(progress.IsComplete());
+    EXPECT_FALSE(progress.IsIndeterminate());
+  }
+  {
+    HoldingSpaceProgress progress(/*current_bytes=*/50, /*total_bytes=*/100,
+                                  /*complete=*/false);
+    EXPECT_EQ(progress.GetValue(), 0.5f);
+    EXPECT_FALSE(progress.IsComplete());
+    EXPECT_FALSE(progress.IsIndeterminate());
+  }
+  {
+    HoldingSpaceProgress progress(/*current_bytes=*/100, /*total_bytes=*/100);
+    EXPECT_EQ(progress.GetValue(), 1.f);
+    EXPECT_TRUE(progress.IsComplete());
+    EXPECT_FALSE(progress.IsIndeterminate());
+  }
+  {
+    HoldingSpaceProgress progress(/*current_bytes=*/100, /*total_bytes=*/100,
+                                  /*complete=*/false);
+    EXPECT_EQ(progress.GetValue(), 1.f - std::numeric_limits<float>::epsilon());
+    EXPECT_FALSE(progress.IsComplete());
+    EXPECT_FALSE(progress.IsIndeterminate());
+  }
+  {
+    HoldingSpaceProgress progress(/*current_bytes=*/100, /*total_bytes=*/100,
+                                  /*complete=*/true);
+    EXPECT_EQ(progress.GetValue(), 1.f);
+    EXPECT_TRUE(progress.IsComplete());
+    EXPECT_FALSE(progress.IsIndeterminate());
+  }
 }
 
 // Verifies that `HoldingSpaceProgress(const HoldingSpaceProgress&)` is WAI.
 TEST_F(HoldingSpaceProgressTest, CopyConstructor) {
-  HoldingSpaceProgress progress(/*current_bytes=*/50, /*total_bytes=*/100);
+  {
+    HoldingSpaceProgress progress(/*current_bytes=*/100, /*total_bytes=*/100);
 
-  HoldingSpaceProgress copy(progress);
-  EXPECT_EQ(copy.GetValue(), 0.5f);
-  EXPECT_FALSE(copy.IsComplete());
-  EXPECT_FALSE(copy.IsIndeterminate());
+    HoldingSpaceProgress copy(progress);
+    EXPECT_EQ(copy.GetValue(), 1.f);
+    EXPECT_TRUE(copy.IsComplete());
+    EXPECT_FALSE(copy.IsIndeterminate());
 
-  EXPECT_EQ(progress, copy);
+    EXPECT_EQ(progress, copy);
+  }
+  {
+    HoldingSpaceProgress progress(/*current_bytes=*/100, /*total_bytes=*/100,
+                                  /*complete=*/false);
+
+    HoldingSpaceProgress copy(progress);
+    EXPECT_EQ(copy.GetValue(), 1.f - std::numeric_limits<float>::epsilon());
+    EXPECT_FALSE(copy.IsComplete());
+    EXPECT_FALSE(copy.IsIndeterminate());
+
+    EXPECT_EQ(progress, copy);
+  }
+  {
+    HoldingSpaceProgress progress(/*current_bytes=*/100, /*total_bytes=*/100,
+                                  /*complete=*/true);
+
+    HoldingSpaceProgress copy(progress);
+    EXPECT_EQ(copy.GetValue(), 1.f);
+    EXPECT_TRUE(copy.IsComplete());
+    EXPECT_FALSE(copy.IsIndeterminate());
+
+    EXPECT_EQ(progress, copy);
+  }
 }
 
 // Verifies that the `+` operator is WAI.
@@ -48,23 +122,25 @@
     HoldingSpaceProgress expected_result;
   };
 
-  std::vector<TestCase> test_cases({
-      {HoldingSpaceProgress(), HoldingSpaceProgress(), HoldingSpaceProgress()},
-      {HoldingSpaceProgress(), HoldingSpaceProgress(absl::nullopt, 0),
-       HoldingSpaceProgress(absl::nullopt, 0)},
-      {HoldingSpaceProgress(), HoldingSpaceProgress(0, absl::nullopt),
-       HoldingSpaceProgress(0, absl::nullopt)},
-      {HoldingSpaceProgress(absl::nullopt, 1),
-       HoldingSpaceProgress(absl::nullopt, 1),
-       HoldingSpaceProgress(absl::nullopt, 2)},
-      {HoldingSpaceProgress(1, absl::nullopt),
-       HoldingSpaceProgress(1, absl::nullopt),
-       HoldingSpaceProgress(2, absl::nullopt)},
-      {HoldingSpaceProgress(50, 100), HoldingSpaceProgress(50, 100),
-       HoldingSpaceProgress(100, 200)},
-      {HoldingSpaceProgress(100, 100), HoldingSpaceProgress(100, 100),
-       HoldingSpaceProgress(200, 200)},
-  });
+  std::vector<TestCase> test_cases(
+      {{HoldingSpaceProgress(), HoldingSpaceProgress(), HoldingSpaceProgress()},
+       {HoldingSpaceProgress(), HoldingSpaceProgress(absl::nullopt, 0),
+        HoldingSpaceProgress(absl::nullopt, 0)},
+       {HoldingSpaceProgress(), HoldingSpaceProgress(0, absl::nullopt),
+        HoldingSpaceProgress(0, absl::nullopt)},
+       {HoldingSpaceProgress(absl::nullopt, 1),
+        HoldingSpaceProgress(absl::nullopt, 1),
+        HoldingSpaceProgress(absl::nullopt, 2)},
+       {HoldingSpaceProgress(1, absl::nullopt),
+        HoldingSpaceProgress(1, absl::nullopt),
+        HoldingSpaceProgress(2, absl::nullopt)},
+       {HoldingSpaceProgress(50, 100), HoldingSpaceProgress(50, 100),
+        HoldingSpaceProgress(100, 200)},
+       {HoldingSpaceProgress(100, 100), HoldingSpaceProgress(100, 100),
+        HoldingSpaceProgress(200, 200)},
+       {HoldingSpaceProgress(100, 100, true),
+        HoldingSpaceProgress(100, 100, false),
+        HoldingSpaceProgress(200, 200, false)}});
 
   for (const auto& test_case : test_cases)
     EXPECT_EQ(test_case.lhs + test_case.rhs, test_case.expected_result);
@@ -79,15 +155,17 @@
 
 // Verifies that `HoldingSpaceProgress::GetValue()` is WAI.
 TEST_F(HoldingSpaceProgressTest, GetValue) {
-  std::vector<TestCase<absl::optional<float>>> test_cases({
-      {HoldingSpaceProgress(), 1.f},
-      {HoldingSpaceProgress(0, 0), 1.f},
-      {HoldingSpaceProgress(absl::nullopt, absl::nullopt), absl::nullopt},
-      {HoldingSpaceProgress(absl::nullopt, 0), absl::nullopt},
-      {HoldingSpaceProgress(0, absl::nullopt), absl::nullopt},
-      {HoldingSpaceProgress(50, 100), 0.5f},
-      {HoldingSpaceProgress(100, 100), 1.f},
-  });
+  std::vector<TestCase<absl::optional<float>>> test_cases(
+      {{HoldingSpaceProgress(), 1.f},
+       {HoldingSpaceProgress(0, 0), 1.f},
+       {HoldingSpaceProgress(absl::nullopt, absl::nullopt), absl::nullopt},
+       {HoldingSpaceProgress(absl::nullopt, 0), absl::nullopt},
+       {HoldingSpaceProgress(0, absl::nullopt), absl::nullopt},
+       {HoldingSpaceProgress(50, 100), 0.5f},
+       {HoldingSpaceProgress(100, 100), 1.f},
+       {HoldingSpaceProgress(100, 100, true), 1.f},
+       {HoldingSpaceProgress(100, 100, false),
+        1.f - std::numeric_limits<float>::epsilon()}});
 
   for (const auto& test_case : test_cases)
     EXPECT_EQ(test_case.progress.GetValue(), test_case.expected_result);
@@ -103,6 +181,8 @@
       {HoldingSpaceProgress(0, absl::nullopt), false},
       {HoldingSpaceProgress(50, 100), false},
       {HoldingSpaceProgress(100, 100), true},
+      {HoldingSpaceProgress(100, 100, true), true},
+      {HoldingSpaceProgress(100, 100, false), false},
   });
 
   for (const auto& test_case : test_cases)
@@ -119,6 +199,8 @@
       {HoldingSpaceProgress(0, absl::nullopt), true},
       {HoldingSpaceProgress(50, 100), false},
       {HoldingSpaceProgress(100, 100), false},
+      {HoldingSpaceProgress(100, 100, true), false},
+      {HoldingSpaceProgress(100, 100, false), false},
   });
 
   for (const auto& test_case : test_cases)
diff --git a/ash/public/cpp/shelf_config.h b/ash/public/cpp/shelf_config.h
index 01265b06..0b2b8f3 100644
--- a/ash/public/cpp/shelf_config.h
+++ b/ash/public/cpp/shelf_config.h
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/display/display_observer.h"
 #include "ui/gfx/animation/tween.h"
@@ -338,6 +339,9 @@
   // config.
   std::unique_ptr<ShelfAccessibilityObserver> accessibility_observer_;
 
+  // Receive callbacks from DisplayObserver.
+  absl::optional<display::ScopedDisplayObserver> display_observer_;
+
   base::ObserverList<Observer> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(ShelfConfig);
diff --git a/ash/shelf/drag_window_from_shelf_controller.cc b/ash/shelf/drag_window_from_shelf_controller.cc
index b450a597..4da046fb 100644
--- a/ash/shelf/drag_window_from_shelf_controller.cc
+++ b/ash/shelf/drag_window_from_shelf_controller.cc
@@ -189,7 +189,9 @@
   if (std::abs(scroll_y) <= kOpenOverviewThreshold &&
       !overview_controller->InOverviewSession() &&
       windows_hider_->WindowsMinimized()) {
-    overview_controller->StartOverview(OverviewEnterExitType::kImmediateEnter);
+    overview_controller->StartOverview(
+        OverviewStartAction::kDragWindowFromShelf,
+        OverviewEnterExitType::kImmediateEnter);
     OnWindowDragStartedInOverview();
   }
 
@@ -257,8 +259,10 @@
   window_drag_result_ = absl::nullopt;
   if (ShouldGoToHomeScreen(location_in_screen, velocity_y)) {
     DCHECK(!in_splitview);
-    if (in_overview)
-      overview_controller->EndOverview(OverviewEnterExitType::kFadeOutExit);
+    if (in_overview) {
+      overview_controller->EndOverview(OverviewEndAction::kDragWindowFromShelf,
+                                       OverviewEnterExitType::kFadeOutExit);
+    }
     window_drag_result_ = ShelfWindowDragResult::kGoToHomeScreen;
   } else if (ShouldRestoreToOriginalBounds(location_in_screen, velocity_y)) {
     window_drag_result_ = ShelfWindowDragResult::kRestoreToOriginalBounds;
@@ -299,8 +303,10 @@
 
   // End overview if it was opened during dragging.
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  if (overview_controller->InOverviewSession())
-    overview_controller->EndOverview(OverviewEnterExitType::kImmediateExit);
+  if (overview_controller->InOverviewSession()) {
+    overview_controller->EndOverview(OverviewEndAction::kDragWindowFromShelf,
+                                     OverviewEnterExitType::kImmediateExit);
+  }
   ReshowHiddenWindowsOnDragEnd();
 
   window_drag_result_ = ShelfWindowDragResult::kDragCanceled;
@@ -719,6 +725,7 @@
   base::AutoReset<bool> auto_reset(&during_window_restoration_callback_, true);
   if (end_overview) {
     Shell::Get()->overview_controller()->EndOverview(
+        OverviewEndAction::kDragWindowFromShelf,
         OverviewEnterExitType::kImmediateExit);
   }
   ReshowHiddenWindowsOnDragEnd();
diff --git a/ash/shelf/drag_window_from_shelf_controller_unittest.cc b/ash/shelf/drag_window_from_shelf_controller_unittest.cc
index 444a84b..99bb54b51 100644
--- a/ash/shelf/drag_window_from_shelf_controller_unittest.cc
+++ b/ash/shelf/drag_window_from_shelf_controller_unittest.cc
@@ -280,7 +280,7 @@
   EXPECT_TRUE(overview_controller->overview_session()->IsWindowInOverview(
       window1.get()));
   EXPECT_FALSE(window2->IsVisible());
-  overview_controller->EndOverview();
+  ExitOverview();
 
   // If the dragged window is snapped in splitview, while the other windows are
   // showing in overview, do not reshow the hidden windows.
@@ -518,7 +518,7 @@
   EXPECT_TRUE(split_view_controller()->InSplitViewMode());
   EXPECT_FALSE(split_view_controller()->IsWindowInSplitView(window1.get()));
   EXPECT_TRUE(split_view_controller()->IsWindowInSplitView(window2.get()));
-  overview_controller->EndOverview();
+  ExitOverview();
 
   // If the window is flung with a small velocity:
   StartDrag(window1.get(), shelf_bounds.left_center());
@@ -548,7 +548,7 @@
   EXPECT_TRUE(split_view_controller()->InSplitViewMode());
   EXPECT_FALSE(split_view_controller()->IsWindowInSplitView(window1.get()));
   EXPECT_TRUE(split_view_controller()->IsWindowInSplitView(window2.get()));
-  overview_controller->EndOverview();
+  ExitOverview();
 }
 
 // Test wallpaper should be blurred as in overview, even though overview might
diff --git a/ash/shelf/home_to_overview_nudge_controller_unittest.cc b/ash/shelf/home_to_overview_nudge_controller_unittest.cc
index 59f61fc..57c6a61c 100644
--- a/ash/shelf/home_to_overview_nudge_controller_unittest.cc
+++ b/ash/shelf/home_to_overview_nudge_controller_unittest.cc
@@ -256,13 +256,13 @@
   EXPECT_FALSE(GetNudgeController()->HasHideTimerForTesting());
 
   // Transitioning to overview should hide the nudge.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
 
   EXPECT_FALSE(GetNudgeController()->nudge_for_testing());
 
   // Ending overview, and transitioning to the home screen again should not show
   // the nudge.
-  Shell::Get()->overview_controller()->EndOverview();
+  ExitOverview();
   EXPECT_FALSE(GetNudgeController()->nudge_for_testing());
   EXPECT_EQ(gfx::Transform(),
             GetHotseatWidget()->GetLayerForNudgeAnimation()->transform());
diff --git a/ash/shelf/hotseat_widget_unittest.cc b/ash/shelf/hotseat_widget_unittest.cc
index c31dd9f..cc78e15 100644
--- a/ash/shelf/hotseat_widget_unittest.cc
+++ b/ash/shelf/hotseat_widget_unittest.cc
@@ -179,7 +179,7 @@
     // toggle overview.
     if (!navigation_buttons_shown_in_tablet_mode_ &&
         Shell::Get()->tablet_mode_controller()->InTabletMode()) {
-      Shell::Get()->overview_controller()->StartOverview();
+      EnterOverview();
       return;
     }
 
@@ -199,7 +199,7 @@
     // toggle overview.
     if (!navigation_buttons_shown_in_tablet_mode_ &&
         Shell::Get()->tablet_mode_controller()->InTabletMode()) {
-      Shell::Get()->overview_controller()->EndOverview();
+      ExitOverview();
       return;
     }
 
@@ -770,8 +770,7 @@
 
   // Go into split view mode by first going into overview, and then snapping
   // the open window on one side.
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   SplitViewController* split_view_controller =
       SplitViewController::Get(Shell::GetPrimaryRootWindow());
   split_view_controller->SnapWindow(window.get(), SplitViewController::LEFT);
@@ -1244,13 +1243,13 @@
   {
     HotseatStateWatcher watcher(GetShelfLayoutManager());
     // Enter overview by using the controller.
-    Shell::Get()->overview_controller()->StartOverview();
+    EnterOverview();
     WaitForOverviewAnimation(/*enter=*/true);
 
     watcher.CheckEqual({HotseatState::kExtended});
   }
 
-  Shell::Get()->overview_controller()->EndOverview();
+  ExitOverview();
   WaitForOverviewAnimation(/*enter=*/false);
 
   // Test in-app -> overview again with the autohide shown shelf.
@@ -1261,7 +1260,7 @@
   {
     HotseatStateWatcher watcher(GetShelfLayoutManager());
     // Enter overview by using the controller.
-    Shell::Get()->overview_controller()->StartOverview();
+    EnterOverview();
     WaitForOverviewAnimation(/*enter=*/true);
 
     watcher.CheckEqual({});
@@ -1277,11 +1276,11 @@
   TabletModeControllerTestApi().EnterTabletMode();
   DisplayWorkAreaChangeCounter counter;
 
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   WaitForOverviewAnimation(/*enter=*/true);
   EXPECT_EQ(0, counter.count());
 
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   WaitForOverviewAnimation(/*enter=*/true);
   EXPECT_EQ(0, counter.count());
 }
@@ -1442,7 +1441,7 @@
   // This point will not be visible.
   auto* overview_controller = Shell::Get()->overview_controller();
   auto* hotseat_widget = GetPrimaryShelf()->hotseat_widget();
-  overview_controller->StartOverview();
+  EnterOverview();
   ASSERT_TRUE(overview_controller->InOverviewSession());
   ASSERT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state());
   gfx::Point far_left_point =
@@ -1772,7 +1771,7 @@
   wm::ActivateWindow(window.get());
 
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
 
   Shelf* const shelf = GetPrimaryShelf();
 
@@ -1822,7 +1821,7 @@
   wm::ActivateWindow(window.get());
 
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
 
   SplitViewController* split_view_controller =
       SplitViewController::Get(Shell::GetPrimaryRootWindow());
diff --git a/ash/shelf/scrollable_shelf_view_unittest.cc b/ash/shelf/scrollable_shelf_view_unittest.cc
index a74f46b..648a96a 100644
--- a/ash/shelf/scrollable_shelf_view_unittest.cc
+++ b/ash/shelf/scrollable_shelf_view_unittest.cc
@@ -1458,7 +1458,7 @@
   // Pin an app icon then enter the overview mode. Verify that app scaling is
   // turned on.
   const ShelfID shelf_id = AddAppShortcut();
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   WaitForOverviewAnimation(/*enter=*/true);
   EXPECT_EQ(HotseatDensity::kSemiDense,
             hotseat_widget->target_hotseat_density());
@@ -1470,7 +1470,7 @@
   EXPECT_EQ(HotseatDensity::kNormal, hotseat_widget->target_hotseat_density());
 
   // Exit overview mode. Verify the hotseat density.
-  Shell::Get()->overview_controller()->EndOverview();
+  ExitOverview();
   WaitForOverviewAnimation(/*enter=*/false);
   EXPECT_EQ(HotseatDensity::kNormal, hotseat_widget->target_hotseat_density());
 }
diff --git a/ash/shelf/shelf_config.cc b/ash/shelf/shelf_config.cc
index 2c7146a7..562334e 100644
--- a/ash/shelf/shelf_config.cc
+++ b/ash/shelf/shelf_config.cc
@@ -158,7 +158,7 @@
   Shell* const shell = Shell::Get();
 
   shell->app_list_controller()->AddObserver(this);
-  display::Screen::GetScreen()->AddObserver(this);
+  display_observer_.emplace(this);
   shell->system_tray_model()->virtual_keyboard()->AddObserver(this);
   shell->overview_controller()->AddObserver(this);
   shell->session_controller()->AddObserver(this);
@@ -175,7 +175,7 @@
   shell->session_controller()->RemoveObserver(this);
   shell->overview_controller()->RemoveObserver(this);
   shell->system_tray_model()->virtual_keyboard()->RemoveObserver(this);
-  display::Screen::GetScreen()->RemoveObserver(this);
+  display_observer_.reset();
   shell->app_list_controller()->RemoveObserver(this);
 }
 
diff --git a/ash/shelf/shelf_config_unittest.cc b/ash/shelf/shelf_config_unittest.cc
index 0df0f3f..a83fb2ec 100644
--- a/ash/shelf/shelf_config_unittest.cc
+++ b/ash/shelf/shelf_config_unittest.cc
@@ -176,12 +176,11 @@
   EXPECT_TRUE(ShelfConfig::Get()->is_in_app());
 
   // Now go into overview.
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(ShelfConfig::Get()->is_in_app());
 
   // Back to the app.
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_TRUE(ShelfConfig::Get()->is_in_app());
 
   // Leave the session.
diff --git a/ash/shelf/shelf_drag_handle_unittest.cc b/ash/shelf/shelf_drag_handle_unittest.cc
index b45d102..850d3ea7 100644
--- a/ash/shelf/shelf_drag_handle_unittest.cc
+++ b/ash/shelf/shelf_drag_handle_unittest.cc
@@ -790,7 +790,7 @@
   DragHandle* const drag_handle = shelf_widget->GetDragHandle();
 
   ASSERT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing());
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   ASSERT_FALSE(drag_handle->has_show_drag_handle_timer_for_testing());
 }
 
@@ -812,7 +812,7 @@
   TabletModeControllerTestApi().LeaveTabletMode();
   TabletModeControllerTestApi().EnterTabletMode();
 
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   ASSERT_FALSE(drag_handle->has_show_drag_handle_timer_for_testing());
 
   GetEventGenerator()->GestureTapAt(
@@ -844,8 +844,7 @@
 
   // Go into split view mode by first going into overview, and then snapping
   // the open window on one side.
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   SplitViewController* split_view_controller =
       SplitViewController::Get(shelf_widget->GetNativeWindow());
   split_view_controller->SnapWindow(window.get(), SplitViewController::LEFT);
@@ -875,8 +874,7 @@
 
   // Go into split view mode by first going into overview, and then snapping
   // the open window on one side.
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   SplitViewController* split_view_controller =
       SplitViewController::Get(shelf_widget->GetNativeWindow());
   split_view_controller->SnapWindow(window.get(), SplitViewController::LEFT);
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 97dd6cb..ac56193 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -381,7 +381,6 @@
 
   for (auto& observer : observers_)
     observer.WillDeleteShelfLayoutManager();
-  display::Screen::GetScreen()->RemoveObserver(this);
   auto* shell = Shell::Get();
   shell->locale_update_controller()->RemoveObserver(this);
   shell->RemoveShellObserver(this);
@@ -405,7 +404,6 @@
   state_.session_state = shell->session_controller()->GetSessionState();
   shelf_background_type_ = GetShelfBackgroundType();
   wallpaper_controller_observation_.Observe(shell->wallpaper_controller());
-  display::Screen::GetScreen()->AddObserver(this);
 
   // DesksController could be null when virtual desks feature is not enabled.
   if (DesksController::Get())
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index cb698048..a8ce5af 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -637,6 +637,8 @@
   base::ScopedObservation<WallpaperController, WallpaperControllerObserver>
       wallpaper_controller_observation_{this};
 
+  display::ScopedDisplayObserver display_observer_{this};
+
   // Location of the most recent mouse drag event in screen coordinate.
   gfx::Point last_mouse_drag_position_;
 
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 33625ec..6acf153 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -154,10 +154,8 @@
 
 class TestDisplayObserver : public display::DisplayObserver {
  public:
-  TestDisplayObserver() { display::Screen::GetScreen()->AddObserver(this); }
-  ~TestDisplayObserver() override {
-    display::Screen::GetScreen()->RemoveObserver(this);
-  }
+  TestDisplayObserver() = default;
+  ~TestDisplayObserver() override = default;
 
   int metrics_change_count() const { return metrics_change_count_; }
 
@@ -168,6 +166,7 @@
     metrics_change_count_++;
   }
 
+  display::ScopedDisplayObserver display_observer_{this};
   int metrics_change_count_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(TestDisplayObserver);
@@ -627,9 +626,8 @@
                 ShelfConfig::Get()->hidden_shelf_in_screen_portion(),
             GetShelfWidget()->GetWindowBoundsInScreen().y());
 
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
   // Tests that the shelf is visible when in overview mode.
-  overview_controller->StartOverview();
+  EnterOverview();
   ShellTestApi().WaitForOverviewAnimationState(
       OverviewAnimationState::kEnterAnimationComplete);
 
@@ -639,7 +637,7 @@
             GetShelfWidget()->GetBackgroundType());
 
   // Test that on exiting overview mode, the shelf returns to auto hide state.
-  overview_controller->EndOverview();
+  ExitOverview();
   ShellTestApi().WaitForOverviewAnimationState(
       OverviewAnimationState::kExitAnimationComplete);
 
@@ -3071,8 +3069,7 @@
   wm::ActivateWindow(window1.get());
 
   // Starts the drag from the center of the shelf's bottom.
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   gfx::Point start = shelf_widget_bounds.bottom_center();
   StartScroll(start);
   UpdateScroll(-shelf_size - hotseat_size - hotseat_padding_size);
@@ -3085,9 +3082,9 @@
       SplitViewController::Get(Shell::GetPrimaryRootWindow());
   split_view_controller->SnapWindow(window1.get(), SplitViewController::LEFT);
   split_view_controller->SnapWindow(window2.get(), SplitViewController::RIGHT);
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(split_view_controller->InSplitViewMode());
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
   StartScroll(shelf_widget_bounds.bottom_right());
   UpdateScroll(-shelf_size - hotseat_size - hotseat_padding_size);
   EXPECT_FALSE(IsWindowDragInProgress());
@@ -3108,8 +3105,7 @@
   window1->Hide();
   EXPECT_EQ(HotseatState::kShownHomeLauncher, GetHotseatWidget()->state());
 
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   GetHotseatWidget()->SetState(HotseatState::kShownHomeLauncher);
   const gfx::Rect hotseat_bounds = GetHotseatWidget()->GetTargetBounds();
 
@@ -3123,7 +3119,7 @@
       -(DragWindowFromShelfController::kVelocityToHomeScreenThreshold + 10));
 
   // We should exit overview mode after completing the fling gesture.
-  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
 }
 
 // Test that upward fling in overview transitions from overview to home.
@@ -3137,8 +3133,7 @@
       AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400));
   wm::ActivateWindow(window1.get());
 
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
 
   base::HistogramTester histogram_tester;
   HotseatStateWatcher watcher(GetShelfLayoutManager());
@@ -3150,7 +3145,7 @@
       true /* is_fling */,
       -(DragWindowFromShelfController::kVelocityToHomeScreenThreshold + 10));
 
-  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
 
   watcher.WaitUntilStateChanged();
   watcher.CheckEqual({HotseatState::kShownHomeLauncher});
@@ -3176,7 +3171,7 @@
   WindowState::Get(window1.get())->Minimize();
 
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   base::HistogramTester histogram_tester;
 
@@ -3216,7 +3211,7 @@
   split_view_controller->SnapWindow(window1.get(), SplitViewController::LEFT);
   split_view_controller->SnapWindow(window2.get(), SplitViewController::RIGHT);
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state());
 
@@ -3278,7 +3273,7 @@
   split_view_controller->SnapWindow(window1.get(), SplitViewController::LEFT);
   split_view_controller->SnapWindow(window2.get(), SplitViewController::RIGHT);
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state());
 
@@ -3820,13 +3815,12 @@
                           ui::SHOW_STATE_FULLSCREEN);
   wm::ActivateWindow(fullscreen.get());
 
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
   TestDisplayObserver observer;
-  overview_controller->StartOverview();
+  EnterOverview();
   WaitForOverviewAnimation(/*enter=*/true);
   ASSERT_TRUE(TabletModeControllerTestApi().IsTabletModeStarted());
   EXPECT_EQ(0, observer.metrics_change_count());
-  overview_controller->EndOverview();
+  ExitOverview();
   WaitForOverviewAnimation(/*enter=*/false);
   ASSERT_TRUE(TabletModeControllerTestApi().IsTabletModeStarted());
   EXPECT_EQ(0, observer.metrics_change_count());
@@ -3851,8 +3845,7 @@
                 display_bounds.height());
 
   // Change alignment during overview enter animation.
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   // When setting the shelf alignment, bounds aren't expected to animate.
   shelf->SetAlignment(ShelfAlignment::kLeft);
   // Setting alignment exits overview which we should wait for.
@@ -3860,9 +3853,9 @@
   EXPECT_EQ(left_shelf_bounds, GetShelfWidget()->GetWindowBoundsInScreen());
 
   // Change alignment during overview exit animation.
-  overview_controller->StartOverview();
+  EnterOverview();
   WaitForOverviewAnimation(/*enter=*/true);
-  overview_controller->EndOverview();
+  ExitOverview();
   // When setting the shelf alignment, bounds aren't expected to animate.
   shelf->SetAlignment(ShelfAlignment::kBottom);
   WaitForOverviewAnimation(/*enter=*/false);
diff --git a/ash/shelf/swipe_home_to_overview_controller.cc b/ash/shelf/swipe_home_to_overview_controller.cc
index f21518ed..3ed3dd0d 100644
--- a/ash/shelf/swipe_home_to_overview_controller.cc
+++ b/ash/shelf/swipe_home_to_overview_controller.cc
@@ -196,7 +196,8 @@
   // NOTE: No need to update the home launcher opacity and scale here - the
   // AppListControllerImpl will update the home launcher state when it detects
   // that the overview is starting.
-  Shell::Get()->overview_controller()->StartOverview();
+  Shell::Get()->overview_controller()->StartOverview(
+      OverviewStartAction::kExitHomeLauncher);
 
   // No need to keep blur disabled for the drag - note that blur might remain
   // disabled at this point due to the started overview transition (which
diff --git a/ash/shell_unittest.cc b/ash/shell_unittest.cc
index d704db05..5f5b0170 100644
--- a/ash/shell_unittest.cc
+++ b/ash/shell_unittest.cc
@@ -552,12 +552,11 @@
   // Confirm that pressing tab when overview mode is open does not go to home
   // button. Tab should be handled by overview mode and not hit the shell event
   // handler.
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   generator->PressKey(ui::VKEY_TAB, ui::EF_NONE);
   generator->ReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
   EXPECT_FALSE(home_button->GetNativeView()->HasFocus());
-  overview_controller->EndOverview();
+  ExitOverview();
 
   // Hit shift tab and expect that focus is on status widget.
   generator->PressKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
diff --git a/ash/system/audio/display_speaker_controller.cc b/ash/system/audio/display_speaker_controller.cc
index c0722f2..432430a92 100644
--- a/ash/system/audio/display_speaker_controller.cc
+++ b/ash/system/audio/display_speaker_controller.cc
@@ -9,7 +9,6 @@
 #include "third_party/cros_system_api/dbus/service_constants.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/manager/managed_display_info.h"
-#include "ui/display/screen.h"
 
 inline cras::DisplayRotation ToCRASDisplayRotation(
     display::Display::Rotation rotation) {
@@ -28,13 +27,11 @@
 namespace ash {
 
 DisplaySpeakerController::DisplaySpeakerController() {
-  display::Screen::GetScreen()->AddObserver(this);
   chromeos::PowerManagerClient::Get()->AddObserver(this);
 }
 
 DisplaySpeakerController::~DisplaySpeakerController() {
   chromeos::PowerManagerClient::Get()->RemoveObserver(this);
-  display::Screen::GetScreen()->RemoveObserver(this);
 }
 
 void DisplaySpeakerController::OnDisplayAdded(
diff --git a/ash/system/audio/display_speaker_controller.h b/ash/system/audio/display_speaker_controller.h
index 336e8bf..8985e71 100644
--- a/ash/system/audio/display_speaker_controller.h
+++ b/ash/system/audio/display_speaker_controller.h
@@ -31,6 +31,9 @@
  private:
   // Update the state of internal speakers based on orientation.
   void UpdateInternalSpeakerForDisplayRotation();
+
+  display::ScopedDisplayObserver display_observer_{this};
+
   DISALLOW_COPY_AND_ASSIGN(DisplaySpeakerController);
 };
 
diff --git a/ash/system/holding_space/downloads_section.cc b/ash/system/holding_space/downloads_section.cc
index 6280d51..97a18ab 100644
--- a/ash/system/holding_space/downloads_section.cc
+++ b/ash/system/holding_space/downloads_section.cc
@@ -6,6 +6,7 @@
 
 #include "ash/bubble/bubble_utils.h"
 #include "ash/bubble/simple_grid_layout.h"
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/holding_space/holding_space_client.h"
 #include "ash/public/cpp/holding_space/holding_space_constants.h"
 #include "ash/public/cpp/holding_space/holding_space_controller.h"
@@ -138,7 +139,10 @@
            HoldingSpaceItem::Type::kLacrosDownload,
            HoldingSpaceItem::Type::kNearbyShare,
            HoldingSpaceItem::Type::kPrintedPdf, HoldingSpaceItem::Type::kScan},
-          /*max_count=*/kMaxDownloads) {}
+          /*max_count=*/
+          features::IsHoldingSpaceInProgressDownloadsIntegrationEnabled()
+              ? kMaxDownloadsWithInProgressDownloadIntegration
+              : kMaxDownloads) {}
 
 DownloadsSection::~DownloadsSection() = default;
 
diff --git a/ash/system/holding_space/holding_space_item_chip_view.cc b/ash/system/holding_space/holding_space_item_chip_view.cc
index 2b85d9e3..4e93a1ba 100644
--- a/ash/system/holding_space/holding_space_item_chip_view.cc
+++ b/ash/system/holding_space/holding_space_item_chip_view.cc
@@ -17,7 +17,6 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/system/holding_space/holding_space_item_view.h"
 #include "ash/system/holding_space/holding_space_progress_ring.h"
-#include "ash/system/holding_space/holding_space_view_builder.h"
 #include "ash/system/holding_space/holding_space_view_delegate.h"
 #include "base/bind.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -37,7 +36,6 @@
 #include "ui/views/layout/box_layout_view.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/flex_layout.h"
-#include "ui/views/metadata/view_factory.h"
 
 namespace ash {
 namespace {
@@ -95,6 +93,19 @@
   using Callback = base::RepeatingCallback<void(views::Label*, gfx::Canvas*)>;
   void SetCallback(Callback callback) { callback_ = std::move(callback); }
 
+  void SetPaintToLayer(bool fills_bounds_opaquely) {
+    views::Label::SetPaintToLayer();
+    layer()->SetFillsBoundsOpaquely(fills_bounds_opaquely);
+  }
+
+  void SetStyle(bubble_utils::LabelStyle style) {
+    bubble_utils::ApplyStyle(this, style);
+  }
+
+  void SetViewAccessibilityIsIgnored(bool is_ignored) {
+    GetViewAccessibility().OverrideIsIgnored(is_ignored);
+  }
+
  private:
   // views::Label:
   void OnPaint(gfx::Canvas* canvas) override {
@@ -108,6 +119,9 @@
 
 BEGIN_VIEW_BUILDER(/*no export*/, PaintCallbackLabel, views::Label)
 VIEW_BUILDER_PROPERTY(PaintCallbackLabel::Callback, Callback)
+VIEW_BUILDER_PROPERTY(bubble_utils::LabelStyle, Style)
+VIEW_BUILDER_PROPERTY(bool, PaintToLayer)
+VIEW_BUILDER_PROPERTY(bool, ViewAccessibilityIsIgnored)
 END_VIEW_BUILDER
 
 // ProgressRingView ------------------------------------------------------------
@@ -162,38 +176,24 @@
 
 // Helpers ---------------------------------------------------------------------
 
-// Returns a label builder with given `style`, `elide_behavior`, and `callback`.
-std::unique_ptr<HoldingSpaceViewBuilder<views::Label>> CreateLabelBuilder(
-    bubble_utils::LabelStyle style,
-    gfx::ElideBehavior elide_behavior,
-    PaintCallbackLabel::Callback callback) {
-  auto label = views::Builder<PaintCallbackLabel>()
-                   .SetCallback(std::move(callback))
-                   .SetElideBehavior(elide_behavior)
-                   .SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT)
-                   .SetPaintToLayer()
-                   .Build();
-
-  // NOTE: A11y events are handled by `HoldingSpaceItemChipView`.
-  label->GetViewAccessibility().OverrideIsIgnored(true);
-  label->layer()->SetFillsBoundsOpaquely(false);
-  bubble_utils::ApplyStyle(label.get(), style);
-  return std::make_unique<HoldingSpaceViewBuilder<views::Label>>(
-      std::move(label));
+// Returns a label builder.
+// NOTE: A11y events are handled by `HoldingSpaceItemChipView`.
+views::Builder<PaintCallbackLabel> CreateLabelBuilder() {
+  auto label = views::Builder<PaintCallbackLabel>();
+  label.SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT)
+      .SetPaintToLayer(/*fills_bounds_opaquely=*/false)
+      .SetViewAccessibilityIsIgnored(true);
+  return label;
 }
 
-// Returns a secondary action builder that invokes `callback` on press.
-std::unique_ptr<views::Builder<views::ImageButton>>
-CreateSecondaryActionBuilder(views::ImageButton::PressedCallback callback) {
+// Returns a secondary action builder.
+views::Builder<views::ImageButton> CreateSecondaryActionBuilder() {
   using HorizontalAlignment = views::ImageButton::HorizontalAlignment;
   using VerticalAlignment = views::ImageButton::VerticalAlignment;
-  auto secondary_action =
-      std::make_unique<views::Builder<views::ImageButton>>();
-  secondary_action->SetCallback(std::move(callback))
-      .SetFocusBehavior(views::View::FocusBehavior::NEVER)
+  auto secondary_action = views::Builder<views::ImageButton>();
+  secondary_action.SetFocusBehavior(views::View::FocusBehavior::NEVER)
       .SetImageHorizontalAlignment(HorizontalAlignment::ALIGN_CENTER)
-      .SetImageVerticalAlignment(VerticalAlignment::ALIGN_MIDDLE)
-      .SetVisible(false);
+      .SetImageVerticalAlignment(VerticalAlignment::ALIGN_MIDDLE);
   return secondary_action;
 }
 
@@ -231,74 +231,71 @@
       base::BindRepeating(&HoldingSpaceItemChipView::OnSecondaryActionPressed,
                           base::Unretained(this));
 
-  HoldingSpaceViewBuilder<HoldingSpaceItemChipView>(this)
+  views::Builder<HoldingSpaceItemChipView>(this)
       .SetPreferredSize(gfx::Size(kPreferredWidth, kPreferredHeight))
       .SetLayoutManager(std::move(layout_manager))
       .AddChild(
-          HoldingSpaceViewBuilder<views::View>(
-              views::Builder<ProgressRingView>()
-                  .SetHoldingSpaceItem(item)
-                  .SetUseDefaultFillLayout(true))
+          views::Builder<ProgressRingView>()
+              .SetHoldingSpaceItem(item)
+              .SetUseDefaultFillLayout(true)
+              .AddChild(views::Builder<ObservableRoundedImageView>()
+                            .SetCornerRadius(kHoldingSpaceChipIconSize / 2)
+                            .SetBoundsChangedCallback(base::BindRepeating(
+                                &HoldingSpaceItemChipView::UpdateImageTransform,
+                                base::Unretained(this)))
+                            .CopyAddressTo(&image_)
+                            .SetID(kHoldingSpaceItemImageId))
+              .AddChild(CreateCheckmarkBuilder())
               .AddChild(
-                  HoldingSpaceViewBuilder<RoundedImageView>(
-                      views::Builder<ObservableRoundedImageView>()
-                          .SetCornerRadius(kHoldingSpaceChipIconSize / 2)
-                          .SetBoundsChangedCallback(base::BindRepeating(
-                              &HoldingSpaceItemChipView::UpdateImageTransform,
-                              base::Unretained(this))))
-                      .CopyAddressTo(&image_)
-                      .SetID(kHoldingSpaceItemImageId))
-              .AddChild(CreateCheckmark())
-              .AddChild(
-                  HoldingSpaceViewBuilder<views::View>(
-                      views::Builder<views::View>()
-                          .CopyAddressTo(&secondary_action_container_)
-                          .SetID(kHoldingSpaceItemSecondaryActionContainerId)
-                          .SetUseDefaultFillLayout(true)
-                          .SetVisible(false))
-                      .AddChild(CreateSecondaryActionBuilder(
-                                    secondary_action_callback)
-                                    ->CopyAddressTo(&secondary_action_pause_)
-                                    .SetID(kHoldingSpaceItemPauseButtonId))
-                      .AddChild(CreateSecondaryActionBuilder(
-                                    secondary_action_callback)
-                                    ->CopyAddressTo(&secondary_action_resume_)
+                  views::Builder<views::View>()
+                      .CopyAddressTo(&secondary_action_container_)
+                      .SetID(kHoldingSpaceItemSecondaryActionContainerId)
+                      .SetUseDefaultFillLayout(true)
+                      .SetVisible(false)
+                      .AddChild(CreateSecondaryActionBuilder()
+                                    .CopyAddressTo(&secondary_action_pause_)
+                                    .SetID(kHoldingSpaceItemPauseButtonId)
+                                    .SetCallback(secondary_action_callback)
+                                    .SetVisible(false))
+                      .AddChild(CreateSecondaryActionBuilder()
+                                    .CopyAddressTo(&secondary_action_resume_)
                                     .SetID(kHoldingSpaceItemResumeButtonId)
-                                    .SetFlipCanvasOnPaintForRTLUI(false))))
+                                    .SetCallback(secondary_action_callback)
+                                    .SetFlipCanvasOnPaintForRTLUI(false)
+                                    .SetVisible(false))))
       .AddChild(
-          HoldingSpaceViewBuilder<views::View>(
-              views::Builder<views::View>()
-                  .SetUseDefaultFillLayout(true)
-                  .SetProperty(views::kFlexBehaviorKey,
-                               views::FlexSpecification(
-                                   views::MinimumFlexSizeRule::kScaleToZero,
-                                   views::MaximumFlexSizeRule::kUnbounded)))
+          views::Builder<views::View>()
+              .SetUseDefaultFillLayout(true)
+              .SetProperty(views::kFlexBehaviorKey,
+                           views::FlexSpecification(
+                               views::MinimumFlexSizeRule::kScaleToZero,
+                               views::MaximumFlexSizeRule::kUnbounded))
               .AddChild(
-                  HoldingSpaceViewBuilder<views::View>(
-                      views::Builder<views::BoxLayoutView>()
-                          .SetOrientation(Orientation::kVertical)
-                          .SetMainAxisAlignment(MainAxisAlignment::kCenter)
-                          .SetCrossAxisAlignment(CrossAxisAlignment::kStretch)
-                          .SetInsideBorderInsets(kLabelMargins))
-                      .AddChild(CreateLabelBuilder(
-                                    bubble_utils::LabelStyle::kChipTitle,
-                                    gfx::ELIDE_MIDDLE,
-                                    paint_label_mask_callback)
-                                    ->CopyAddressTo(&primary_label_)
-                                    .SetID(kHoldingSpaceItemPrimaryChipLabelId))
+                  views::Builder<views::BoxLayoutView>()
+                      .SetOrientation(Orientation::kVertical)
+                      .SetMainAxisAlignment(MainAxisAlignment::kCenter)
+                      .SetCrossAxisAlignment(CrossAxisAlignment::kStretch)
+                      .SetInsideBorderInsets(kLabelMargins)
                       .AddChild(
-                          CreateLabelBuilder(
-                              bubble_utils::LabelStyle::kChipBody,
-                              gfx::FADE_TAIL, paint_label_mask_callback)
-                              ->CopyAddressTo(&secondary_label_)
-                              .SetID(kHoldingSpaceItemSecondaryChipLabelId)))
-              .AddChild(
-                  HoldingSpaceViewBuilder<views::BoxLayoutView>(
-                      views::Builder<views::BoxLayoutView>()
-                          .SetOrientation(Orientation::kHorizontal)
-                          .SetMainAxisAlignment(MainAxisAlignment::kEnd)
-                          .SetCrossAxisAlignment(CrossAxisAlignment::kCenter))
-                      .AddChild(CreatePrimaryAction(/*min_size=*/gfx::Size()))))
+                          CreateLabelBuilder()
+                              .CopyAddressTo(&primary_label_)
+                              .SetID(kHoldingSpaceItemPrimaryChipLabelId)
+                              .SetStyle(bubble_utils::LabelStyle::kChipTitle)
+                              .SetElideBehavior(gfx::ELIDE_MIDDLE)
+                              .SetCallback(paint_label_mask_callback))
+                      .AddChild(
+                          CreateLabelBuilder()
+                              .CopyAddressTo(&secondary_label_)
+                              .SetID(kHoldingSpaceItemSecondaryChipLabelId)
+                              .SetStyle(bubble_utils::LabelStyle::kChipBody)
+                              .SetElideBehavior(gfx::FADE_TAIL)
+                              .SetCallback(paint_label_mask_callback)))
+              .AddChild(views::Builder<views::BoxLayoutView>()
+                            .SetOrientation(Orientation::kHorizontal)
+                            .SetMainAxisAlignment(MainAxisAlignment::kEnd)
+                            .SetCrossAxisAlignment(CrossAxisAlignment::kCenter)
+                            .AddChild(CreatePrimaryActionBuilder(
+                                /*min_size=*/gfx::Size()))))
       .BuildChildren();
 
   // Subscribe to be notified of changes to `item_`'s image.
diff --git a/ash/system/holding_space/holding_space_item_chip_view.h b/ash/system/holding_space/holding_space_item_chip_view.h
index 0b287b26..5199673 100644
--- a/ash/system/holding_space/holding_space_item_chip_view.h
+++ b/ash/system/holding_space/holding_space_item_chip_view.h
@@ -9,6 +9,7 @@
 #include "ash/public/cpp/holding_space/holding_space_image.h"
 #include "ash/system/holding_space/holding_space_item_view.h"
 #include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/views/metadata/view_factory.h"
 
 namespace views {
 class ImageButton;
@@ -67,6 +68,13 @@
   base::CallbackListSubscription image_subscription_;
 };
 
+BEGIN_VIEW_BUILDER(/* no export */,
+                   HoldingSpaceItemChipView,
+                   HoldingSpaceItemView)
+END_VIEW_BUILDER
+
 }  // namespace ash
 
+DEFINE_VIEW_BUILDER(/* no export */, ash::HoldingSpaceItemChipView)
+
 #endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_CHIP_VIEW_H_
diff --git a/ash/system/holding_space/holding_space_item_screen_capture_view.cc b/ash/system/holding_space/holding_space_item_screen_capture_view.cc
index 41e11ecd..3d8e6f3 100644
--- a/ash/system/holding_space/holding_space_item_screen_capture_view.cc
+++ b/ash/system/holding_space/holding_space_item_screen_capture_view.cc
@@ -10,7 +10,6 @@
 #include "ash/public/cpp/rounded_image_view.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/system/holding_space/holding_space_util.h"
-#include "ash/system/holding_space/holding_space_view_builder.h"
 #include "ash/system/tray/tray_constants.h"
 #include "base/bind.h"
 #include "components/vector_icons/vector_icons.h"
@@ -24,7 +23,6 @@
 #include "ui/views/layout/box_layout_view.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/flex_layout_view.h"
-#include "ui/views/metadata/view_factory.h"
 
 namespace ash {
 
@@ -40,45 +38,42 @@
   using CrossAxisAlignment = views::BoxLayout::CrossAxisAlignment;
   using MainAxisAlignment = views::BoxLayout::MainAxisAlignment;
 
-  HoldingSpaceViewBuilder<HoldingSpaceItemScreenCaptureView>(this)
-      .SetPreferredSize(kHoldingSpaceScreenCaptureSize)
+  views::Builder<HoldingSpaceItemScreenCaptureView> builder(this);
+  builder.SetPreferredSize(kHoldingSpaceScreenCaptureSize)
       .SetLayoutManager(std::make_unique<views::FillLayout>())
       .AddChild(views::Builder<RoundedImageView>()
                     .CopyAddressTo(&image_)
                     .SetID(kHoldingSpaceItemImageId)
-                    .SetCornerRadius(kHoldingSpaceCornerRadius))
-      .AddChildIf(
-          item->type() == HoldingSpaceItem::Type::kScreenRecording,
-          base::BindOnce(
-              [](views::ImageView** play_icon) -> std::unique_ptr<views::View> {
-                return views::Builder<views::BoxLayoutView>()
-                    .SetOrientation(views::BoxLayout::Orientation::kHorizontal)
-                    .SetMainAxisAlignment(MainAxisAlignment::kCenter)
-                    .SetCrossAxisAlignment(CrossAxisAlignment::kCenter)
-                    .SetFocusBehavior(views::View::FocusBehavior::NEVER)
-                    .AddChild(
-                        views::Builder<views::ImageView>()
-                            .CopyAddressTo(play_icon)
-                            .SetID(kHoldingSpaceScreenCapturePlayIconId)
-                            .SetPreferredSize(kPlayIconSize)
-                            .SetImageSize(gfx::Size(kHoldingSpaceIconSize,
-                                                    kHoldingSpaceIconSize)))
-                    .Build();
-              },
-              &play_icon_))
-      .AddChild(HoldingSpaceViewBuilder<views::FlexLayoutView>(
-                    views::Builder<views::FlexLayoutView>()
-                        .SetOrientation(views::LayoutOrientation::kHorizontal)
-                        .SetCrossAxisAlignment(views::LayoutAlignment::kStart)
-                        .SetInteriorMargin(
-                            kCheckmarkAndPrimaryActionContainerPadding))
-                    .AddChild(CreateCheckmark())
-                    .AddChild(views::Builder<views::View>().SetProperty(
-                        views::kFlexBehaviorKey,
-                        views::FlexSpecification(
-                            views::MinimumFlexSizeRule::kScaleToZero,
-                            views::MaximumFlexSizeRule::kUnbounded)))
-                    .AddChild(CreatePrimaryAction(kPrimaryActionSize)))
+                    .SetCornerRadius(kHoldingSpaceCornerRadius));
+
+  if (item->type() == HoldingSpaceItem::Type::kScreenRecording) {
+    builder.AddChild(
+        views::Builder<views::BoxLayoutView>()
+            .SetOrientation(views::BoxLayout::Orientation::kHorizontal)
+            .SetMainAxisAlignment(MainAxisAlignment::kCenter)
+            .SetCrossAxisAlignment(CrossAxisAlignment::kCenter)
+            .SetFocusBehavior(views::View::FocusBehavior::NEVER)
+            .AddChild(views::Builder<views::ImageView>()
+                          .CopyAddressTo(&play_icon_)
+                          .SetID(kHoldingSpaceScreenCapturePlayIconId)
+                          .SetPreferredSize(kPlayIconSize)
+                          .SetImageSize(gfx::Size(kHoldingSpaceIconSize,
+                                                  kHoldingSpaceIconSize))));
+  }
+
+  builder
+      .AddChild(
+          views::Builder<views::FlexLayoutView>()
+              .SetOrientation(views::LayoutOrientation::kHorizontal)
+              .SetCrossAxisAlignment(views::LayoutAlignment::kStart)
+              .SetInteriorMargin(kCheckmarkAndPrimaryActionContainerPadding)
+              .AddChild(CreateCheckmarkBuilder())
+              .AddChild(views::Builder<views::View>().SetProperty(
+                  views::kFlexBehaviorKey,
+                  views::FlexSpecification(
+                      views::MinimumFlexSizeRule::kScaleToZero,
+                      views::MaximumFlexSizeRule::kUnbounded)))
+              .AddChild(CreatePrimaryActionBuilder(kPrimaryActionSize)))
       .BuildChildren();
 
   // Subscribe to be notified of changes to `item_`'s image.
diff --git a/ash/system/holding_space/holding_space_item_screen_capture_view.h b/ash/system/holding_space/holding_space_item_screen_capture_view.h
index 3e1629c..cbca273a 100644
--- a/ash/system/holding_space/holding_space_item_screen_capture_view.h
+++ b/ash/system/holding_space/holding_space_item_screen_capture_view.h
@@ -9,6 +9,7 @@
 #include "ash/public/cpp/holding_space/holding_space_image.h"
 #include "ash/system/holding_space/holding_space_item_view.h"
 #include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/views/metadata/view_factory.h"
 
 namespace views {
 class ImageView;
@@ -49,6 +50,13 @@
   base::CallbackListSubscription image_subscription_;
 };
 
+BEGIN_VIEW_BUILDER(/* no export */,
+                   HoldingSpaceItemScreenCaptureView,
+                   HoldingSpaceItemView)
+END_VIEW_BUILDER
+
 }  // namespace ash
 
+DEFINE_VIEW_BUILDER(/* no export */, ash::HoldingSpaceItemScreenCaptureView)
+
 #endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_SCREEN_CAPTURE_VIEW_H_
diff --git a/ash/system/holding_space/holding_space_item_view.cc b/ash/system/holding_space/holding_space_item_view.cc
index d12bf5a..7fb592c 100644
--- a/ash/system/holding_space/holding_space_item_view.cc
+++ b/ash/system/holding_space/holding_space_item_view.cc
@@ -27,7 +27,6 @@
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/layout/fill_layout.h"
-#include "ui/views/metadata/view_factory.h"
 #include "ui/views/painter.h"
 #include "ui/views/style/platform_style.h"
 #include "ui/views/vector_icons.h"
@@ -329,16 +328,17 @@
   OnSelectionUiChanged();
 }
 
-std::unique_ptr<views::ImageView> HoldingSpaceItemView::CreateCheckmark() {
+views::Builder<views::ImageView>
+HoldingSpaceItemView::CreateCheckmarkBuilder() {
   DCHECK(!checkmark_);
-  return views::Builder<views::ImageView>()
-      .CopyAddressTo(&checkmark_)
+  auto checkmark = views::Builder<views::ImageView>();
+  checkmark.CopyAddressTo(&checkmark_)
       .SetID(kHoldingSpaceItemCheckmarkId)
-      .SetVisible(selected())
-      .Build();
+      .SetVisible(selected());
+  return checkmark;
 }
 
-std::unique_ptr<views::View> HoldingSpaceItemView::CreatePrimaryAction(
+views::Builder<views::View> HoldingSpaceItemView::CreatePrimaryActionBuilder(
     const gfx::Size& min_size) {
   DCHECK(!primary_action_container_);
   DCHECK(!primary_action_cancel_);
@@ -350,8 +350,8 @@
   gfx::Size preferred_size(kHoldingSpaceIconSize, kHoldingSpaceIconSize);
   preferred_size.SetToMax(min_size);
 
-  return views::Builder<views::View>()
-      .CopyAddressTo(&primary_action_container_)
+  auto primary_action = views::Builder<views::View>();
+  primary_action.CopyAddressTo(&primary_action_container_)
       .SetID(kHoldingSpaceItemPrimaryActionContainerId)
       .SetUseDefaultFillLayout(true)
       .SetVisible(false)
@@ -378,8 +378,8 @@
               .SetImageHorizontalAlignment(HorizontalAlignment::ALIGN_CENTER)
               .SetImageVerticalAlignment(VerticalAlignment::ALIGN_MIDDLE)
               .SetPreferredSize(preferred_size)
-              .SetVisible(false))
-      .Build();
+              .SetVisible(false));
+  return primary_action;
 }
 
 void HoldingSpaceItemView::OnSelectionUiChanged() {
diff --git a/ash/system/holding_space/holding_space_item_view.h b/ash/system/holding_space/holding_space_item_view.h
index 5b3de88e..d440edf 100644
--- a/ash/system/holding_space/holding_space_item_view.h
+++ b/ash/system/holding_space/holding_space_item_view.h
@@ -13,6 +13,7 @@
 #include "base/callback_list.h"
 #include "base/scoped_observation.h"
 #include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/views/metadata/view_factory.h"
 #include "ui/views/view.h"
 
 namespace views {
@@ -80,8 +81,10 @@
   bool selected() const { return selected_; }
 
  protected:
-  std::unique_ptr<views::ImageView> CreateCheckmark();
-  std::unique_ptr<views::View> CreatePrimaryAction(const gfx::Size& min_size);
+  views::Builder<views::ImageView> CreateCheckmarkBuilder();
+  views::Builder<views::View> CreatePrimaryActionBuilder(
+      const gfx::Size& min_size);
+
   virtual void OnPrimaryActionVisibilityChanged(bool visible) {}
   virtual void OnSelectionUiChanged();
 
@@ -128,6 +131,11 @@
   base::WeakPtrFactory<HoldingSpaceItemView> weak_factory_{this};
 };
 
+BEGIN_VIEW_BUILDER(/* no export */, HoldingSpaceItemView, views::View)
+END_VIEW_BUILDER
+
 }  // namespace ash
 
+DEFINE_VIEW_BUILDER(/* no export */, ash::HoldingSpaceItemView)
+
 #endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_VIEW_H_
diff --git a/ash/system/holding_space/holding_space_tray_unittest.cc b/ash/system/holding_space/holding_space_tray_unittest.cc
index 42b68a4b0..f2944b1 100644
--- a/ash/system/holding_space/holding_space_tray_unittest.cc
+++ b/ash/system/holding_space/holding_space_tray_unittest.cc
@@ -8,6 +8,7 @@
 #include <deque>
 #include <vector>
 
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/holding_space/holding_space_client.h"
 #include "ash/public/cpp/holding_space/holding_space_constants.h"
 #include "ash/public/cpp/holding_space/holding_space_controller.h"
@@ -37,6 +38,7 @@
 #include "base/strings/strcat.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
@@ -745,7 +747,7 @@
 
   // Transition to overview, the shelf is expected to remain in home screen
   // style state.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   ShellTestApi().WaitForOverviewAnimationState(
       OverviewAnimationState::kEnterAnimationComplete);
 
@@ -900,24 +902,51 @@
 }
 
 // Base class for tests of the holding space downloads section parameterized by
-// the set of holding space item types which are expected to appear there.
+// the set of holding space item types which are expected to appear there and
+// whether or not the in-progress downloads integration feature is enabled.
 class HoldingSpaceTrayDownloadsSectionTest
     : public HoldingSpaceTrayTest,
-      public ::testing::WithParamInterface<HoldingSpaceItem::Type> {
+      public ::testing::WithParamInterface<
+          std::tuple<HoldingSpaceItem::Type, bool>> {
  public:
-  HoldingSpaceItem::Type GetType() const { return GetParam(); }
+  HoldingSpaceTrayDownloadsSectionTest() {
+    scoped_feature_list_.InitWithFeatureState(
+        features::kHoldingSpaceInProgressDownloadsIntegration,
+        IsInProgressDownloadsIntegrationEnabled());
+  }
+
+  // Returns the max number of downloads given the test parameterization.
+  size_t GetMaxNumberOfDownloads() const {
+    return IsInProgressDownloadsIntegrationEnabled()
+               ? kMaxDownloadsWithInProgressDownloadIntegration
+               : kMaxDownloads;
+  }
+
+  // Returns the holding space item type given the test parameterization.
+  HoldingSpaceItem::Type GetType() const { return std::get<0>(GetParam()); }
+
+  // Returns whether in-progress downloads integration is enabled given test
+  // parameterization.
+  bool IsInProgressDownloadsIntegrationEnabled() const {
+    return std::get<1>(GetParam());
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 INSTANTIATE_TEST_SUITE_P(
     All,
     HoldingSpaceTrayDownloadsSectionTest,
-    ::testing::Values(HoldingSpaceItem::Type::kArcDownload,
-                      HoldingSpaceItem::Type::kDiagnosticsLog,
-                      HoldingSpaceItem::Type::kDownload,
-                      HoldingSpaceItem::Type::kLacrosDownload,
-                      HoldingSpaceItem::Type::kNearbyShare,
-                      HoldingSpaceItem::Type::kPrintedPdf,
-                      HoldingSpaceItem::Type::kScan));
+    ::testing::Combine(
+        ::testing::Values(HoldingSpaceItem::Type::kArcDownload,
+                          HoldingSpaceItem::Type::kDiagnosticsLog,
+                          HoldingSpaceItem::Type::kDownload,
+                          HoldingSpaceItem::Type::kLacrosDownload,
+                          HoldingSpaceItem::Type::kNearbyShare,
+                          HoldingSpaceItem::Type::kPrintedPdf,
+                          HoldingSpaceItem::Type::kScan),
+        ::testing::Bool()));
 
 // Tests how download chips are updated during item addition, removal and
 // initialization.
@@ -931,7 +960,8 @@
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
 
   // Add a download item and verify recent file bubble gets shown.
-  HoldingSpaceItem* item_1 = AddItem(GetType(), base::FilePath("/tmp/fake_1"));
+  std::vector<HoldingSpaceItem*> items;
+  items.push_back(AddItem(GetType(), base::FilePath("/tmp/fake_1")));
 
   EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
   EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
@@ -942,65 +972,77 @@
 
   // Add partially initialized download item - verify it doesn't get shown in
   // the UI yet.
-  HoldingSpaceItem* item_2 =
-      AddPartiallyInitializedItem(GetType(), base::FilePath("/tmp/fake_2"));
+  items.push_back(
+      AddPartiallyInitializedItem(GetType(), base::FilePath("/tmp/fake_2")));
 
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
   std::vector<views::View*> download_chips = test_api()->GetDownloadChips();
   ASSERT_EQ(1u, download_chips.size());
-  EXPECT_EQ(item_1->id(),
+  EXPECT_EQ(items[0]->id(),
             HoldingSpaceItemView::Cast(download_chips[0])->item()->id());
 
-  // Add another download, and verify it's shown in the UI.
-  HoldingSpaceItem* item_3 = AddItem(GetType(), base::FilePath("/tmp/fake_3"));
+  // Add a few more download items until the section reaches capacity.
+  for (size_t i = 2; i <= GetMaxNumberOfDownloads(); ++i) {
+    items.push_back(AddItem(
+        GetType(), base::FilePath("/tmp/fake_" + base::NumberToString(i))));
+  }
 
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
   download_chips = test_api()->GetDownloadChips();
-  ASSERT_EQ(2u, download_chips.size());
-  EXPECT_EQ(item_3->id(),
-            HoldingSpaceItemView::Cast(download_chips[0])->item()->id());
-  EXPECT_EQ(item_1->id(),
-            HoldingSpaceItemView::Cast(download_chips[1])->item()->id());
+  ASSERT_EQ(GetMaxNumberOfDownloads(), download_chips.size());
+
+  // All downloads should be visible except for that which is associated with
+  // the partially initialized item at index == `1`.
+  for (int download_chip_index = 0, item_index = items.size() - 1;
+       item_index >= 0; --item_index) {
+    if (item_index != 1) {
+      HoldingSpaceItemView* download_chip =
+          HoldingSpaceItemView::Cast(download_chips.at(download_chip_index++));
+      EXPECT_EQ(download_chip->item()->id(), items[item_index]->id());
+    }
+  }
 
   // Fully initialize partially initialized item, and verify it gets added to
   // the section, in the order of addition, replacing the oldest item.
-  model()->InitializeOrRemoveItem(item_2->id(), GURL("filesystem:fake_2"));
+  model()->InitializeOrRemoveItem(items[1]->id(), GURL("filesystem:fake_2"));
 
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
   download_chips = test_api()->GetDownloadChips();
-  ASSERT_EQ(2u, download_chips.size());
-  EXPECT_EQ(item_3->id(),
-            HoldingSpaceItemView::Cast(download_chips[0])->item()->id());
-  EXPECT_EQ(item_2->id(),
-            HoldingSpaceItemView::Cast(download_chips[1])->item()->id());
+
+  for (int download_chip_index = 0, item_index = items.size() - 1;
+       item_index > 0; ++download_chip_index, --item_index) {
+    HoldingSpaceItemView* download_chip =
+        HoldingSpaceItemView::Cast(download_chips.at(download_chip_index));
+    EXPECT_EQ(download_chip->item()->id(), items[item_index]->id());
+  }
 
   // Remove the newest item, and verify the section gets updated.
-  model()->RemoveItem(item_3->id());
+  auto item_it = items.end() - 1;
+  model()->RemoveItem((*item_it)->id());
+  items.erase(item_it);
 
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
   download_chips = test_api()->GetDownloadChips();
-  ASSERT_EQ(2u, download_chips.size());
-  EXPECT_EQ(item_2->id(),
-            HoldingSpaceItemView::Cast(download_chips[0])->item()->id());
-  EXPECT_EQ(item_1->id(),
-            HoldingSpaceItemView::Cast(download_chips[1])->item()->id());
+  ASSERT_EQ(GetMaxNumberOfDownloads(), download_chips.size());
 
-  // Remove other items, and verify the recent files bubble gets hidden.
-  model()->RemoveItem(item_2->id());
+  for (int download_chip_index = 0, item_index = items.size() - 1;
+       item_index >= 0; ++download_chip_index, --item_index) {
+    HoldingSpaceItemView* download_chip =
+        HoldingSpaceItemView::Cast(download_chips.at(download_chip_index));
+    EXPECT_EQ(download_chip->item()->id(), items[item_index]->id());
+  }
 
-  EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
-  download_chips = test_api()->GetDownloadChips();
-  ASSERT_EQ(1u, download_chips.size());
-  EXPECT_EQ(item_1->id(),
-            HoldingSpaceItemView::Cast(download_chips[0])->item()->id());
+  // Remove other items and verify the recent files bubble gets hidden.
+  while (!items.empty()) {
+    model()->RemoveItem(items.front()->id());
+    items.erase(items.begin());
+  }
 
-  model()->RemoveItem(item_1->id());
   EXPECT_TRUE(test_api()->GetDownloadChips().empty());
-
   EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
 
   // Pinned bubble is showing "educational" info, and it should remain shown.
@@ -1016,7 +1058,7 @@
 
   // Add a number of initialized download items.
   std::deque<HoldingSpaceItem*> items;
-  for (size_t i = 0; i < kMaxDownloads; ++i) {
+  for (size_t i = 0; i < GetMaxNumberOfDownloads(); ++i) {
     items.push_back(AddItem(
         GetType(), base::FilePath("/tmp/fake_" + base::NumberToString(i))));
   }
@@ -1048,45 +1090,59 @@
 
   // Add partially initialized download item - verify it doesn't get shown in
   // the UI yet.
-  HoldingSpaceItem* item_1 =
-      AddPartiallyInitializedItem(GetType(), base::FilePath("/tmp/fake_1"));
+  std::vector<HoldingSpaceItem*> items;
+  items.push_back(
+      AddPartiallyInitializedItem(GetType(), base::FilePath("/tmp/fake_1")));
 
-  // Add two download items.
-  HoldingSpaceItem* item_2 = AddItem(GetType(), base::FilePath("/tmp/fake_2"));
-  HoldingSpaceItem* item_3 = AddItem(GetType(), base::FilePath("/tmp/fake_3"));
+  // Add download items until the section reaches capacity.
+  for (size_t i = 1; i < GetMaxNumberOfDownloads() + 1; ++i) {
+    items.push_back(AddItem(
+        GetType(), base::FilePath("/tmp/fake_" + base::NumberToString(i))));
+  }
+
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
   std::vector<views::View*> download_chips = test_api()->GetDownloadChips();
-  ASSERT_EQ(2u, download_chips.size());
-  EXPECT_EQ(item_3->id(),
-            HoldingSpaceItemView::Cast(download_chips[0])->item()->id());
-  EXPECT_EQ(item_2->id(),
-            HoldingSpaceItemView::Cast(download_chips[1])->item()->id());
+  ASSERT_EQ(GetMaxNumberOfDownloads(), download_chips.size());
+
+  for (size_t download_chip_index = 0, item_index = items.size() - 1;
+       item_index > 0; ++download_chip_index, --item_index) {
+    HoldingSpaceItemView* download_chip =
+        HoldingSpaceItemView::Cast(download_chips.at(download_chip_index));
+    EXPECT_EQ(download_chip->item()->id(), items[item_index]->id());
+  }
 
   // Fully initialize partially initialized item, and verify it's not added to
   // the section.
-  model()->InitializeOrRemoveItem(item_1->id(), GURL("filesystem:fake_1"));
+  model()->InitializeOrRemoveItem(items[0]->id(), GURL("filesystem:fake_1"));
 
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
   download_chips = test_api()->GetDownloadChips();
-  ASSERT_EQ(2u, download_chips.size());
-  EXPECT_EQ(item_3->id(),
-            HoldingSpaceItemView::Cast(download_chips[0])->item()->id());
-  EXPECT_EQ(item_2->id(),
-            HoldingSpaceItemView::Cast(download_chips[1])->item()->id());
+  ASSERT_EQ(GetMaxNumberOfDownloads(), download_chips.size());
+
+  for (size_t download_chip_index = 0, item_index = items.size() - 1;
+       item_index > 0; ++download_chip_index, --item_index) {
+    HoldingSpaceItemView* download_chip =
+        HoldingSpaceItemView::Cast(download_chips.at(download_chip_index));
+    EXPECT_EQ(download_chip->item()->id(), items[item_index]->id());
+  }
 
   // Remove the oldest item, and verify the section doesn't get updated.
-  model()->RemoveItem(item_1->id());
+  model()->RemoveItem(items.front()->id());
+  items.erase(items.begin());
 
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
   download_chips = test_api()->GetDownloadChips();
-  ASSERT_EQ(2u, download_chips.size());
-  EXPECT_EQ(item_3->id(),
-            HoldingSpaceItemView::Cast(download_chips[0])->item()->id());
-  EXPECT_EQ(item_2->id(),
-            HoldingSpaceItemView::Cast(download_chips[1])->item()->id());
+  ASSERT_EQ(GetMaxNumberOfDownloads(), download_chips.size());
+
+  for (int download_chip_index = 0, item_index = items.size() - 1;
+       item_index >= 0; ++download_chip_index, --item_index) {
+    HoldingSpaceItemView* download_chip =
+        HoldingSpaceItemView::Cast(download_chips.at(download_chip_index));
+    EXPECT_EQ(download_chip->item()->id(), items[item_index]->id());
+  }
 }
 
 // Tests that a partially initialized download item does not get shown if a full
diff --git a/ash/system/holding_space/holding_space_view_builder.h b/ash/system/holding_space/holding_space_view_builder.h
deleted file mode 100644
index 394fe3e..0000000
--- a/ash/system/holding_space/holding_space_view_builder.h
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_VIEW_BUILDER_H_
-#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_VIEW_BUILDER_H_
-
-#include "memory"
-#include "vector"
-
-#include "base/callback.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "ui/gfx/geometry/size.h"
-#include "ui/views/view.h"
-
-namespace views {
-class LayoutManager;
-}  // namespace views
-
-namespace ash {
-
-// A class which facilitates building of a view hierarchy. It is designed to be
-// used similarly to (and conjunction with) `views::Builder` but differs in that
-// it uses `std::unique_ptr<ViewType>` as its underlying storage mechanism
-// rather than wrapped `views::Builder<ViewType>` references. This allows it to
-// interop with both builders as well as traditionally constructed views.
-// NOTE: The intention is to remove this class once changes have been made at
-// the views framework level to support improved child lifetime management in
-// `views::Builder>. That effort is being tracked in https://crbug.com/1218115.
-template <typename ViewType>
-class HoldingSpaceViewBuilder {
- public:
-  // Constructs an instance from a `builder`.
-  template <typename BuilderType>
-  HoldingSpaceViewBuilder(BuilderType& builder)
-      : HoldingSpaceViewBuilder(builder.Build()) {}
-
-  // Constructs an instance from an owned `root_view`.
-  explicit HoldingSpaceViewBuilder(std::unique_ptr<ViewType> root_view)
-      : owned_root_view_(std::move(root_view)),
-        root_view_(owned_root_view_.get()) {
-    DCHECK(owned_root_view_);
-    DCHECK(root_view_);
-  }
-
-  // Constructs an instance from an unowned `root_view`.
-  explicit HoldingSpaceViewBuilder(ViewType* root_view)
-      : root_view_(root_view) {
-    DCHECK(root_view_);
-  }
-
-  HoldingSpaceViewBuilder(const HoldingSpaceViewBuilder&) = delete;
-  HoldingSpaceViewBuilder& operator=(const HoldingSpaceViewBuilder&) = delete;
-  ~HoldingSpaceViewBuilder() = default;
-
-  // Builds and returns an `owned_root_view_`.
-  std::unique_ptr<ViewType> Build() {
-    DCHECK(owned_root_view_);
-    BuildChildren();
-    return std::move(owned_root_view_);
-  }
-
-  // Builds children for a potentially unowned `root_view_`.
-  void BuildChildren() {
-    for (auto& child : children_)
-      root_view_->AddChildView(std::move(child));
-    children_.clear();
-  }
-
-  // Adds a pending child to be added to `root_view_` at `Build*()` time,
-  // returning a reference to `this` as a convenience.
-  template <typename BuilderType>
-  HoldingSpaceViewBuilder<ViewType>& AddChild(BuilderType& builder) {
-    return AddChild(builder.Build());
-  }
-
-  // Adds a pending `child` to be added to `root_view_` at `Build*()` time,
-  // returning a reference to `this` as a convenience.
-  HoldingSpaceViewBuilder<ViewType>& AddChild(
-      std::unique_ptr<views::View> child) {
-    children_.push_back(std::move(child));
-    return *this;
-  }
-
-  // Adds a pending `child` to be added to `root_view_` at `Build*()` time if
-  // the specified `condition` is `true`, returning a reference to `this` as a
-  // convenience.
-  HoldingSpaceViewBuilder<ViewType>& AddChildIf(
-      bool condition,
-      base::OnceCallback<std::unique_ptr<views::View>()> callback) {
-    return condition ? AddChild(std::move(callback).Run()) : *this;
-  }
-
-  // Copies the address of `root_view_` to the specified `address_ptr`,
-  // returning a reference to `this` as a convenience.
-  HoldingSpaceViewBuilder<ViewType>& CopyAddressTo(ViewType** address_ptr) {
-    *address_ptr = root_view_;
-    return *this;
-  }
-
-  // Sets the `id` for `root_view_`, returning a reference to `this` as a
-  // convenience.
-  HoldingSpaceViewBuilder<ViewType>& SetID(int id) {
-    root_view_->SetID(id);
-    return *this;
-  }
-
-  // Sets the `layout_manager` for `root_view_`, returning a reference to `this`
-  // as a convenience.
-  HoldingSpaceViewBuilder<ViewType>& SetLayoutManager(
-      std::unique_ptr<views::LayoutManager> layout_manager) {
-    root_view_->SetLayoutManager(std::move(layout_manager));
-    return *this;
-  }
-
-  // Sets the `preferred_size` for `root_view`, returning a reference to `this`
-  // as a convenience.
-  HoldingSpaceViewBuilder<ViewType>& SetPreferredSize(
-      const gfx::Size& preferred_size) {
-    root_view_->SetPreferredSize(preferred_size);
-    return *this;
-  }
-
- private:
-  std::unique_ptr<ViewType> owned_root_view_;
-  ViewType* const root_view_;
-  std::vector<std::unique_ptr<views::View>> children_;
-};
-
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_VIEW_BUILDER_H_
diff --git a/ash/system/message_center/ash_message_popup_collection_unittest.cc b/ash/system/message_center/ash_message_popup_collection_unittest.cc
index 9e5f5e6..3d91a37e 100644
--- a/ash/system/message_center/ash_message_popup_collection_unittest.cc
+++ b/ash/system/message_center/ash_message_popup_collection_unittest.cc
@@ -406,12 +406,12 @@
   EXPECT_NE(baseline_with_visible_shelf, baseline_with_hidden_shelf);
 
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   const int baseline_in_overview = popup_collection()->GetBaseline();
   EXPECT_EQ(baseline_in_overview, baseline_with_visible_shelf);
 
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_FALSE(overview_controller->InOverviewSession());
   const int baseline_no_overview = popup_collection()->GetBaseline();
   EXPECT_EQ(baseline_no_overview, baseline_with_hidden_shelf);
diff --git a/ash/system/overview/overview_button_tray.cc b/ash/system/overview/overview_button_tray.cc
index fdf3fb7e..6a92649 100644
--- a/ash/system/overview/overview_button_tray.cc
+++ b/ash/system/overview/overview_button_tray.cc
@@ -146,9 +146,9 @@
                                : absl::make_optional(event.time_stamp());
 
   if (overview_controller->InOverviewSession())
-    overview_controller->EndOverview();
+    overview_controller->EndOverview(OverviewEndAction::kOverviewButton);
   else
-    overview_controller->StartOverview();
+    overview_controller->StartOverview(OverviewStartAction::kOverviewButton);
   Shell::Get()->metrics()->RecordUserMetricsAction(UMA_TRAY_OVERVIEW);
 
   // The return value doesn't matter here. OnOverviewModeStarting() and
diff --git a/ash/system/overview/overview_button_tray_unittest.cc b/ash/system/overview/overview_button_tray_unittest.cc
index 5ba75c7..cffde86 100644
--- a/ash/system/overview/overview_button_tray_unittest.cc
+++ b/ash/system/overview/overview_button_tray_unittest.cc
@@ -312,11 +312,11 @@
   std::unique_ptr<aura::Window> window(
       CreateTestWindowInShellWithBounds(gfx::Rect(5, 5, 20, 20)));
 
-  EXPECT_TRUE(Shell::Get()->overview_controller()->StartOverview());
+  EXPECT_TRUE(EnterOverview());
   EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
   EXPECT_TRUE(GetTray()->is_active());
 
-  EXPECT_TRUE(Shell::Get()->overview_controller()->EndOverview());
+  EXPECT_TRUE(ExitOverview());
   EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
   EXPECT_FALSE(GetTray()->is_active());
 }
@@ -399,7 +399,7 @@
 
   // Enter splitview mode. Snap |window1| to the left, this will be the default
   // splitview window.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT);
   split_view_controller()->SnapWindow(window2.get(),
                                       SplitViewController::RIGHT);
@@ -615,7 +615,7 @@
   // Create a window to show in overview.
   std::unique_ptr<aura::Window> window(
       CreateTestWindowInShellWithBounds(gfx::Rect(5, 5, 20, 20)));
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
   EXPECT_FALSE(GetTray()->GetVisible());
 
diff --git a/ash/system/power/power_button_menu_screen_view.cc b/ash/system/power/power_button_menu_screen_view.cc
index 479d7be..3c2ec589 100644
--- a/ash/system/power/power_button_menu_screen_view.cc
+++ b/ash/system/power/power_button_menu_screen_view.cc
@@ -129,14 +129,10 @@
   power_button_menu_view_ = new PowerButtonMenuView(power_button_position_);
   AddChildView(power_button_menu_view_);
 
-  display::Screen::GetScreen()->AddObserver(this);
-
   AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
 }
 
-PowerButtonMenuScreenView::~PowerButtonMenuScreenView() {
-  display::Screen::GetScreen()->RemoveObserver(this);
-}
+PowerButtonMenuScreenView::~PowerButtonMenuScreenView() = default;
 
 void PowerButtonMenuScreenView::ScheduleShowHideAnimation(bool show) {
   power_button_screen_background_shield_->ScheduleShowHideAnimation(show);
diff --git a/ash/system/power/power_button_menu_screen_view.h b/ash/system/power/power_button_menu_screen_view.h
index 7b6a6ca..c6f4b07 100644
--- a/ash/system/power/power_button_menu_screen_view.h
+++ b/ash/system/power/power_button_menu_screen_view.h
@@ -94,6 +94,8 @@
 
   // The origin of the menu bounds in different screen orientations.
   std::unordered_map<OrientationLockType, gfx::Point> menu_bounds_origins_;
+
+  display::ScopedDisplayObserver display_observer_{this};
 };
 
 }  // namespace ash
diff --git a/ash/system/toast/toast_overlay.cc b/ash/system/toast/toast_overlay.cc
index e08b499..7d474da 100644
--- a/ash/system/toast/toast_overlay.cc
+++ b/ash/system/toast/toast_overlay.cc
@@ -108,13 +108,9 @@
 //  ToastDisplayObserver
 class ToastOverlay::ToastDisplayObserver : public display::DisplayObserver {
  public:
-  ToastDisplayObserver(ToastOverlay* overlay) : overlay_(overlay) {
-    display::Screen::GetScreen()->AddObserver(this);
-  }
+  ToastDisplayObserver(ToastOverlay* overlay) : overlay_(overlay) {}
 
-  ~ToastDisplayObserver() override {
-    display::Screen::GetScreen()->RemoveObserver(this);
-  }
+  ~ToastDisplayObserver() override {}
 
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t changed_metrics) override {
@@ -123,6 +119,9 @@
 
  private:
   ToastOverlay* const overlay_;
+
+  display::ScopedDisplayObserver display_observer_{this};
+
   DISALLOW_COPY_AND_ASSIGN(ToastDisplayObserver);
 };
 
diff --git a/ash/system/unified/unified_system_tray_model.cc b/ash/system/unified/unified_system_tray_model.cc
index de1806c..f8a3ebf 100644
--- a/ash/system/unified/unified_system_tray_model.cc
+++ b/ash/system/unified/unified_system_tray_model.cc
@@ -68,6 +68,8 @@
 
   UnifiedSystemTrayModel* const owner_;
 
+  display::ScopedDisplayObserver display_observer_{this};
+
   // Keep track of current system tray size.
   UnifiedSystemTrayModel::SystemTrayButtonSize system_tray_size_;
 };
@@ -113,13 +115,11 @@
 UnifiedSystemTrayModel::SizeObserver::SizeObserver(
     UnifiedSystemTrayModel* owner)
     : owner_(owner) {
-  display::Screen::GetScreen()->AddObserver(this);
   Shell::Get()->AddShellObserver(this);
   system_tray_size_ = owner_->GetSystemTrayButtonSize();
 }
 
 UnifiedSystemTrayModel::SizeObserver::~SizeObserver() {
-  display::Screen::GetScreen()->RemoveObserver(this);
   Shell::Get()->RemoveShellObserver(this);
 }
 
diff --git a/ash/system/unified/user_chooser_detailed_view_controller_unittest.cc b/ash/system/unified/user_chooser_detailed_view_controller_unittest.cc
index 55bdd96..872d7491 100644
--- a/ash/system/unified/user_chooser_detailed_view_controller_unittest.cc
+++ b/ash/system/unified/user_chooser_detailed_view_controller_unittest.cc
@@ -55,7 +55,7 @@
 TEST_F(UserChooserDetailedViewControllerTest,
        ShowMultiProfileLoginWithOverview) {
   // Enter ovewview mode.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   // Show system tray.
@@ -84,7 +84,7 @@
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
 
   // Enter overview mode.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   // Show system tray.
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index 0b9e227..1879d92 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -32,6 +32,7 @@
 #include "ash/test_screenshot_delegate.h"
 #include "ash/test_shell_delegate.h"
 #include "ash/utility/screenshot_controller.h"
+#include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_positioner.h"
 #include "ash/wm/work_area_insets.h"
@@ -473,6 +474,16 @@
   event_generator->ClickLeftButton();
 }
 
+bool AshTestBase::EnterOverview(OverviewEnterExitType type) {
+  return Shell::Get()->overview_controller()->StartOverview(
+      OverviewStartAction::kTests, type);
+}
+
+bool AshTestBase::ExitOverview(OverviewEnterExitType type) {
+  return Shell::Get()->overview_controller()->EndOverview(
+      OverviewEndAction::kTests, type);
+}
+
 void AshTestBase::SwapPrimaryDisplay() {
   if (display::Screen::GetScreen()->GetNumDisplays() <= 1)
     return;
diff --git a/ash/test/ash_test_base.h b/ash/test/ash_test_base.h
index e580e76..f46c24cb 100644
--- a/ash/test/ash_test_base.h
+++ b/ash/test/ash_test_base.h
@@ -15,6 +15,7 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/test_session_controller_client.h"
 #include "ash/wm/desks/desks_util.h"
+#include "ash/wm/overview/overview_types.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/test/task_environment.h"
@@ -193,6 +194,12 @@
   void SimulateMouseClickAt(ui::test::EventGenerator* event_generator,
                             const views::View* target_view);
 
+  // Enters/Exits overview mode with the given animation type `type`.
+  bool EnterOverview(
+      OverviewEnterExitType type = OverviewEnterExitType::kNormal);
+  bool ExitOverview(
+      OverviewEnterExitType type = OverviewEnterExitType::kNormal);
+
  protected:
   enum UserSessionBlockReason {
     FIRST_BLOCK_REASON,
diff --git a/ash/touch/touch_observer_hud.cc b/ash/touch/touch_observer_hud.cc
index 5796c4f7..5608d2a 100644
--- a/ash/touch/touch_observer_hud.cc
+++ b/ash/touch/touch_observer_hud.cc
@@ -10,7 +10,6 @@
 #include "ash/shell.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/display/display.h"
-#include "ui/display/screen.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/views/widget/widget.h"
@@ -47,7 +46,6 @@
   widget_->AddObserver(this);
 
   // Observe changes in display size and mode to update touch HUD.
-  display::Screen::GetScreen()->AddObserver(this);
   Shell::Get()->display_configurator()->AddObserver(this);
   Shell::Get()->window_tree_host_manager()->AddObserver(this);
   root_window_->AddPreTargetHandler(this);
@@ -56,7 +54,6 @@
 TouchObserverHud::~TouchObserverHud() {
   Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
   Shell::Get()->display_configurator()->RemoveObserver(this);
-  display::Screen::GetScreen()->RemoveObserver(this);
 
   widget_->RemoveObserver(this);
   CHECK(!views::WidgetObserver::IsInObserverList());
diff --git a/ash/touch/touch_observer_hud.h b/ash/touch/touch_observer_hud.h
index 5b4ba05..f4e1c1f 100644
--- a/ash/touch/touch_observer_hud.h
+++ b/ash/touch/touch_observer_hud.h
@@ -84,6 +84,8 @@
 
   views::Widget* widget_;
 
+  display::ScopedDisplayObserver display_observer_{this};
+
   DISALLOW_COPY_AND_ASSIGN(TouchObserverHud);
 };
 
diff --git a/ash/utility/occlusion_tracker_pauser.cc b/ash/utility/occlusion_tracker_pauser.cc
index 35a3636..944740f6 100644
--- a/ash/utility/occlusion_tracker_pauser.cc
+++ b/ash/utility/occlusion_tracker_pauser.cc
@@ -4,7 +4,6 @@
 
 #include "ash/utility/occlusion_tracker_pauser.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
 #include "ash/shell.h"
 #include "base/bind.h"
 #include "ui/aura/window_tree_host.h"
@@ -18,20 +17,6 @@
 
 void OcclusionTrackerPauser::PauseUntilAnimationsEnd(
     const base::TimeDelta& extra_pause_duration) {
-  auto* a11y_controller = Shell::Get()->accessibility_controller();
-  /*
-   * Ddo not pause the occlusion tracker when accessibility feature is enabled
-   * because it may add a permnent CompositorAnimationObserver.
-   * TODO(crbug.com/1222698): Remove this if when the accessibility layer
-   * migrates to use LayerAnimationElement.
-   */
-  for (int type = AccessibilityControllerImpl::kAutoclick;
-       type < AccessibilityControllerImpl::kFeatureCount; type++) {
-    auto casted_type =
-        static_cast<AccessibilityControllerImpl::FeatureType>(type);
-    if (a11y_controller->GetFeature(casted_type).enabled())
-      return;
-  }
   for (auto* root : Shell::GetAllRootWindows())
     Pause(root->GetHost()->compositor(), extra_pause_duration);
 }
diff --git a/ash/utility/screenshot_controller.cc b/ash/utility/screenshot_controller.cc
index 739b991b4..5e632ab 100644
--- a/ash/utility/screenshot_controller.cc
+++ b/ash/utility/screenshot_controller.cc
@@ -388,7 +388,7 @@
   in_screenshot_session_ = true;
   mode_ = WINDOW;
 
-  display::Screen::GetScreen()->AddObserver(this);
+  display_observer_.emplace(this);
   for (aura::Window* root : Shell::GetAllRootWindows()) {
     layers_[root] = std::make_unique<ScreenshotLayer>(
         this,
@@ -411,7 +411,7 @@
     return;
   in_screenshot_session_ = true;
   mode_ = PARTIAL;
-  display::Screen::GetScreen()->AddObserver(this);
+  display_observer_.emplace(this);
   for (aura::Window* root : Shell::GetAllRootWindows()) {
     layers_[root] = std::make_unique<ScreenshotLayer>(
         this,
@@ -441,7 +441,7 @@
   root_window_ = nullptr;
   SetSelectedWindow(nullptr);
   in_screenshot_session_ = false;
-  display::Screen::GetScreen()->RemoveObserver(this);
+  display_observer_.reset();
   layers_.clear();
   cursor_setter_.reset();
   EnableMouseWarp(true);
diff --git a/ash/utility/screenshot_controller.h b/ash/utility/screenshot_controller.h
index 3ba1019..89708d70 100644
--- a/ash/utility/screenshot_controller.h
+++ b/ash/utility/screenshot_controller.h
@@ -15,6 +15,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/timer/timer.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/aura/window_observer.h"
 #include "ui/display/display_observer.h"
 #include "ui/events/event_handler.h"
@@ -138,6 +139,9 @@
   // The object to specify the crosshair cursor.
   std::unique_ptr<ScopedCursorSetter> cursor_setter_;
 
+  // The display observer while in a screenshot session.
+  absl::optional<display::ScopedDisplayObserver> display_observer_;
+
   // True while taking a partial or window screen.
   bool in_screenshot_session_ = false;
 
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index 2557e5e..d6f1e6f 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -2187,7 +2187,7 @@
       /*preview_mode=*/false, /*always_on_top=*/false);
   TestWallpaperControllerObserver observer(controller_);
 
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
 
   EXPECT_FALSE(controller_->IsWallpaperBlurredForLockState());
   EXPECT_EQ(0, observer.blur_changed_count());
@@ -2370,7 +2370,7 @@
   // the user wallpaper info remains unchanged, and enters overview mode
   // properly.
   ClearWallpaperCount();
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_NE(kWallpaperColor, GetWallpaperColor());
diff --git a/ash/webui/common/resources/navigation_icons.html b/ash/webui/common/resources/navigation_icons.html
index 58dd798..aaba4a9 100644
--- a/ash/webui/common/resources/navigation_icons.html
+++ b/ash/webui/common/resources/navigation_icons.html
@@ -3,6 +3,9 @@
     <defs>
       <!-- TODO(jimmyxgong): Replace this stub icon with actual icons. -->
       <g id="laptop-chromebook" viewBox="0 0 24 24"><path d="M22 18V3H2v15H0v2h24v-2h-2zm-8 0h-4v-1h4v1zm6-3H4V5h16v10z"></path></g>
+      <g id="ethernet" viewBox="0 0 20 20">
+        <path fill-rule="evenodd" clip-rule="evenodd" d="M14.0909 5L12.9373 6.175L16.6845 10L12.9373 13.825L14.0909 15L19 10L14.0909 5ZM7.06273 6.175L5.90909 5L1 10L5.90909 15L7.06273 13.825L3.31545 10L7.06273 6.175ZM7.05457 11C7.59682 11 8.03639 10.5523 8.03639 10C8.03639 9.44772 7.59682 9 7.05457 9C6.51233 9 6.07275 9.44772 6.07275 10C6.07275 10.5523 6.51233 11 7.05457 11ZM10 11C10.5422 11 10.9818 10.5523 10.9818 10C10.9818 9.44772 10.5422 9 10 9C9.45776 9 9.01819 9.44772 9.01819 10C9.01819 10.5523 9.45776 11 10 11ZM12.9454 11C13.4877 11 13.9273 10.5523 13.9273 10C13.9273 9.44772 13.4877 9 12.9454 9C12.4032 9 11.9636 9.44772 11.9636 10C11.9636 10.5523 12.4032 11 12.9454 11Z" />
+      </g>
     </defs>
   </svg>
 </iron-iconset-svg>
diff --git a/ash/webui/diagnostics_ui/diagnostics_ui.cc b/ash/webui/diagnostics_ui/diagnostics_ui.cc
index 53d42865..dca0aa16 100644
--- a/ash/webui/diagnostics_ui/diagnostics_ui.cc
+++ b/ash/webui/diagnostics_ui/diagnostics_ui.cc
@@ -54,6 +54,7 @@
       {"boardAndVersionInfo", IDS_DIAGNOSTICS_DEVICE_INFO_TEXT},
       {"captivePortalRoutineText", IDS_NETWORK_DIAGNOSTICS_CAPTIVE_PORTAL},
       {"chargeTestResultText", IDS_CHARGE_TEST_RESULT},
+      {"connectivityText", IDS_DIAGNOSTICS_CONNECTIVITY},
       {"cpuBannerMessage", IDS_DIAGNOSTICS_CPU_BANNER_MESSAGE},
       {"cpuCacheRoutineText", IDS_DIAGNOSTICS_CPU_CACHE_ROUTINE_TEXT},
       {"cpuChipText", IDS_DIAGNOSTICS_CPU_CHIP_TEXT},
@@ -98,6 +99,7 @@
       {"memoryRoutineText", IDS_DIAGNOSTICS_MEMORY_ROUTINE_TEXT},
       {"memoryTitle", IDS_DIAGNOSTICS_MEMORY_TITLE},
       {"noEthernet", IDS_DIAGNOSTICS_NO_ETHERNET},
+      {"overviewText", IDS_DIAGNOSTICS_OVERVIEW},
       {"percentageLabel", IDS_DIAGNOSTICS_PERCENTAGE_LABEL},
       {"remainingCharge", IDS_DIAGNOSTICS_REMAINING_CHARGE_LABEL},
       {"routineEntryText", IDS_DIANOSTICS_ROUTINE_ENTRY_TEXT},
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_app.js b/ash/webui/diagnostics_ui/resources/diagnostics_app.js
index bb85bbd18d..a6a3ef1 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_app.js
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_app.js
@@ -53,9 +53,15 @@
   /** @override */
   attached() {
     if (this.showNavPanel_) {
-      this.$$('#navigationPanel').addSelector('System', 'system-page');
+      this.$$('#navigationPanel')
+          .addSelector(
+              loadTimeData.getString('overviewText'), 'system-page',
+              'navigation-selector:laptop-chromebook');
       if (this.isNetworkingEnabled_) {
-        this.$$('#navigationPanel').addSelector('Connectivity', 'network-list');
+        this.$$('#navigationPanel')
+            .addSelector(
+                loadTimeData.getString('connectivityText'), 'network-list',
+                'navigation-selector:ethernet');
       }
       if (this.isInputEnabled_) {
         this.$$('#navigationPanel').addSelector('Input', 'input-list');
diff --git a/ash/wm/desks/desk.cc b/ash/wm/desks/desk.cc
index fa23a99..8516ce9 100644
--- a/ash/wm/desks/desk.cc
+++ b/ash/wm/desks/desk.cc
@@ -28,7 +28,6 @@
 #include "base/containers/cxx20_erase.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_functions.h"
-#include "base/no_destructor.h"
 #include "base/strings/stringprintf.h"
 #include "chromeos/ui/base/window_properties.h"
 #include "ui/aura/client/aura_constants.h"
@@ -132,12 +131,13 @@
 
 // Returns Jan 1, 2010 00:00:00 as a base::Time object in the local timezone.
 base::Time GetLocalEpoch() {
-  static base::NoDestructor<base::Time> local_epoch;
-  if (local_epoch->is_null()) {
+  static const base::Time local_epoch = [] {
+    base::Time local_epoch;
     ignore_result(base::Time::FromLocalExploded({2010, 1, 5, 1, 0, 0, 0, 0},
-                                                local_epoch.get()));
-  }
-  return *local_epoch;
+                                                &local_epoch));
+    return local_epoch;
+  }();
+  return local_epoch;
 }
 
 // Used to temporarily turn off the automatic window positioning while windows
diff --git a/ash/wm/desks/desk_animation_impl.cc b/ash/wm/desks/desk_animation_impl.cc
index 132b2ac6..a999422 100644
--- a/ash/wm/desks/desk_animation_impl.cc
+++ b/ash/wm/desks/desk_animation_impl.cc
@@ -253,6 +253,7 @@
     // ending desk screenshot. This makes sure that the ending desk
     // screenshot will only show the windows in that desk, not overview stuff.
     Shell::Get()->overview_controller()->EndOverview(
+        OverviewEndAction::kDeskActivation,
         OverviewEnterExitType::kImmediateExit);
   }
   SplitViewController* split_view_controller =
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index 39e5592..ab29dc70 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -549,6 +549,7 @@
       // overview shutdown animation is complete. See https://crbug.com/1001586.
       const bool immediate_exit = source == DesksSwitchSource::kUserSwitch;
       overview_controller->EndOverview(
+          OverviewEndAction::kDeskActivation,
           immediate_exit ? OverviewEnterExitType::kImmediateExit
                          : OverviewEnterExitType::kNormal);
     }
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 30e0ee2..c79e9e9 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -405,7 +405,7 @@
   auto* controller = DesksController::Get();
 
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
 
   const auto* overview_grid =
@@ -462,9 +462,9 @@
   // Exit overview mode and re-enter. Since we have more than one pre-existing
   // desks, their mini_views should be created upon construction of the desks
   // bar.
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_FALSE(overview_controller->InOverviewSession());
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
 
   // Get the new grid and the new desk_bar_view.
@@ -484,9 +484,8 @@
 TEST_F(DesksTest, RemoveDeskWithEmptyName) {
   auto* controller = DesksController::Get();
 
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
 
@@ -581,9 +580,8 @@
 // Test that gesture taps do not reset the button state to normal when the
 // button is disabled. https://crbug.com/1084241.
 TEST_F(DesksTest, GestureTapOnNewDeskButton) {
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   const auto* overview_grid =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
@@ -997,7 +995,7 @@
   // desks mini views, and there are exactly two windows in the overview mode
   // grid.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   const auto* overview_grid =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
@@ -1034,14 +1032,14 @@
   // showing exactly one window.
   auto win2 = CreateAppWindow(gfx::Rect(50, 50, 200, 200));
   wm::ActivateWindow(win2.get());
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   EXPECT_EQ(1u, overview_grid->window_list().size());
 
   // When exiting overview mode without changing desks, the focus should be
   // restored to the same window.
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_FALSE(overview_controller->InOverviewSession());
   // Run a loop since the overview session is destroyed async and until that
   // happens, focus will be on the dummy "OverviewModeFocusedWidget".
@@ -1064,7 +1062,7 @@
 
   // Enter overview mode.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
 
   auto roots = Shell::GetAllRootWindows();
@@ -1117,7 +1115,7 @@
   ActivateDesk(desk_4);
   auto* overview_controller = Shell::Get()->overview_controller();
   auto win3 = CreateAppWindow(gfx::Rect(50, 50, 200, 200));
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   const auto* overview_grid =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
@@ -1177,7 +1175,7 @@
 
   // Exiting overview mode should not cause any mini_views refreshes, since the
   // destroyed overview-specific windows do not show up in the mini_view.
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_FALSE(overview_controller->InOverviewSession());
   EXPECT_EQ(1, desk_4_observer.notify_counts());
   desk_4->RemoveObserver(&desk_4_observer);
@@ -1223,7 +1221,7 @@
 
   // Enter overview mode, and remove desk_2 from its mini-view close button.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   const auto* overview_grid =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
@@ -1286,7 +1284,7 @@
 
   // Exiting overview mode should not cause any mini_views refreshes, since the
   // destroyed overview-specific windows do not show up in the mini_view.
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_FALSE(overview_controller->InOverviewSession());
   EXPECT_EQ(1, desk_1_observer.notify_counts());
   desk_1->RemoveObserver(&desk_1_observer);
@@ -1303,7 +1301,7 @@
   // Enter overview mode, and click on `desk_1`'s mini_view, and expect that
   // overview mode exits since this is the already active desk.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   const auto* overview_grid =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
@@ -1415,7 +1413,7 @@
   EXPECT_TRUE(shadow->layer()->GetTargetVisibility());
 
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   EXPECT_EQ(2u, overview_grid->size());
@@ -1501,7 +1499,7 @@
   ASSERT_TRUE(window_state->IsMinimized());
 
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   auto* overview_session = overview_controller->overview_session();
@@ -1542,7 +1540,7 @@
   ASSERT_EQ(2u, DesksController::Get()->desks().size());
   auto win = CreateAppWindow(gfx::Rect(0, 0, 250, 100));
   auto* overview_controller = Shell::Get()->overview_controller();
-  ASSERT_TRUE(overview_controller->StartOverview());
+  ASSERT_TRUE(EnterOverview());
   auto* overview_session = overview_controller->overview_session();
   DragItemToPoint(overview_session->GetOverviewItemForWindow(win.get()),
                   GetOverviewGridForRoot(Shell::GetPrimaryRootWindow())
@@ -1567,7 +1565,7 @@
   EXPECT_EQ(window.get(), window_util::GetActiveWindow());
 
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   const auto* overview_grid =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
@@ -1706,9 +1704,8 @@
   // The widgets created by overview mode, whose windows are added to the active
   // desk's container, should never result in mini_views updates since they're
   // not mirrored there at all.
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   EXPECT_EQ(0, desk_1_observer.notify_counts());
   EXPECT_EQ(0, desk_2_observer.notify_counts());
@@ -1798,8 +1795,7 @@
   NewDesk();
   ASSERT_EQ(2u, controller->desks().size());
 
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   const auto* overview_grid =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   const auto* desks_bar_view = overview_grid->desks_bar_view();
@@ -1856,7 +1852,7 @@
 
   auto win = CreateAppWindow(gfx::Rect(0, 0, 250, 100));
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   auto* overview_session = overview_controller->overview_session();
   auto* overview_item = overview_session->GetOverviewItemForWindow(win.get());
@@ -1899,7 +1895,7 @@
 
   auto win = CreateAppWindow(gfx::Rect(0, 0, 250, 100));
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   auto* overview_session = overview_controller->overview_session();
   auto* overview_item = overview_session->GetOverviewItemForWindow(win.get());
@@ -1978,8 +1974,7 @@
     NewDesk();
     controller_ = DesksController::Get();
 
-    auto* overview_controller = Shell::Get()->overview_controller();
-    overview_controller->StartOverview();
+    EnterOverview();
     overview_grid_ = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
     desks_bar_view_ = overview_grid_->desks_bar_view();
     ASSERT_TRUE(desks_bar_view_);
@@ -2028,8 +2023,7 @@
 
 TEST_F(DesksEditableNamesTest, NamesSetByUsersAreNotOverwritten) {
   ASSERT_EQ(2u, controller()->desks().size());
-  auto* overview_controller = Shell::Get()->overview_controller();
-  ASSERT_TRUE(overview_controller->InOverviewSession());
+  ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   // Change the name of the first desk. Adding/removing desks or
   // exiting/reentering overview should not cause changes to the desk's name.
@@ -2083,8 +2077,8 @@
   VerifyDesksRestoreData(GetPrimaryUserPrefService(),
                          {std::string("code"), std::string()});
 
-  overview_controller->EndOverview();
-  overview_controller->StartOverview();
+  ExitOverview();
+  EnterOverview();
   EXPECT_TRUE(desk_1->is_name_set_by_user());
   EXPECT_FALSE(desk_3->is_name_set_by_user());
   EXPECT_EQ(u"code", desk_1->name());
@@ -2159,8 +2153,7 @@
 
 TEST_F(DesksEditableNamesTest, MaxLength) {
   ASSERT_EQ(2u, controller()->desks().size());
-  auto* overview_controller = Shell::Get()->overview_controller();
-  ASSERT_TRUE(overview_controller->InOverviewSession());
+  ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   ClickOnDeskNameViewAtIndex(0);
   // Select all and delete.
@@ -2245,7 +2238,7 @@
   // Enter overview and expect that the backdrop is still present for desk_1 but
   // hidden.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   ASSERT_TRUE(desk_1_backdrop_controller->backdrop_window());
   EXPECT_FALSE(desk_1_backdrop_controller->backdrop_window()->IsVisible());
@@ -2276,7 +2269,7 @@
 
   // Exit overview, and expect that desk_2's backdrop remains hidden since the
   // desk is not activated yet.
-  overview_controller->EndOverview(OverviewEnterExitType::kImmediateExit);
+  ExitOverview(OverviewEnterExitType::kImmediateExit);
   EXPECT_FALSE(overview_controller->InOverviewSession());
   EXPECT_FALSE(desk_1_backdrop_controller->backdrop_window());
   ASSERT_TRUE(desk_2_backdrop_controller->backdrop_window());
@@ -2312,7 +2305,7 @@
   // Enter overview and expect that |desk_1| has a backdrop stacked under
   // |window| while desk_2 has none.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   ASSERT_TRUE(desk_1_backdrop_controller->backdrop_window());
   EXPECT_FALSE(desk_2_backdrop_controller->backdrop_window());
@@ -2375,7 +2368,7 @@
 
   // Enter overview and expect that the DesksBar widget won't be created.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   const auto* overview_grid =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
@@ -2391,10 +2384,10 @@
 
   // Exit overview and add a new desk, then re-enter overview. Expect that now
   // the desks bar is visible.
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_FALSE(overview_controller->InOverviewSession());
   NewDesk();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   desks_bar_view = overview_grid->desks_bar_view();
@@ -2495,7 +2488,7 @@
   // end, but TabletModeWindowManager should not maximize the snapped windows
   // and they should retain their snapped state.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   auto* desks_bar_view = overview_grid->desks_bar_view();
@@ -2520,7 +2513,7 @@
   split_view_controller()->SnapWindow(win4.get(), SplitViewController::RIGHT);
   EXPECT_EQ(win3.get(), split_view_controller()->left_window());
   EXPECT_EQ(win4.get(), split_view_controller()->right_window());
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   desks_bar_view = overview_grid->desks_bar_view();
@@ -2560,7 +2553,7 @@
   std::unique_ptr<aura::Window> win3(CreateTestWindowInShellWithDelegate(
       &win3_delegate, /*id=*/-1, gfx::Rect(big)));
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  EXPECT_TRUE(overview_controller->StartOverview());
+  EXPECT_TRUE(EnterOverview());
   split_view_controller()->SnapWindow(win1.get(), SplitViewController::LEFT);
   EXPECT_EQ(win1.get(), split_view_controller()->left_window());
   EXPECT_FALSE(split_view_controller()->CanSnapWindow(win2.get()));
@@ -2585,7 +2578,7 @@
   EXPECT_FALSE(overview_controller->InOverviewSession());
 
   // Switch back to |desk_1| and verify that split view is arranged as before.
-  EXPECT_TRUE(overview_controller->StartOverview());
+  EXPECT_TRUE(EnterOverview());
   ASSERT_TRUE(overview_controller->InOverviewSession());
   overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   desks_bar_view = overview_grid->desks_bar_view();
@@ -2746,7 +2739,7 @@
   EXPECT_EQ(win1.get(), window_util::GetActiveWindow());
 
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   auto* overview_session = overview_controller->overview_session();
 
@@ -2849,7 +2842,7 @@
   NewDesk();
   ASSERT_EQ(3u, controller->desks().size());
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   const auto* overview_grid =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
@@ -2975,7 +2968,7 @@
   EXPECT_EQ(window.get(), window_util::GetActiveWindow());
 
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
 
@@ -3025,7 +3018,7 @@
   auto win0 = CreateAppWindow(gfx::Rect(0, 0, 250, 100));
   auto win1 = CreateAppWindow(gfx::Rect(50, 50, 200, 200));
   auto* overview_controller = Shell::Get()->overview_controller();
-  ASSERT_TRUE(overview_controller->StartOverview());
+  ASSERT_TRUE(EnterOverview());
   auto* overview_session = overview_controller->overview_session();
   auto* generator = GetEventGenerator();
   DragItemToPoint(overview_session->GetOverviewItemForWindow(win0.get()),
@@ -3099,9 +3092,7 @@
   EXPECT_EQ(desk3->GetDeskContainerForRoot(Shell::GetPrimaryRootWindow()),
             win0->parent());
 
-  auto* overview_controller = Shell::Get()->overview_controller();
-  ASSERT_TRUE(overview_controller->StartOverview());
-
+  ASSERT_TRUE(EnterOverview());
   const gfx::Rect expected_clip = win0->layer()->GetTargetClipRect();
 
   // Remove |desk3|. |win0| is now a child of |desk2|.
@@ -3379,7 +3370,7 @@
 TEST_F(DesksMultiUserTest, SwitchingUsersEndsOverview) {
   SimulateUserLogin(GetUser1AccountId());
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  EXPECT_TRUE(overview_controller->StartOverview());
+  EXPECT_TRUE(EnterOverview());
   EXPECT_TRUE(overview_controller->InOverviewSession());
   SwitchActiveUser(GetUser2AccountId());
   EXPECT_FALSE(overview_controller->InOverviewSession());
@@ -3677,7 +3668,7 @@
 
   // Using the accelerator doesn't result in exiting overview.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   SendAccelerator(ui::VKEY_OEM_MINUS, flags);
   ASSERT_EQ(1u, controller->desks().size());
@@ -3804,7 +3795,7 @@
   EXPECT_EQ(win0.get(), window_util::GetActiveWindow());
 
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   const int flags = ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN;
   // In overview, while no window is highlighted, nothing should happen.
@@ -4114,9 +4105,8 @@
   auto* controller = DesksController::Get();
 
   // Start overview.
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   // Hover over the new desk button.
   const auto* overview_grid =
@@ -4157,9 +4147,8 @@
   UpdateDisplay("800x800,800x800");
 
   // Start overview.
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   // Retrieve the desks bar view for each root window.
   auto root_windows = Shell::GetAllRootWindows();
@@ -4188,8 +4177,8 @@
             desk_name_view_2->GetText());
 
   // Restart overview to reset the zero state.
-  overview_controller->EndOverview();
-  overview_controller->StartOverview();
+  ExitOverview();
+  EnterOverview();
   desks_bar_view_1 = GetOverviewGridForRoot(root_windows[0])->desks_bar_view();
   desks_bar_view_2 = GetOverviewGridForRoot(root_windows[1])->desks_bar_view();
   ASSERT_TRUE(desks_bar_view_1->IsZeroState());
@@ -4237,7 +4226,7 @@
 
   // Start overview.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
 
   // Focus on a `DeskNameView`.
@@ -4259,9 +4248,8 @@
 
 TEST_F(DesksTest, ScrollableDesks) {
   UpdateDisplay("201x400");
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   auto* root_window = Shell::GetPrimaryRootWindow();
   const auto* desks_bar_view =
@@ -4320,8 +4308,7 @@
   auto* root_window = Shell::GetPrimaryRootWindow();
   SplitViewController::Get(root_window)
       ->SnapWindow(window.get(), SplitViewController::LEFT);
-  auto* overview_controller = Shell::Get()->overview_controller();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
   auto* desks_bar = GetOverviewGridForRoot(root_window)->desks_bar_view();
   auto* event_generator = GetEventGenerator();
   event_generator->MoveMouseTo(desks_bar->GetBoundsInScreen().CenterPoint());
@@ -4364,7 +4351,7 @@
   for (size_t i = 1; i < max_desks_size; i++)
     NewDesk();
 
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   auto* desks_bar =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow())->desks_bar_view();
 
@@ -4449,9 +4436,8 @@
 
   auto* desks_controller = DesksController::Get();
   EXPECT_EQ(desks_controller->desks().size(), max_desks_size);
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
   auto* desks_bar =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow())->desks_bar_view();
 
@@ -4540,8 +4526,7 @@
   auto* root_window = Shell::GetPrimaryRootWindow();
   SplitViewController::Get(root_window)
       ->SnapWindow(window.get(), SplitViewController::LEFT);
-  auto* overview_controller = Shell::Get()->overview_controller();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
   auto* desks_bar = GetOverviewGridForRoot(root_window)->desks_bar_view();
   auto mini_views = desks_bar->mini_views();
   ASSERT_EQ(mini_views.size(), desks_util::kMaxNumberOfDesks);
@@ -4827,8 +4812,7 @@
 TEST_F(DesksTest, EnterOverviewWithCorrectDesksBarState) {
   auto* controller = DesksController::Get();
   ASSERT_EQ(1u, controller->desks().size());
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
 
   auto* root_window = Shell::GetPrimaryRootWindow();
   auto* desks_bar_view = GetOverviewGridForRoot(root_window)->desks_bar_view();
@@ -4841,11 +4825,11 @@
   // Click new desk button in the zero state bar to create a new desk.
   ClickOnView(desks_bar_view->zero_state_new_desk_button(),
               GetEventGenerator());
-  overview_controller->EndOverview();
+  ExitOverview();
 
   // Desks bar should not stay in zero state if there are more than one desks.
   EXPECT_EQ(2u, controller->desks().size());
-  overview_controller->StartOverview();
+  EnterOverview();
   desks_bar_view = GetOverviewGridForRoot(root_window)->desks_bar_view();
   EXPECT_EQ(2u, desks_bar_view->mini_views().size());
   EXPECT_FALSE(desks_bar_view->IsZeroState());
@@ -4853,8 +4837,7 @@
 
 // Tests the behavior of desks bar zero state.
 TEST_F(DesksTest, DesksBarZeroState) {
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
 
   auto* root_window = Shell::GetPrimaryRootWindow();
   auto* desks_bar_view = GetOverviewGridForRoot(root_window)->desks_bar_view();
@@ -4876,8 +4859,8 @@
   EXPECT_FALSE(zero_state_default_desk_button->GetVisible());
   EXPECT_FALSE(zero_state_new_desk_button->GetVisible());
 
-  overview_controller->EndOverview();
-  overview_controller->StartOverview();
+  ExitOverview();
+  EnterOverview();
   desks_bar_view = GetOverviewGridForRoot(root_window)->desks_bar_view();
   ASSERT_TRUE(desks_bar_view->IsZeroState());
 
@@ -4902,8 +4885,7 @@
 
 TEST_F(DesksTest, NewDeskButton) {
   auto* controller = DesksController::Get();
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
 
   auto* root_window = Shell::GetPrimaryRootWindow();
   auto* desks_bar_view = GetOverviewGridForRoot(root_window)->desks_bar_view();
@@ -4933,8 +4915,7 @@
 
 TEST_F(DesksTest, ZeroStateDeskButtonText) {
   UpdateDisplay("1600x1200");
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
 
   auto* root_window = Shell::GetPrimaryRootWindow();
   auto* desks_bar_view = GetOverviewGridForRoot(root_window)->desks_bar_view();
@@ -4955,8 +4936,8 @@
   SendKey(ui::VKEY_S);
   SendKey(ui::VKEY_T);
   SendKey(ui::VKEY_RETURN);
-  overview_controller->EndOverview();
-  overview_controller->StartOverview();
+  ExitOverview();
+  EnterOverview();
 
   desks_bar_view = GetOverviewGridForRoot(root_window)->desks_bar_view();
   EXPECT_TRUE(desks_bar_view->IsZeroState());
@@ -4983,8 +4964,8 @@
   for (size_t i = 0; i < DeskNameView::kMaxLength + 5; i++)
     SendKey(ui::VKEY_A);
   SendKey(ui::VKEY_RETURN);
-  overview_controller->EndOverview();
-  overview_controller->StartOverview();
+  ExitOverview();
+  EnterOverview();
 
   desks_bar_view = GetOverviewGridForRoot(root_window)->desks_bar_view();
   auto* zero_state_default_desk_button =
@@ -5004,9 +4985,8 @@
 TEST_F(DesksTest, ReorderDesksByMouse) {
   auto* desks_controller = DesksController::Get();
 
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   auto* root_window = Shell::GetPrimaryRootWindow();
   const auto* desks_bar_view =
@@ -5075,9 +5055,8 @@
 TEST_F(DesksTest, ReorderDesksByGesture) {
   auto* desks_controller = DesksController::Get();
 
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   auto* root_window = Shell::GetPrimaryRootWindow();
   const auto* desks_bar_view =
@@ -5147,7 +5126,7 @@
   auto* desks_controller = DesksController::Get();
 
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
 
   auto* root_window = Shell::GetPrimaryRootWindow();
@@ -5234,7 +5213,7 @@
   auto* desks_controller = DesksController::Get();
 
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
 
   auto* root_window = Shell::GetPrimaryRootWindow();
@@ -5325,9 +5304,8 @@
 
   auto* desks_controller = DesksController::Get();
   EXPECT_EQ(desks_controller->desks().size(), max_desks_size);
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
   auto* desks_bar =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow())->desks_bar_view();
 
@@ -5427,9 +5405,8 @@
 // back, click its target location won't cause any crashes.
 // Regression test of https://crbug.com/1171880.
 TEST_F(DesksTest, ClickTargetLocationOfDroppedDesk) {
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  ASSERT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   const auto* desks_bar_view = overview_grid->desks_bar_view();
@@ -5461,9 +5438,8 @@
 // Tests that while reordering desks by drag & drop, when a desk is snapping
 // back, dragging a desk preview on the shelf will start a new drag.
 TEST_F(DesksTest, DragNewDeskWhileSnappingBack) {
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  ASSERT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   const auto* desks_bar_view = overview_grid->desks_bar_view();
@@ -5498,9 +5474,8 @@
 // mouse or exiting overview will not have UAF issues
 // (https://crbug.com/1222120).
 TEST_F(DesksTest, RemoveDeskWhileDragging) {
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   const auto* desks_bar_view =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow())->desks_bar_view();
@@ -5541,7 +5516,7 @@
   EXPECT_FALSE(desks_bar_view->IsDraggingDesk());
 
   // Exiting overview will not have any issue.
-  overview_controller->EndOverview();
+  ExitOverview();
 }
 
 // Tests that the right desk containers are visible when switching between desks
@@ -5609,9 +5584,8 @@
   ASSERT_EQ(2u, DesksController::Get()->desks().size());
 
   // Start overview.
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  ASSERT_TRUE(overview_controller->InOverviewSession());
+  EnterOverview();
+  ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   // Setup an internal keyboard and an external keyboard.
   ui::DeviceDataManagerTestApi().SetKeyboardDevices(
@@ -5853,7 +5827,6 @@
 
 // Tests the bar's visibility while entering or leaving overview mode.
 TEST_F(PersistentDesksBarTest, OverviewMode) {
-  auto* shell = Shell::Get();
   auto* desks_controller = DesksController::Get();
   ASSERT_EQ(1u, desks_controller->desks().size());
 
@@ -5861,35 +5834,34 @@
   EXPECT_TRUE(GetBarWidget());
   // Entering overview mode should destroy the bar. Exiting overview mode with
   // more than one desk should create the bar and show it.
-  auto* overview_controller = shell->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_FALSE(GetBarWidget());
   EXPECT_EQ(2u, desks_controller->desks().size());
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_TRUE(GetBarWidget());
   EXPECT_TRUE(IsWidgetVisible());
 
   // Exiting overview mode with only one desk should not create the bar.
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_FALSE(GetBarWidget());
   RemoveDesk(desks_controller->desks()[1].get());
   EXPECT_EQ(1u, desks_controller->desks().size());
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_FALSE(GetBarWidget());
 
   // Each desk button in the bar should show the corresponding desk's name.
-  overview_controller->StartOverview();
+  EnterOverview();
   NewDesk();
   EXPECT_EQ(2u, desks_controller->desks().size());
   desks_controller->desks()[1].get()->SetName(u"test", /*set_by_user=*/true);
-  overview_controller->EndOverview();
+  ExitOverview();
   auto desk_buttons = DesksTestApi::GetPersistentDesksBarDeskButtons();
   for (size_t i = 0; i < desk_buttons.size(); i++)
     EXPECT_EQ(desk_buttons[i]->GetText(), desks_controller->desks()[i]->name());
 
   // The desk buttons should have the same order as the desks after reordering.
   auto* event_generator = GetEventGenerator();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_EQ(u"test", desks_controller->desks()[1]->name());
   const auto* desks_bar_view =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow())->desks_bar_view();
@@ -5899,7 +5871,7 @@
                                    .CenterPoint());
   event_generator->ReleaseLeftButton();
   EXPECT_EQ(u"test", desks_controller->desks()[0]->name());
-  overview_controller->EndOverview();
+  ExitOverview();
   desk_buttons = DesksTestApi::GetPersistentDesksBarDeskButtons();
   for (size_t i = 0; i < desk_buttons.size(); i++)
     EXPECT_EQ(desk_buttons[i]->GetText(), desks_controller->desks()[i]->name());
@@ -5932,7 +5904,7 @@
   // The bar should not be created after entering or leaving tablet mode with
   // overview mode on.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   TabletModeControllerTestApi().EnterTabletMode();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   EXPECT_FALSE(GetBarWidget());
@@ -5959,20 +5931,19 @@
 
   // With the bar being set to hide, it should not be created after exiting
   // overview mode with more than one desk.
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
-  overview_controller->EndOverview();
+  EnterOverview();
+  ExitOverview();
   EXPECT_FALSE(GetBarWidget());
 
   // With the bar being set to show, it should be created after exiting overview
   // mode with more than one desk.
-  overview_controller->StartOverview();
+  EnterOverview();
   context_menu = DesksTestApi::GetDesksBarContextMenu();
   context_menu->ExecuteCommand(
       static_cast<int>(
           PersistentDesksBarContextMenu::CommandId::kShowOrHideBar),
       /*event_flags=*/0);
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_TRUE(GetBarWidget());
   EXPECT_TRUE(IsWidgetVisible());
 }
diff --git a/ash/wm/desks/persistent_desks_bar_button.cc b/ash/wm/desks/persistent_desks_bar_button.cc
index adfd317..788880eb 100644
--- a/ash/wm/desks/persistent_desks_bar_button.cc
+++ b/ash/wm/desks/persistent_desks_bar_button.cc
@@ -114,7 +114,8 @@
 PersistentDesksBarOverviewButton::~PersistentDesksBarOverviewButton() = default;
 
 void PersistentDesksBarOverviewButton::OnButtonPressed() {
-  Shell::Get()->overview_controller()->StartOverview();
+  Shell::Get()->overview_controller()->StartOverview(
+      OverviewStartAction::kBentoBar);
 }
 
 }  // namespace ash
diff --git a/ash/wm/desks/persistent_desks_bar_controller.cc b/ash/wm/desks/persistent_desks_bar_controller.cc
index 4b9defa..d01d0649 100644
--- a/ash/wm/desks/persistent_desks_bar_controller.cc
+++ b/ash/wm/desks/persistent_desks_bar_controller.cc
@@ -14,6 +14,7 @@
 #include "ash/wm/desks/persistent_desks_bar_view.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "base/metrics/histogram_macros.h"
 #include "ui/aura/window.h"
 #include "ui/display/screen.h"
 #include "ui/views/widget/widget.h"
@@ -172,6 +173,8 @@
   is_enabled_ = !is_enabled_;
   if (!is_enabled_)
     DestroyBarWidget();
+
+  UMA_HISTOGRAM_BOOLEAN("Ash.Desks.BentoBarEnabled", is_enabled_);
 }
 
 bool PersistentDesksBarController::ShouldPersistentDesksBarBeCreated() const {
diff --git a/ash/wm/desks/root_window_desk_switch_animator.cc b/ash/wm/desks/root_window_desk_switch_animator.cc
index cd38d3a0..8411afe 100644
--- a/ash/wm/desks/root_window_desk_switch_animator.cc
+++ b/ash/wm/desks/root_window_desk_switch_animator.cc
@@ -619,7 +619,14 @@
 }
 
 int RootWindowDeskSwitchAnimator::GetXPositionOfScreenshot(int index) {
+  // TODO(crbug.com/1223866): Investigate if we can prevent this higher in the
+  // call stack.
+  if (index < 0 || index >= static_cast<int>(screenshot_layers_.size()))
+    return 0;
   ui::Layer* layer = screenshot_layers_[index];
+  if (!layer)
+    return 0;
+
   DCHECK(layer);
   return layer->bounds().x();
 }
diff --git a/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc b/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc
index 8720998..16c7763 100644
--- a/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc
+++ b/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc
@@ -171,19 +171,14 @@
 
 }  // namespace
 
-BackGestureEventHandler::BackGestureEventHandler()
-    : gesture_provider_(this, this) {
+BackGestureEventHandler::BackGestureEventHandler() {
   if (features::AreContextualNudgesEnabled()) {
     nudge_controller_ =
         std::make_unique<BackGestureContextualNudgeControllerImpl>();
   }
-
-  display::Screen::GetScreen()->AddObserver(this);
 }
 
-BackGestureEventHandler::~BackGestureEventHandler() {
-  display::Screen::GetScreen()->RemoveObserver(this);
-}
+BackGestureEventHandler::~BackGestureEventHandler() = default;
 
 void BackGestureEventHandler::OnDisplayMetricsChanged(
     const display::Display& display,
diff --git a/ash/wm/gestures/back_gesture/back_gesture_event_handler.h b/ash/wm/gestures/back_gesture/back_gesture_event_handler.h
index c8b03330..e4b9e6f 100644
--- a/ash/wm/gestures/back_gesture/back_gesture_event_handler.h
+++ b/ash/wm/gestures/back_gesture/back_gesture_event_handler.h
@@ -112,7 +112,10 @@
   // OnTouchEvent session. This is done to avoid tap down event be used by the
   // window that is underneath to do other things (e.g, highlight a menu item)
   // instead of going back.
-  ui::GestureProviderAura gesture_provider_;
+  ui::GestureProviderAura gesture_provider_{this, this};
+
+  // Register for DisplayObserver callbacks.
+  display::ScopedDisplayObserver display_observer_{this};
 
   // False if BackGestureEventHandler should not handle touch events directly in
   // OnTouchEvent(), but should wait after touch ack is received. This is needed
diff --git a/ash/wm/gestures/back_gesture/back_gesture_event_handler_unittest.cc b/ash/wm/gestures/back_gesture/back_gesture_event_handler_unittest.cc
index db06809..52b7ffb 100644
--- a/ash/wm/gestures/back_gesture/back_gesture_event_handler_unittest.cc
+++ b/ash/wm/gestures/back_gesture/back_gesture_event_handler_unittest.cc
@@ -204,7 +204,7 @@
   WindowState::Get(top_window())->Unminimize();
   ASSERT_FALSE(WindowState::Get(top_window())->IsMinimized());
   auto* shell = Shell::Get();
-  shell->overview_controller()->StartOverview();
+  EnterOverview();
   ASSERT_TRUE(shell->overview_controller()->InOverviewSession());
   GenerateBackSequence();
   // Should trigger go back instead of minimize the window since it is in
@@ -213,9 +213,9 @@
 
   // Swipe back at overview mode without opened window should still trigger
   // going back.
-  shell->overview_controller()->EndOverview();
+  ExitOverview();
   ResetTopWindow();
-  shell->overview_controller()->StartOverview();
+  EnterOverview();
   GenerateBackSequence();
   EXPECT_EQ(2, target_back_release.accelerator_count());
   EXPECT_TRUE(shell->app_list_controller()->IsHomeScreenVisible());
@@ -376,7 +376,7 @@
 
   // Start overview first and then snap window in splitview to make sure
   // window activation order remains the same.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   auto* split_view_controller =
       SplitViewController::Get(Shell::GetPrimaryRootWindow());
   split_view_controller->SnapWindow(left_window.get(),
diff --git a/ash/wm/gestures/wm_gesture_handler.cc b/ash/wm/gestures/wm_gesture_handler.cc
index ede3907..21505e1 100644
--- a/ash/wm/gestures/wm_gesture_handler.cc
+++ b/ash/wm/gestures/wm_gesture_handler.cc
@@ -120,14 +120,15 @@
     base::RecordAction(base::UserMetricsAction("Touchpad_Gesture_Overview"));
     if (overview_controller->AcceptSelection())
       return true;
-    overview_controller->EndOverview();
+    overview_controller->EndOverview(OverviewEndAction::k3FingerVerticalScroll);
   } else {
     auto* window_cycle_controller = Shell::Get()->window_cycle_controller();
     if (window_cycle_controller->IsCycling())
       window_cycle_controller->CancelCycling();
 
     base::RecordAction(base::UserMetricsAction("Touchpad_Gesture_Overview"));
-    overview_controller->StartOverview();
+    overview_controller->StartOverview(
+        OverviewStartAction::k3FingerVerticalScroll);
   }
 
   return true;
diff --git a/ash/wm/gestures/wm_gesture_handler_unittest.cc b/ash/wm/gestures/wm_gesture_handler_unittest.cc
index fe486f9..1f09950 100644
--- a/ash/wm/gestures/wm_gesture_handler_unittest.cc
+++ b/ash/wm/gestures/wm_gesture_handler_unittest.cc
@@ -211,7 +211,7 @@
   // Enter overview mode as if using an accelerator.
   // Entering overview mode with an upwards three-finger scroll gesture would
   // have the same result (allow selection using horizontal scroll).
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(InOverviewSession());
 
   // Scrolls until a window is highlight, ignoring any desks items (if any).
@@ -377,8 +377,7 @@
 TEST_F(WmGestureHandlerTest, ActivateHighlightedDeskWithVerticalScroll) {
   auto* desks_controller = DesksController::Get();
 
-  auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(InOverviewSession());
 
   // Create a new desk (we have two desks now).
@@ -389,7 +388,8 @@
   EXPECT_EQ(0, desks_controller->GetActiveDeskIndex());
 
   // Move highlight to the second desk.
-  OverviewSession* overview_session = overview_controller->overview_session();
+  OverviewSession* overview_session =
+      Shell::Get()->overview_controller()->overview_session();
   DeskMiniView* mini_view_1 =
       overview_session->GetGridWithRootWindow(Shell::GetPrimaryRootWindow())
           ->desks_bar_view()
diff --git a/ash/wm/lock_state_controller_unittest.cc b/ash/wm/lock_state_controller_unittest.cc
index 09128f6..8276c86 100644
--- a/ash/wm/lock_state_controller_unittest.cc
+++ b/ash/wm/lock_state_controller_unittest.cc
@@ -729,9 +729,8 @@
                              ->wallpaper_widget_controller()
                              ->wallpaper_view();
 
-  auto* overview_controller = Shell::Get()->overview_controller();
   // Enter Overview and verify wallpaper properties.
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_EQ(wallpaper_constants::kOverviewBlur, wallpaper_view->blur_sigma());
 
   // Start lock animation and verify wallpaper properties.
diff --git a/ash/wm/overlay_layout_manager.cc b/ash/wm/overlay_layout_manager.cc
index 8ac160f9..d93b313 100644
--- a/ash/wm/overlay_layout_manager.cc
+++ b/ash/wm/overlay_layout_manager.cc
@@ -18,12 +18,9 @@
 OverlayLayoutManager::OverlayLayoutManager(aura::Window* overlay_container)
     : overlay_container_(overlay_container) {
   DCHECK(overlay_container_);
-  Screen::GetScreen()->AddObserver(this);
 }
 
-OverlayLayoutManager::~OverlayLayoutManager() {
-  Screen::GetScreen()->RemoveObserver(this);
-}
+OverlayLayoutManager::~OverlayLayoutManager() = default;
 
 void OverlayLayoutManager::OnDisplayMetricsChanged(
     const display::Display& display,
diff --git a/ash/wm/overlay_layout_manager.h b/ash/wm/overlay_layout_manager.h
index db5a1040..e36fb9c 100644
--- a/ash/wm/overlay_layout_manager.h
+++ b/ash/wm/overlay_layout_manager.h
@@ -31,6 +31,8 @@
  private:
   aura::Window* overlay_container_;
 
+  display::ScopedDisplayObserver display_observer_{this};
+
   DISALLOW_COPY_AND_ASSIGN(OverlayLayoutManager);
 };
 
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc
index 6fa0f13b..75bf195 100644
--- a/ash/wm/overview/overview_controller.cc
+++ b/ash/wm/overview/overview_controller.cc
@@ -117,7 +117,8 @@
   }
 }
 
-bool OverviewController::StartOverview(OverviewEnterExitType type) {
+bool OverviewController::StartOverview(OverviewStartAction action,
+                                       OverviewEnterExitType type) {
   // No need to start overview if overview is currently active.
   if (InOverviewSession())
     return true;
@@ -126,10 +127,12 @@
     return false;
 
   ToggleOverview(type);
+  RecordOverviewStartAction(action);
   return true;
 }
 
-bool OverviewController::EndOverview(OverviewEnterExitType type) {
+bool OverviewController::EndOverview(OverviewEndAction action,
+                                     OverviewEnterExitType type) {
   // No need to end overview if overview is already ended.
   if (!InOverviewSession())
     return true;
@@ -138,6 +141,7 @@
     return false;
 
   ToggleOverview(type);
+  RecordOverviewEndAction(action);
   return true;
 }
 
diff --git a/ash/wm/overview/overview_controller.h b/ash/wm/overview/overview_controller.h
index 0200cf9e..beab8f52 100644
--- a/ash/wm/overview/overview_controller.h
+++ b/ash/wm/overview/overview_controller.h
@@ -11,6 +11,7 @@
 #include "ash/ash_export.h"
 #include "ash/wm/overview/delayed_animation_observer.h"
 #include "ash/wm/overview/overview_delegate.h"
+#include "ash/wm/overview/overview_metrics.h"
 #include "ash/wm/overview/overview_observer.h"
 #include "ash/wm/overview/overview_session.h"
 #include "ash/wm/overview/overview_types.h"
@@ -33,12 +34,15 @@
   OverviewController();
   ~OverviewController() override;
 
-  // Starts/Ends overview with |type|. Returns true if enter or exit overview
-  // successful. Depending on |type| the enter/exit animation will look
-  // different.
+  // Starts/Ends overview with `type`. Returns true if enter or exit overview
+  // successful. Depending on `type` the enter/exit animation will look
+  // different. `action` is used by UMA to record the reasons that trigger
+  // overview starts or ends. E.g, pressing the overview button.
   bool StartOverview(
+      OverviewStartAction action,
       OverviewEnterExitType type = OverviewEnterExitType::kNormal);
-  bool EndOverview(OverviewEnterExitType type = OverviewEnterExitType::kNormal);
+  bool EndOverview(OverviewEndAction action,
+                   OverviewEnterExitType type = OverviewEnterExitType::kNormal);
 
   // Returns true if overview mode is active.
   bool InOverviewSession() const;
diff --git a/ash/wm/overview/overview_controller_unittest.cc b/ash/wm/overview/overview_controller_unittest.cc
index ea877555..6b3496d 100644
--- a/ash/wm/overview/overview_controller_unittest.cc
+++ b/ash/wm/overview/overview_controller_unittest.cc
@@ -216,7 +216,7 @@
   TestOverviewObserver observer(/*should_monitor_animation_state = */ true);
   // Enter without windows.
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   EXPECT_EQ(TestOverviewObserver::COMPLETED,
             observer.starting_animation_state());
@@ -228,7 +228,7 @@
 
   // Exiting overview has no animations until the overview animation is
   // complete.
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_FALSE(overview_controller->InOverviewSession());
   EXPECT_EQ(TestOverviewObserver::UNKNOWN, observer.ending_animation_state());
   EXPECT_EQ(wallpaper_constants::kOverviewBlur,
@@ -253,7 +253,7 @@
   ASSERT_EQ(TestOverviewObserver::UNKNOWN, observer.ending_animation_state());
 
   // Enter with windows.
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   EXPECT_EQ(TestOverviewObserver::UNKNOWN, observer.starting_animation_state());
   EXPECT_EQ(TestOverviewObserver::UNKNOWN, observer.ending_animation_state());
@@ -262,7 +262,7 @@
   EXPECT_FALSE(wallpaper_widget_controller->IsAnimating());
 
   // Exit with windows before starting animation ends.
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_FALSE(overview_controller->InOverviewSession());
   EXPECT_EQ(TestOverviewObserver::CANCELED,
             observer.starting_animation_state());
@@ -275,7 +275,7 @@
   observer.Reset();
 
   // Enter again before exit animation ends.
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   EXPECT_EQ(TestOverviewObserver::UNKNOWN, observer.starting_animation_state());
   EXPECT_EQ(TestOverviewObserver::CANCELED, observer.ending_animation_state());
@@ -323,7 +323,7 @@
   EXPECT_EQ(OcclusionState::VISIBLE, window2->GetOcclusionState());
 
   // Enter with windows.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   EXPECT_EQ(OcclusionState::OCCLUDED, window1->GetOcclusionState());
   EXPECT_EQ(OcclusionState::VISIBLE, window2->GetOcclusionState());
 
@@ -335,7 +335,7 @@
   EXPECT_EQ(OcclusionState::VISIBLE, window1->GetOcclusionState());
 
   // Exit with windows.
-  Shell::Get()->overview_controller()->EndOverview();
+  ExitOverview();
   EXPECT_EQ(OcclusionState::VISIBLE, window1->GetOcclusionState());
   EXPECT_EQ(OcclusionState::VISIBLE, window2->GetOcclusionState());
   observer.WaitForEndingAnimationComplete();
@@ -347,7 +347,7 @@
   observer.Reset();
 
   // Enter again.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   EXPECT_EQ(OcclusionState::OCCLUDED, window1->GetOcclusionState());
   EXPECT_EQ(OcclusionState::VISIBLE, window2->GetOcclusionState());
   auto* active = window_util::GetActiveWindow();
@@ -381,7 +381,7 @@
       CreateTestWindowInShellWithBounds(bounds));
   WaitForShowAnimation(window.get());
   auto* controller = Shell::Get()->overview_controller();
-  controller->StartOverview();
+  EnterOverview();
   // Ensure |window| is in overview with window state non-PIP.
   EXPECT_TRUE(controller->overview_session()->IsWindowInOverview(window.get()));
   WMEvent pip_event(WM_EVENT_PIP);
@@ -398,7 +398,7 @@
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplay().id());
   GetAppListTestHelper()->CheckVisibility(true);
 
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   GetAppListTestHelper()->WaitUntilIdle();
   GetAppListTestHelper()->CheckVisibility(false);
 }
@@ -420,9 +420,8 @@
   ASSERT_TRUE(window3->IsVisible());
 
   // Enter overview. Only one of the three windows is in overview, and visible.
-  auto* controller = Shell::Get()->overview_controller();
-  controller->StartOverview();
-  auto* session = controller->overview_session();
+  EnterOverview();
+  auto* session = Shell::Get()->overview_controller()->overview_session();
   ASSERT_TRUE(session);
   EXPECT_TRUE(session->IsWindowInOverview(window1.get()));
   EXPECT_FALSE(session->IsWindowInOverview(window2.get()));
@@ -434,7 +433,7 @@
   // On exiting overview, the windows should all be visible. Use a run loop
   // since |session| is destroyed in a post task, and the restoring windows'
   // previous visibility happens in the destructor.
-  controller->EndOverview();
+  ExitOverview();
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(window1->IsVisible());
   EXPECT_TRUE(window2->IsVisible());
@@ -467,15 +466,13 @@
 
   // Tests the case where we enter without windows and do regular enter/exit
   // (wait for enter animation to finish before exiting).
-  auto* overview_controller = Shell::Get()->overview_controller();
-
   for (bool is_tablet_mode : {false, true}) {
     SCOPED_TRACE(is_tablet_mode ? "Tablet Mode" : "Clamshell Mode");
     set_tablet_mode_enabled(is_tablet_mode);
 
-    overview_controller->StartOverview();
+    EnterOverview();
     wait_for_animation(/*enter=*/true);
-    overview_controller->EndOverview();
+    ExitOverview();
     wait_for_animation(/*enter=*/false);
     EXPECT_TRUE(observer.ObserverCountsEqual());
   }
@@ -489,26 +486,26 @@
 
     // Tests the case where we enter with windows and do regular enter/exit
     // (wait for enter animation to finish before exiting).
-    overview_controller->StartOverview();
+    EnterOverview();
     wait_for_animation(/*enter=*/true);
-    overview_controller->EndOverview();
+    ExitOverview();
     wait_for_animation(/*enter=*/false);
     EXPECT_TRUE(observer.ObserverCountsEqual());
 
     // Tests the case where we exit overview before the start animation has
     // completed.
-    overview_controller->StartOverview();
-    overview_controller->EndOverview();
+    EnterOverview();
+    ExitOverview();
     wait_for_animation(/*enter=*/false);
     EXPECT_TRUE(observer.ObserverCountsEqual());
 
     // Tests the case where we enter overview before the exit animation has
     // completed.
-    overview_controller->StartOverview();
+    EnterOverview();
     wait_for_animation(/*enter=*/true);
-    overview_controller->EndOverview();
-    overview_controller->StartOverview();
-    overview_controller->EndOverview();
+    ExitOverview();
+    EnterOverview();
+    ExitOverview();
     wait_for_animation(/*enter=*/false);
     EXPECT_TRUE(observer.ObserverCountsEqual());
   }
@@ -528,13 +525,12 @@
   std::unique_ptr<aura::Window> window(
       CreateTestWindowInShellWithBounds(bounds));
 
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   EXPECT_FALSE(observer.last_animation_was_fade());
 
   // Exit to home launcher using fade out animation. This should minimize all
   // windows.
-  Shell::Get()->overview_controller()->EndOverview(
-      OverviewEnterExitType::kFadeOutExit);
+  ExitOverview(OverviewEnterExitType::kFadeOutExit);
 
   EXPECT_TRUE(observer.last_animation_was_fade());
 
@@ -543,7 +539,7 @@
 
   // All windows are minimized, so we should use the fade in animation to enter
   // overview.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(observer.last_animation_was_fade());
 }
 
@@ -556,17 +552,17 @@
   std::unique_ptr<aura::Window> window(
       CreateTestWindowInShellWithBounds(bounds));
 
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   EXPECT_FALSE(observer.last_animation_was_fade());
 
-  Shell::Get()->overview_controller()->EndOverview();
+  ExitOverview();
   EXPECT_FALSE(observer.last_animation_was_fade());
 
   // Even with all window minimized, overview should not use fade animation to
   // enter.
   ASSERT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
   WindowState::Get(window.get())->Minimize();
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   EXPECT_FALSE(observer.last_animation_was_fade());
 }
 
@@ -579,8 +575,7 @@
   ui::ScopedAnimationDurationScaleMode non_zero(
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
 
-  Shell::Get()->overview_controller()->StartOverview(
-      OverviewEnterExitType::kFadeInEnter);
+  EnterOverview(OverviewEnterExitType::kFadeInEnter);
   auto* wallpaper_widget_controller =
       Shell::GetPrimaryRootWindowController()->wallpaper_widget_controller();
   EXPECT_GT(wallpaper_widget_controller->GetWallpaperBlur(), 0);
@@ -603,13 +598,12 @@
   // started asynchronously.
   ui::ScopedAnimationDurationScaleMode non_zero(
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
 
   // Exit to home launcher using fade out animation. This should minimize all
   // windows.
   TestOverviewObserver observer(/*should_monitor_animation_state = */ true);
-  Shell::Get()->overview_controller()->EndOverview(
-      OverviewEnterExitType::kFadeOutExit);
+  ExitOverview(OverviewEnterExitType::kFadeOutExit);
 
   EXPECT_TRUE(observer.last_animation_was_fade());
 
@@ -631,7 +625,7 @@
 
   ui::ScopedAnimationDurationScaleMode non_zero(
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
 
   // Destroy a window during the enter animation.
   window1.reset();
@@ -639,7 +633,7 @@
       OverviewAnimationState::kEnterAnimationComplete);
   ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
-  Shell::Get()->overview_controller()->EndOverview();
+  ExitOverview();
 
   // Destroy a window during the exit animation.
   window2.reset();
@@ -667,7 +661,8 @@
   // DeskSwitchAnimationWaiter:
   void OnDeskActivationChanged(const Desk* activated,
                                const Desk* deactivated) override {
-    Shell::Get()->overview_controller()->StartOverview();
+    Shell::Get()->overview_controller()->StartOverview(
+        OverviewStartAction::kTests);
   }
 };
 
@@ -690,7 +685,7 @@
   waiter.Wait();
   EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
 
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
 
   // Tests that exiting overview works as it is part of the desk switch
@@ -727,7 +722,7 @@
   keyboard_ui_controller()->ShowKeyboard(false /* locked */);
   ASSERT_TRUE(keyboard::WaitUntilShown());
 
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
 
   // Timeout failure here if the keyboard does not hide.
   keyboard::WaitUntilHidden();
@@ -738,7 +733,7 @@
   keyboard_ui_controller()->ShowKeyboard(true /* locked */);
   ASSERT_TRUE(keyboard::WaitUntilShown());
 
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   EXPECT_FALSE(keyboard::IsKeyboardHiding());
 }
 
@@ -767,16 +762,15 @@
     arc_windows[i] = created_windows[i + browser_window_count].get();
   }
 
-  auto* controller = Shell::Get()->overview_controller();
   EXPECT_CALL(observer, OnThrottlingStarted(
                             testing::UnorderedElementsAreArray(arc_windows),
                             frame_throttling_controller->throttled_fps()));
-  controller->StartOverview();
+  EnterOverview();
   EXPECT_THAT(frame_throttling_controller->GetFrameSinkIdsToThrottle(),
               ::testing::UnorderedElementsAreArray(ids));
 
   EXPECT_CALL(observer, OnThrottlingEnded());
-  controller->EndOverview();
+  ExitOverview();
   EXPECT_TRUE(frame_throttling_controller->GetFrameSinkIdsToThrottle().empty());
 
   frame_throttling_controller->RemoveArcObserver(&observer);
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index cb58ad8..a946230 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -953,7 +953,7 @@
       (split_view_controller->InClamshellSplitViewMode() &&
        overview_session_->IsEmpty())) {
     overview_session_->RestoreWindowActivation(false);
-    overview_controller->EndOverview();
+    overview_controller->EndOverview(OverviewEndAction::kSplitView);
     return;
   }
 
diff --git a/ash/wm/overview/overview_grid_event_handler.cc b/ash/wm/overview/overview_grid_event_handler.cc
index ff9214c..1277bcd 100644
--- a/ash/wm/overview/overview_grid_event_handler.cc
+++ b/ash/wm/overview/overview_grid_event_handler.cc
@@ -64,7 +64,8 @@
   // ui::GESTURE_END_EVENT which may cause a bad state.
   if (event->type() == ui::ET_MOUSE_PRESSED &&
       !overview_session_->CanProcessEvent()) {
-    Shell::Get()->overview_controller()->EndOverview();
+    Shell::Get()->overview_controller()->EndOverview(
+        OverviewEndAction::kClickingOutsideWindowsInOverview);
     event->StopPropagation();
     event->SetHandled();
     return;
@@ -156,7 +157,8 @@
       Shell::Get()->app_list_controller()->GoHome(display_id);
     }
   } else {
-    Shell::Get()->overview_controller()->EndOverview();
+    Shell::Get()->overview_controller()->EndOverview(
+        OverviewEndAction::kClickingOutsideWindowsInOverview);
   }
   event->StopPropagation();
 }
diff --git a/ash/wm/overview/overview_grid_unittest.cc b/ash/wm/overview/overview_grid_unittest.cc
index 2374162..7165fc23 100644
--- a/ash/wm/overview/overview_grid_unittest.cc
+++ b/ash/wm/overview/overview_grid_unittest.cc
@@ -279,8 +279,7 @@
   // We cannot create a grid object like in the other tests because creating a
   // grid calls |GetGridBoundsInScreen| with split view state both snapped which
   // is an unnatural state.
-  Shell::Get()->overview_controller()->StartOverview(
-      OverviewEnterExitType::kNormal);
+  EnterOverview();
 
   // Tests that |window3| is not animated even though its bounds are larger than
   // |window2| because it is fully occluded by |window1| + |window2| and the
diff --git a/ash/wm/overview/overview_metrics.cc b/ash/wm/overview/overview_metrics.cc
new file mode 100644
index 0000000..7f2f1ce
--- /dev/null
+++ b/ash/wm/overview/overview_metrics.cc
@@ -0,0 +1,22 @@
+// 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/wm/overview/overview_metrics.h"
+
+#include "base/metrics/histogram_macros.h"
+
+namespace ash {
+
+constexpr char kOverviewStartActionHistogram[] = "Ash.Overview.StartAction";
+constexpr char kOverviewEndActionHistogram[] = "Ash.Overview.EndAction";
+
+void RecordOverviewStartAction(OverviewStartAction type) {
+  UMA_HISTOGRAM_ENUMERATION(kOverviewStartActionHistogram, type);
+}
+
+void RecordOverviewEndAction(OverviewEndAction type) {
+  UMA_HISTOGRAM_ENUMERATION(kOverviewEndActionHistogram, type);
+}
+
+}  // namespace ash
diff --git a/ash/wm/overview/overview_metrics.h b/ash/wm/overview/overview_metrics.h
new file mode 100644
index 0000000..767acdd
--- /dev/null
+++ b/ash/wm/overview/overview_metrics.h
@@ -0,0 +1,57 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WM_OVERVIEW_OVERVIEW_METRICS_H_
+#define ASH_WM_OVERVIEW_OVERVIEW_METRICS_H_
+
+namespace ash {
+
+// Used for histograms. See OverviewStartAction at
+// tools/metrics/histograms/enums.xml.
+enum class OverviewStartAction {
+  kSplitView,
+  kAccelerator,
+  kDragWindowFromShelf,
+  kExitHomeLauncher,
+  kOverviewButton,
+  kOverviewButtonLongPress,
+  kBentoBar,
+  k3FingerVerticalScroll,
+  kDevTools,
+  kTests,
+  kMaxValue = kTests,
+};
+void RecordOverviewStartAction(OverviewStartAction type);
+
+// Used for histograms. See OverviewEndAction at
+// tools/metrics/histograms/enums.xml.
+enum class OverviewEndAction {
+  kSplitView,
+  kDragWindowFromShelf,
+  kEnterHomeLauncher,
+  kClickingOutsideWindowsInOverview,
+  kWindowActivating,
+  kLastWindowRemoved,
+  kDisplayAdded,
+  kAccelerator,
+  kKeyEscapeOrBack,
+  kDeskActivation,
+  kOverviewButton,
+  kOverviewButtonLongPress,
+  k3FingerVerticalScroll,
+  kEnabledDockedMagnifier,
+  kUserSwitch,
+  kStartedWindowCycle,
+  kShuttingDown,
+  kAppListActivatedInClamshell,
+  kShelfAlignmentChanged,
+  kDevTools,
+  kTests,
+  kMaxValue = kTests,
+};
+void RecordOverviewEndAction(OverviewEndAction type);
+
+}  // namespace ash
+
+#endif  // ASH_WM_OVERVIEW_OVERVIEW_METRICS_H_
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 08dfdc1..9954ee04 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -52,7 +52,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
-#include "ui/display/screen.h"
 #include "ui/events/event.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/widget/widget.h"
@@ -76,8 +75,8 @@
 
 // Tries to end overview. Returns true if overview is successfully ended, or
 // just was not active in the first place.
-bool EndOverview() {
-  return Shell::Get()->overview_controller()->EndOverview();
+bool EndOverview(OverviewEndAction action) {
+  return Shell::Get()->overview_controller()->EndOverview(action);
 }
 
 // A self-deleting window state observer that runs the given callback when its
@@ -239,7 +238,7 @@
 
   SplitViewController::Get(Shell::GetPrimaryRootWindow())->AddObserver(this);
 
-  display::Screen::GetScreen()->AddObserver(this);
+  display_observer_.emplace(this);
   base::RecordAction(base::UserMetricsAction("WindowSelector_Overview"));
   // Send an a11y alert.
   Shell::Get()->accessibility_controller()->TriggerAccessibilityAlert(
@@ -261,7 +260,7 @@
 
   // Stop observing screen metrics changes first to avoid auto-positioning
   // windows in response to work area changes from window activation.
-  display::Screen::GetScreen()->RemoveObserver(this);
+  display_observer_.reset();
 
   // Stop observing split view state changes before restoring window focus.
   // Otherwise the activation of the window triggers OnSplitViewStateChanged()
@@ -333,7 +332,7 @@
           ->InTabletSplitViewMode()) {
     UpdateNoWindowsWidget();
   } else {
-    EndOverview();
+    EndOverview(OverviewEndAction::kLastWindowRemoved);
   }
 }
 
@@ -737,7 +736,7 @@
     // Cancel overview session and do not restore activation when active window
     // is set to nullptr. This happens when removing a display.
     RestoreWindowActivation(false);
-    EndOverview();
+    EndOverview(OverviewEndAction::kWindowActivating);
     return;
   }
 
@@ -769,7 +768,7 @@
   if (gained_active == Shell::Get()->app_list_controller()->GetWindow() &&
       !Shell::Get()->tablet_mode_controller()->InTabletMode()) {
     RestoreWindowActivation(false);
-    EndOverview();
+    EndOverview(OverviewEndAction::kAppListActivatedInClamshell);
     return;
   }
 
@@ -808,7 +807,7 @@
 
   // Don't restore window activation on exit if a window was just activated.
   RestoreWindowActivation(false);
-  EndOverview();
+  EndOverview(OverviewEndAction::kWindowActivating);
 }
 
 aura::Window* OverviewSession::GetOverviewFocusWindow() {
@@ -927,10 +926,10 @@
 }
 
 void OverviewSession::OnDisplayAdded(const display::Display& display) {
-  if (EndOverview())
+  if (EndOverview(OverviewEndAction::kDisplayAdded))
     return;
   SplitViewController::Get(Shell::GetPrimaryRootWindow())->EndSplitView();
-  EndOverview();
+  EndOverview(OverviewEndAction::kDisplayAdded);
 }
 
 void OverviewSession::OnDisplayMetricsChanged(const display::Display& display,
@@ -1008,7 +1007,7 @@
   switch (key_code) {
     case ui::VKEY_BROWSER_BACK:
     case ui::VKEY_ESCAPE:
-      EndOverview();
+      EndOverview(OverviewEndAction::kKeyEscapeOrBack);
       break;
     case ui::VKEY_UP:
       ++num_key_presses_;
@@ -1068,7 +1067,7 @@
 
 void OverviewSession::OnShellDestroying() {
   // Cancel selection will call |Shutdown()|, which will remove observer.
-  EndOverview();
+  EndOverview(OverviewEndAction::kShuttingDown);
 }
 
 void OverviewSession::OnShelfAlignmentChanged(aura::Window* root_window,
@@ -1097,7 +1096,7 @@
   // doesn't get updated anyways (see https://crbug.com/834400). In this case,
   // even updating the grid bounds won't make any difference, so we simply exit
   // overview.
-  EndOverview();
+  EndOverview(OverviewEndAction::kShelfAlignmentChanged);
 }
 
 void OverviewSession::OnSplitViewStateChanged(
@@ -1162,7 +1161,7 @@
 }
 
 void OverviewSession::RemoveAllObservers() {
-  display::Screen::GetScreen()->RemoveObserver(this);
+  display_observer_.reset();
   if (active_window_before_overview_)
     active_window_before_overview_->RemoveObserver(this);
   active_window_before_overview_ = nullptr;
diff --git a/ash/wm/overview/overview_session.h b/ash/wm/overview/overview_session.h
index 791a285..8fbf2042 100644
--- a/ash/wm/overview/overview_session.h
+++ b/ash/wm/overview/overview_session.h
@@ -410,6 +410,8 @@
 
   std::unique_ptr<OverviewHighlightController> highlight_controller_;
 
+  absl::optional<display::ScopedDisplayObserver> display_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(OverviewSession);
 };
 
diff --git a/ash/wm/overview/overview_test_api.cc b/ash/wm/overview/overview_test_api.cc
index 0151985..45e9f50 100644
--- a/ash/wm/overview/overview_test_api.cc
+++ b/ash/wm/overview/overview_test_api.cc
@@ -40,8 +40,9 @@
             : OverviewAnimationState::kExitAnimationComplete,
       std::move(done_callback));
 
-  const bool animation_started = start ? overview_controller->StartOverview()
-                                       : overview_controller->EndOverview();
+  const bool animation_started =
+      start ? overview_controller->StartOverview(OverviewStartAction::kTests)
+            : overview_controller->EndOverview(OverviewEndAction::kTests);
 
   if (!animation_started)
     waiter->Cancel();
diff --git a/ash/wm/overview/overview_test_util.cc b/ash/wm/overview/overview_test_util.cc
index 9a21f46..3c4d7fc 100644
--- a/ash/wm/overview/overview_test_util.cc
+++ b/ash/wm/overview/overview_test_util.cc
@@ -68,9 +68,9 @@
 void ToggleOverview(OverviewEnterExitType type) {
   auto* overview_controller = Shell::Get()->overview_controller();
   if (overview_controller->InOverviewSession())
-    overview_controller->EndOverview(type);
+    overview_controller->EndOverview(OverviewEndAction::kTests, type);
   else
-    overview_controller->StartOverview(type);
+    overview_controller->StartOverview(OverviewStartAction::kTests, type);
 }
 
 void WaitForOverviewEnterAnimation() {
diff --git a/ash/wm/overview/overview_window_drag_controller_unittest.cc b/ash/wm/overview/overview_window_drag_controller_unittest.cc
index a25144c..8f441c6 100644
--- a/ash/wm/overview/overview_window_drag_controller_unittest.cc
+++ b/ash/wm/overview/overview_window_drag_controller_unittest.cc
@@ -109,7 +109,7 @@
   base::RunLoop().RunUntilIdle();
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   auto* overview_session = overview_controller->overview_session();
   auto* overview_item =
@@ -144,7 +144,7 @@
   EXPECT_EQ(window.get(), window_util::GetActiveWindow());
 
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   auto* overview_session = overview_controller->overview_session();
   const auto* overview_grid =
@@ -196,7 +196,7 @@
   std::unique_ptr<aura::Window> window =
       CreateAppWindow(gfx::Rect(0, 0, 250, 100));
   auto* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   auto* overview_session = overview_controller->overview_session();
   auto* overview_item =
@@ -294,7 +294,7 @@
   void StartDraggingAndValidateDesksBarShifted(aura::Window* window) {
     // Enter overview mode, and start dragging the window. Validate that the
     // desks bar widget is shifted down to make room for the indicators.
-    overview_controller()->StartOverview();
+    EnterOverview();
     EXPECT_TRUE(overview_controller()->InOverviewSession());
     auto* overview_item = GetOverviewItemForWindow(window);
     ASSERT_TRUE(overview_item);
diff --git a/ash/wm/overview/overview_window_drag_histogram_unittests.cc b/ash/wm/overview/overview_window_drag_histogram_unittests.cc
index e4575ac..0954408 100644
--- a/ash/wm/overview/overview_window_drag_histogram_unittests.cc
+++ b/ash/wm/overview/overview_window_drag_histogram_unittests.cc
@@ -64,7 +64,7 @@
   }
 
   gfx::Point EnterOverviewAndGetItemCenterPoint() {
-    Shell::Get()->overview_controller()->StartOverview();
+    EnterOverview();
     return gfx::ToRoundedPoint(
         GetOverviewItemForWindow(window_.get())->target_bounds().CenterPoint());
   }
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index 961063db..4b3cdc4 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -483,7 +483,8 @@
     // just end overview mode which will then end splitview mode.
     // TODO(xdai): Handle this logic in OverivewSession::OnWindowActivating().
     if (split_view_controller_->InClamshellSplitViewMode()) {
-      Shell::Get()->overview_controller()->EndOverview();
+      Shell::Get()->overview_controller()->EndOverview(
+          OverviewEndAction::kSplitView);
       return;
     }
 
@@ -704,7 +705,6 @@
       split_view_metrics_controller_(
           std::make_unique<SplitViewMetricsController>(this)) {
   Shell::Get()->accessibility_controller()->AddObserver(this);
-  display::Screen::GetScreen()->AddObserver(this);
   Shell::Get()->tablet_mode_controller()->AddObserver(this);
   split_view_type_ = Shell::Get()->tablet_mode_controller()->InTabletMode()
                          ? SplitViewType::kTabletType
@@ -714,7 +714,6 @@
 SplitViewController::~SplitViewController() {
   if (Shell::Get()->tablet_mode_controller())
     Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
-  display::Screen::GetScreen()->RemoveObserver(this);
   if (Shell::Get()->accessibility_controller())
     Shell::Get()->accessibility_controller()->RemoveObserver(this);
   EndSplitView();
@@ -1263,7 +1262,8 @@
     DCHECK(IsWindowInSplitView(target_window));
     DCHECK(target_window);
     EndSplitView();
-    overview_controller->EndOverview();
+    overview_controller->EndOverview(
+        OverviewEndAction::kOverviewButtonLongPress);
     MaximizeIfSnapped(target_window);
     wm::ActivateWindow(target_window);
     base::RecordAction(
@@ -1278,7 +1278,9 @@
   }
 
   // Start overview mode if we aren't already in it.
-  overview_controller->StartOverview(OverviewEnterExitType::kImmediateEnter);
+  overview_controller->StartOverview(
+      OverviewStartAction::kOverviewButtonLongPress,
+      OverviewEnterExitType::kImmediateEnter);
 
   SnapWindow(target_window, SplitViewController::LEFT,
              /*activate_window=*/true);
@@ -1343,7 +1345,8 @@
     return;
 
   EndSplitView();
-  Shell::Get()->overview_controller()->EndOverview();
+  Shell::Get()->overview_controller()->EndOverview(
+      OverviewEndAction::kSplitView);
   ShowAppCannotSnapToast();
 }
 
@@ -1364,7 +1367,8 @@
     if (window_state->drag_details()->bounds_change ==
         WindowResizer::kBoundsChange_Repositions) {
       // Ending overview will also end clamshell split view.
-      Shell::Get()->overview_controller()->EndOverview();
+      Shell::Get()->overview_controller()->EndOverview(
+          OverviewEndAction::kSplitView);
       return;
     }
     DCHECK(window_state->drag_details()->bounds_change &
@@ -1405,7 +1409,8 @@
   if (WindowState::Get(window)->drag_details()->window_component !=
       GetWindowComponentForResize(window)) {
     // Ending overview will also end clamshell split view.
-    Shell::Get()->overview_controller()->EndOverview();
+    Shell::Get()->overview_controller()->EndOverview(
+        OverviewEndAction::kSplitView);
     return;
   }
 
@@ -1435,7 +1440,8 @@
   if (divider_position_ < GetDividerEndPosition() * kOneThirdPositionRatio ||
       divider_position_ > GetDividerEndPosition() * kTwoThirdPositionRatio) {
     // Ending overview will also end clamshell split view.
-    Shell::Get()->overview_controller()->EndOverview();
+    Shell::Get()->overview_controller()->EndOverview(
+        OverviewEndAction::kSplitView);
     WindowState::Get(window)->Maximize();
   }
 }
@@ -1477,7 +1483,8 @@
     // 3. A (clamshell or tablet) split view window gets maximized.
     // 4. A (clamshell or tablet) split view window becomes full screen.
     EndSplitView();
-    Shell::Get()->overview_controller()->EndOverview();
+    Shell::Get()->overview_controller()->EndOverview(
+        OverviewEndAction::kSplitView);
   } else if (window_state->IsMinimized()) {
     OnSnappedWindowDetached(window_state->window(),
                             WindowDetachedReason::kWindowMinimized);
@@ -1488,10 +1495,12 @@
       // the window is not supposed to be minmized in tablet mode. And in
       // clamshell splitview mode, we respect the minimization of the window
       // and end overview instead.
-      if (split_view_type_ == SplitViewType::kTabletType)
+      if (split_view_type_ == SplitViewType::kTabletType) {
         InsertWindowToOverview(window_state->window());
-      else
-        Shell::Get()->overview_controller()->EndOverview();
+      } else {
+        Shell::Get()->overview_controller()->EndOverview(
+            OverviewEndAction::kSplitView);
+      }
     }
   }
 }
@@ -1581,6 +1590,7 @@
   // are in overview (see https://crbug.com/1027179).
   if (state_ == State::kLeftSnapped || state_ == State::kRightSnapped) {
     Shell::Get()->overview_controller()->StartOverview(
+        OverviewStartAction::kSplitView,
         OverviewEnterExitType::kImmediateEnter);
   }
 }
@@ -1948,7 +1958,8 @@
     insert_overview_window = GetDefaultSnappedWindow();
   EndSplitView();
   if (active_window) {
-    Shell::Get()->overview_controller()->EndOverview();
+    Shell::Get()->overview_controller()->EndOverview(
+        OverviewEndAction::kSplitView);
     wm::ActivateWindow(active_window);
   } else if (insert_overview_window) {
     InsertWindowToOverview(insert_overview_window, /*animate=*/false);
@@ -1992,7 +2003,8 @@
   if (!overview_controller->InOverviewSession() &&
       split_view_type_ == SplitViewType::kTabletType &&
       (state_ == State::kLeftSnapped || state_ == State::kRightSnapped)) {
-    overview_controller->StartOverview(OverviewEnterExitType::kNormal);
+    overview_controller->StartOverview(OverviewStartAction::kSplitView,
+                                       OverviewEnterExitType::kNormal);
   }
 }
 
@@ -2035,6 +2047,7 @@
     default_snap_position_ = left_window_ ? LEFT : RIGHT;
     UpdateStateAndNotifyObservers();
     Shell::Get()->overview_controller()->StartOverview(
+        OverviewStartAction::kSplitView,
         reason == WindowDetachedReason::kWindowDragged
             ? OverviewEnterExitType::kImmediateEnter
             : OverviewEnterExitType::kNormal);
@@ -2302,7 +2315,8 @@
       // Activate the dragged window and end the overview. The dragged window
       // will be restored back to its previous state before dragging.
       wm::ActivateWindow(window);
-      Shell::Get()->overview_controller()->EndOverview();
+      Shell::Get()->overview_controller()->EndOverview(
+          OverviewEndAction::kSplitView);
 
       // Update the dragged window's bounds. It's possible that the dragged
       // window's bounds was changed during dragging. Update its bounds after
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h
index d8ab9ad..9dee64f 100644
--- a/ash/wm/splitview/split_view_controller.h
+++ b/ash/wm/splitview/split_view_controller.h
@@ -551,6 +551,9 @@
   // The metrics controller for the same root window.
   std::unique_ptr<SplitViewMetricsController> split_view_metrics_controller_;
 
+  // Register for DisplayObserver callbacks.
+  display::ScopedDisplayObserver display_observer_{this};
+
   // A pointer to the to-be-snapped window that will be activated after it's
   // snapped in splitview. There can be two cases when this value can be
   // non-nullptr, when SnapWindow() explicitly specifies the window needs to be
diff --git a/ash/wm/splitview/split_view_drag_indicators_unittest.cc b/ash/wm/splitview/split_view_drag_indicators_unittest.cc
index 460857c..4753f63 100644
--- a/ash/wm/splitview/split_view_drag_indicators_unittest.cc
+++ b/ash/wm/splitview/split_view_drag_indicators_unittest.cc
@@ -53,9 +53,9 @@
   void ToggleOverview() {
     auto* overview_controller = Shell::Get()->overview_controller();
     if (overview_controller->InOverviewSession())
-      overview_controller->EndOverview();
+      ExitOverview();
     else
-      overview_controller->StartOverview();
+      EnterOverview();
 
     if (!overview_controller->InOverviewSession()) {
       overview_session_ = nullptr;
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc
index b1f35eae..db892b0 100644
--- a/ash/wm/splitview/split_view_utils.cc
+++ b/ash/wm/splitview/split_view_utils.cc
@@ -385,9 +385,9 @@
   SplitViewController::State state = split_view_controller->state();
   if (state == SplitViewController::State::kLeftSnapped ||
       state == SplitViewController::State::kRightSnapped) {
-    overview_controller->StartOverview();
+    overview_controller->StartOverview(OverviewStartAction::kSplitView);
   } else {
-    overview_controller->EndOverview();
+    overview_controller->EndOverview(OverviewEndAction::kSplitView);
   }
 }
 
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc
index 983f365..5752b3e 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -1141,7 +1141,8 @@
       SplitViewController::Get(Shell::GetPrimaryRootWindow())->state();
   if (state == SplitViewController::State::kLeftSnapped ||
       state == SplitViewController::State::kRightSnapped) {
-    Shell::Get()->overview_controller()->StartOverview();
+    Shell::Get()->overview_controller()->StartOverview(
+        OverviewStartAction::kSplitView);
   }
 
   UpdateInternalInputDevicesEventBlocker();
diff --git a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
index a2c85649a..0c265d4b 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
@@ -617,7 +617,7 @@
   ASSERT_FALSE(IsTabletModeStarted());
 
   tablet_mode_controller()->SetEnabledForTest(true);
-  EXPECT_TRUE(Shell::Get()->overview_controller()->StartOverview());
+  EXPECT_TRUE(EnterOverview());
 
   UpdateDisplay("800x600");
   base::RunLoop().RunUntilIdle();
@@ -1776,7 +1776,7 @@
   window2->layer()->GetAnimator()->StopAnimating();
 
   // Enter overview.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   ShellTestApi().WaitForOverviewAnimationState(
       OverviewAnimationState::kEnterAnimationComplete);
 
diff --git a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
index bbfd64782..27b36e4d 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
@@ -174,7 +174,8 @@
             ->overview_button_tray();
     DCHECK(overview_button_tray);
     overview_button_tray->SnapRippleToActivated();
-    controller->StartOverview(OverviewEnterExitType::kImmediateEnter);
+    controller->StartOverview(OverviewStartAction::kSplitView,
+                              OverviewEnterExitType::kImmediateEnter);
   }
 
   if (controller->InOverviewSession()) {
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.cc b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
index 2dc6496..4fc424e 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
@@ -161,7 +161,7 @@
     ArrangeWindowsForTabletMode();
   }
   AddWindowCreationObservers();
-  display::Screen::GetScreen()->AddObserver(this);
+  display_observer_.emplace(this);
   SplitViewController::Get(Shell::GetPrimaryRootWindow())->AddObserver(this);
   Shell::Get()->session_controller()->AddObserver(this);
   Shell::Get()->overview_controller()->AddObserver(this);
@@ -207,7 +207,7 @@
         overview_controller->overview_session()->IsEmpty()) {
       split_view_controller->EndSplitView(
           SplitViewController::EndReason::kExitTabletMode);
-      overview_controller->EndOverview();
+      overview_controller->EndOverview(OverviewEndAction::kSplitView);
     }
   }
 
@@ -217,7 +217,7 @@
   split_view_controller->RemoveObserver(this);
   Shell::Get()->session_controller()->RemoveObserver(this);
   Shell::Get()->overview_controller()->RemoveObserver(this);
-  display::Screen::GetScreen()->RemoveObserver(this);
+  display_observer_.reset();
   RemoveWindowCreationObservers();
 
   ScopedObserveWindowAnimation scoped_observe(window_util::GetTopWindow(), this,
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.h b/ash/wm/tablet_mode/tablet_mode_window_manager.h
index 724c1e9..a0bd695 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.h
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.h
@@ -188,6 +188,8 @@
 
   std::unique_ptr<TabletModeToggleFullscreenEventHandler> event_handler_;
 
+  absl::optional<display::ScopedDisplayObserver> display_observer_;
+
   // True when tablet mode is about to end.
   bool is_exiting_ = false;
 
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
index c60537d..df4d93d 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
@@ -1551,13 +1551,12 @@
   EXPECT_TRUE(window_state->IsMinimized());
   EXPECT_EQ(window->bounds(), rect);
 
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_EQ(window->bounds(), rect);
 
   // Exit overview mode will update all windows' bounds. However, if the window
   // is minimized, the bounds will not be updated.
-  overview_controller->EndOverview();
+  ExitOverview();
   EXPECT_EQ(window->bounds(), rect);
 }
 
@@ -1623,7 +1622,7 @@
   // 1. Clamshell -> tablet. If overview is active, it should still be kept
   // active after transition.
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  EXPECT_TRUE(overview_controller->StartOverview());
+  EXPECT_TRUE(EnterOverview());
   EXPECT_TRUE(overview_controller->InOverviewSession());
   TabletModeWindowManager* manager = CreateTabletModeWindowManager();
   EXPECT_TRUE(manager);
@@ -1636,7 +1635,7 @@
 
   // 3. Clamshell -> tablet. If overview is inactive, it should still be kept
   // inactive after transition. All windows will be maximized.
-  EXPECT_TRUE(overview_controller->EndOverview());
+  EXPECT_TRUE(ExitOverview());
   EXPECT_FALSE(overview_controller->InOverviewSession());
   CreateTabletModeWindowManager();
   EXPECT_FALSE(overview_controller->InOverviewSession());
@@ -1698,7 +1697,7 @@
 
   // 10. Tablet -> Clamshell. If overview and splitview are both active, after
   // transition, they will remain both active.
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(split_view_controller()->InSplitViewMode());
   EXPECT_TRUE(overview_controller->InOverviewSession());
   DestroyTabletModeWindowManager();
@@ -1777,7 +1776,7 @@
   // Clamshell -> Tablet mode transition. If overview is active, it will remain
   // in overview.
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  EXPECT_TRUE(overview_controller->StartOverview());
+  EXPECT_TRUE(EnterOverview());
   EXPECT_TRUE(overview_controller->InOverviewSession());
   TabletModeWindowManager* manager = CreateTabletModeWindowManager();
   EXPECT_TRUE(manager);
diff --git a/ash/wm/toplevel_window_event_handler.cc b/ash/wm/toplevel_window_event_handler.cc
index f8cf986e..43e8764e 100644
--- a/ash/wm/toplevel_window_event_handler.cc
+++ b/ash/wm/toplevel_window_event_handler.cc
@@ -205,11 +205,9 @@
 ToplevelWindowEventHandler::ToplevelWindowEventHandler()
     : first_finger_hittest_(HTNOWHERE) {
   Shell::Get()->window_tree_host_manager()->AddObserver(this);
-  display::Screen::GetScreen()->AddObserver(this);
 }
 
 ToplevelWindowEventHandler::~ToplevelWindowEventHandler() {
-  display::Screen::GetScreen()->RemoveObserver(this);
   Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
 }
 
diff --git a/ash/wm/toplevel_window_event_handler.h b/ash/wm/toplevel_window_event_handler.h
index 8d9bd16..c2792521 100644
--- a/ash/wm/toplevel_window_event_handler.h
+++ b/ash/wm/toplevel_window_event_handler.h
@@ -176,6 +176,8 @@
 
   std::unique_ptr<ScopedWindowResizer> window_resizer_;
 
+  display::ScopedDisplayObserver display_observer_{this};
+
   EndClosure end_closure_;
 
   // Are we running a nested run loop from RunMoveLoop().
diff --git a/ash/wm/window_cycle/window_cycle_controller.cc b/ash/wm/window_cycle/window_cycle_controller.cc
index 8ce71f6..56654d6 100644
--- a/ash/wm/window_cycle/window_cycle_controller.cc
+++ b/ash/wm/window_cycle/window_cycle_controller.cc
@@ -236,7 +236,8 @@
   shell->event_rewriter_controller()->SetAltDownRemappingEnabled(false);
 
   // End overview as the window cycle list takes over window switching.
-  shell->overview_controller()->EndOverview();
+  shell->overview_controller()->EndOverview(
+      OverviewEndAction::kStartedWindowCycle);
 
   WindowCycleController::WindowList window_list = CreateWindowList();
   SaveCurrentActiveDeskAndWindow(window_list);
diff --git a/ash/wm/window_cycle/window_cycle_controller_unittest.cc b/ash/wm/window_cycle/window_cycle_controller_unittest.cc
index 3d9e4f6..be25b55d 100644
--- a/ash/wm/window_cycle/window_cycle_controller_unittest.cc
+++ b/ash/wm/window_cycle/window_cycle_controller_unittest.cc
@@ -1360,7 +1360,7 @@
   EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
 
   // Open an overview session.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(InOverviewSession());
 
   // Open the window cycle list. Scroll right to second item. Overview mode
diff --git a/ash/wm/window_cycle/window_cycle_list.cc b/ash/wm/window_cycle/window_cycle_list.cc
index 9df3d9c..2dc13df 100644
--- a/ash/wm/window_cycle/window_cycle_list.cc
+++ b/ash/wm/window_cycle/window_cycle_list.cc
@@ -1161,7 +1161,6 @@
   params.parent = root_window->GetChildById(kShellWindowId_OverlayContainer);
   params.bounds = cycle_view_->GetTargetBounds();
 
-  screen_observer_.Observe(display::Screen::GetScreen());
   widget->Init(std::move(params));
   widget->Show();
   cycle_view_->FadeInLayer();
diff --git a/ash/wm/window_cycle/window_cycle_list.h b/ash/wm/window_cycle/window_cycle_list.h
index 75af85e..1783a807 100644
--- a/ash/wm/window_cycle/window_cycle_list.h
+++ b/ash/wm/window_cycle/window_cycle_list.h
@@ -11,7 +11,6 @@
 #include "ash/ash_export.h"
 #include "ash/wm/window_cycle/window_cycle_controller.h"
 #include "ash/wm/window_cycle/window_cycle_tab_slider.h"
-#include "base/scoped_observation.h"
 #include "base/timer/timer.h"
 #include "ui/aura/window_observer.h"
 #include "ui/display/display_observer.h"
@@ -186,8 +185,7 @@
   views::Widget* cycle_ui_widget_ = nullptr;
 
   // The window list will dismiss if the display metrics change.
-  base::ScopedObservation<display::Screen, display::DisplayObserver>
-      screen_observer_{this};
+  display::ScopedDisplayObserver display_observer_{this};
 
   // A timer to delay showing the UI. Quick Alt+Tab should not flash a UI.
   base::OneShotTimer show_ui_timer_;
diff --git a/ash/wm/window_finder_unittest.cc b/ash/wm/window_finder_unittest.cc
index a42b8d8..fd00728 100644
--- a/ash/wm/window_finder_unittest.cc
+++ b/ash/wm/window_finder_unittest.cc
@@ -88,7 +88,7 @@
       CreateTestWindow(gfx::Rect(0, 0, 100, 100));
 
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  overview_controller->StartOverview();
+  EnterOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
 
   // Get |window1| and |window2|'s transformed bounds in overview.
diff --git a/ash/wm/workspace/multi_window_resize_controller_unittest.cc b/ash/wm/workspace/multi_window_resize_controller_unittest.cc
index 40fb7be..3c7323bd 100644
--- a/ash/wm/workspace/multi_window_resize_controller_unittest.cc
+++ b/ash/wm/workspace/multi_window_resize_controller_unittest.cc
@@ -667,7 +667,7 @@
   EXPECT_TRUE(IsShowing());
 
   // Tests that after starting overview, the widget is hidden.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   EXPECT_FALSE(HasPendingShow());
   EXPECT_FALSE(IsShowing());
 }
diff --git a/ash/wm/workspace/workspace_event_handler.cc b/ash/wm/workspace/workspace_event_handler.cc
index 550b5ee..f6626a4 100644
--- a/ash/wm/workspace/workspace_event_handler.cc
+++ b/ash/wm/workspace/workspace_event_handler.cc
@@ -143,7 +143,7 @@
         DCHECK_EQ(gfx::Size(), target->delegate()->GetMaximumSize());
         overview_controller->overview_session()
             ->SetWindowListNotAnimatedWhenExiting(target->GetRootWindow());
-        overview_controller->EndOverview();
+        overview_controller->EndOverview(OverviewEndAction::kSplitView);
       }
 
       const WMEvent wm_event(WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE);
diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc
index 29d9c37..49a98dc0 100644
--- a/ash/wm/workspace/workspace_layout_manager.cc
+++ b/ash/wm/workspace/workspace_layout_manager.cc
@@ -108,7 +108,6 @@
   Shell::Get()->AddShellObserver(this);
   Shell::Get()->activation_client()->AddObserver(this);
   root_window_->AddObserver(this);
-  display::Screen::GetScreen()->AddObserver(this);
   backdrop_controller_ = std::make_unique<BackdropController>(window_);
   keyboard::KeyboardUIController::Get()->AddObserver(this);
   settings_bubble_container_ = window->GetRootWindow()->GetChildById(
@@ -131,7 +130,6 @@
     window_state->RemoveObserver(this);
     window->RemoveObserver(this);
   }
-  display::Screen::GetScreen()->RemoveObserver(this);
   Shell::Get()->activation_client()->RemoveObserver(this);
   Shell::Get()->RemoveShellObserver(this);
   keyboard::KeyboardUIController::Get()->RemoveObserver(this);
diff --git a/ash/wm/workspace/workspace_layout_manager.h b/ash/wm/workspace/workspace_layout_manager.h
index dc8e0da..15b5e78d 100644
--- a/ash/wm/workspace/workspace_layout_manager.h
+++ b/ash/wm/workspace/workspace_layout_manager.h
@@ -169,6 +169,8 @@
   aura::Window* accessibility_bubble_container_;
   BubbleWindowObserver accessibility_bubble_window_observer_;
 
+  display::ScopedDisplayObserver display_observer_{this};
+
   // Set of windows we're listening to.
   WindowSet windows_;
 
diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc
index 4ce3ef18..149190e 100644
--- a/ash/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc
@@ -1379,13 +1379,13 @@
   }
 
   // Toggle overview.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   base::RunLoop().RunUntilIdle();
   backdrop = test_helper.GetBackdropWindow();
   ASSERT_TRUE(backdrop);
   EXPECT_FALSE(backdrop->IsVisible());
 
-  Shell::Get()->overview_controller()->EndOverview();
+  ExitOverview();
   base::RunLoop().RunUntilIdle();
   backdrop = test_helper.GetBackdropWindow();
   ASSERT_TRUE(backdrop);
@@ -1423,12 +1423,12 @@
   }
 
   // Toggle overview with the delegate.
-  Shell::Get()->overview_controller()->StartOverview();
+  EnterOverview();
   base::RunLoop().RunUntilIdle();
   backdrop = test_helper.GetBackdropWindow();
   ASSERT_TRUE(backdrop);
   EXPECT_FALSE(backdrop->IsVisible());
-  Shell::Get()->overview_controller()->EndOverview();
+  ExitOverview();
   base::RunLoop().RunUntilIdle();
   backdrop = test_helper.GetBackdropWindow();
   ASSERT_TRUE(backdrop);
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 565e289..a8f821d 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3662,6 +3662,7 @@
       "memory/weak_ptr_unittest.nc",
       "metrics/field_trial_params_unittest.nc",
       "metrics/histogram_unittest.nc",
+      "no_destructor_unittest.nc",
       "observer_list_unittest.nc",
       "sequence_checker_unittest.nc",
       "task/task_traits_extension_unittest.nc",
diff --git a/base/android/android_hardware_buffer_compat.cc b/base/android/android_hardware_buffer_compat.cc
index 0f9dd0b..88feb411 100644
--- a/base/android/android_hardware_buffer_compat.cc
+++ b/base/android/android_hardware_buffer_compat.cc
@@ -64,8 +64,8 @@
 
 // static
 AndroidHardwareBufferCompat& AndroidHardwareBufferCompat::GetInstance() {
-  static base::NoDestructor<AndroidHardwareBufferCompat> compat;
-  return *compat;
+  static AndroidHardwareBufferCompat compat;
+  return compat;
 }
 
 void AndroidHardwareBufferCompat::Allocate(const AHardwareBuffer_Desc* desc,
diff --git a/base/android/android_image_reader_compat.cc b/base/android/android_image_reader_compat.cc
index 4893446..ede7c576 100644
--- a/base/android/android_image_reader_compat.cc
+++ b/base/android/android_image_reader_compat.cc
@@ -26,8 +26,8 @@
 AndroidImageReader& AndroidImageReader::GetInstance() {
   // C++11 static local variable initialization is
   // thread-safe.
-  static base::NoDestructor<AndroidImageReader> instance;
-  return *instance;
+  static AndroidImageReader instance;
+  return instance;
 }
 
 bool AndroidImageReader::IsSupported() {
diff --git a/base/android/reached_addresses_bitset.cc b/base/android/reached_addresses_bitset.cc
index e4fc1d1e..295635b 100644
--- a/base/android/reached_addresses_bitset.cc
+++ b/base/android/reached_addresses_bitset.cc
@@ -7,7 +7,6 @@
 #include "base/android/library_loader/anchor_functions.h"
 #include "base/android/library_loader/anchor_functions_buildflags.h"
 #include "base/check_op.h"
-#include "base/no_destructor.h"
 
 namespace base {
 namespace android {
@@ -25,9 +24,9 @@
 // static
 ReachedAddressesBitset* ReachedAddressesBitset::GetTextBitset() {
 #if BUILDFLAG(SUPPORTS_CODE_ORDERING)
-  static base::NoDestructor<ReachedAddressesBitset> text_bitset(
-      kStartOfText, kEndOfText, g_text_bitfield, kTextBitfieldSize);
-  return text_bitset.get();
+  static ReachedAddressesBitset text_bitset(kStartOfText, kEndOfText,
+                                            g_text_bitfield, kTextBitfieldSize);
+  return &text_bitset;
 #else
   return nullptr;
 #endif
diff --git a/base/android/reached_addresses_bitset.h b/base/android/reached_addresses_bitset.h
index 1f4b8927..03c3a0e 100644
--- a/base/android/reached_addresses_bitset.h
+++ b/base/android/reached_addresses_bitset.h
@@ -9,12 +9,10 @@
 #include <vector>
 
 #include "base/base_export.h"
+#include "base/no_destructor.h"
 
 namespace base {
 
-template <typename T>
-class NoDestructor;
-
 namespace android {
 
 // ReachedAddressesBitset is a set that stores addresses for the
diff --git a/base/linux_util.cc b/base/linux_util.cc
index c1512b84..ee511bd 100644
--- a/base/linux_util.cc
+++ b/base/linux_util.cc
@@ -19,7 +19,6 @@
 #include "base/files/dir_reader_posix.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
-#include "base/no_destructor.h"
 #include "base/strings/safe_sprintf.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
@@ -123,7 +122,7 @@
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
   // We do this check only once per process. If it fails, there's
   // little reason to believe it will work if we attempt to run it again.
-  static NoDestructor<DistroNameGetter> distro_name_getter;
+  static DistroNameGetter distro_name_getter;
 #endif
   return g_linux_distro;
 }
diff --git a/base/memory/checked_ptr.h b/base/memory/checked_ptr.h
index 284f457..b68bffd 100644
--- a/base/memory/checked_ptr.h
+++ b/base/memory/checked_ptr.h
@@ -9,11 +9,11 @@
 #include <stdint.h>
 
 #include <cstddef>
+#include <type_traits>
 #include <utility>
 
 #include "base/allocator/buildflags.h"
-#include "base/allocator/partition_allocator/partition_alloc_config.h"
-#include "base/check_op.h"
+#include "base/check.h"
 #include "base/compiler_specific.h"
 #include "build/build_config.h"
 #include "build/buildflag.h"
@@ -23,6 +23,7 @@
 #if BUILDFLAG(USE_BACKUP_REF_PTR)
 #include "base/allocator/partition_allocator/address_pool_manager_bitmap.h"
 #include "base/allocator/partition_allocator/partition_address_space.h"
+#include "base/allocator/partition_allocator/partition_alloc_config.h"
 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
 #include "base/allocator/partition_allocator/partition_alloc_forward.h"
 #include "base/allocator/partition_allocator/partition_ref_count.h"
diff --git a/base/memory/nonscannable_memory.h b/base/memory/nonscannable_memory.h
index dff6507..16500b5 100644
--- a/base/memory/nonscannable_memory.h
+++ b/base/memory/nonscannable_memory.h
@@ -12,6 +12,7 @@
 
 #include "base/allocator/buildflags.h"
 #include "base/base_export.h"
+#include "base/no_destructor.h"
 
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 #include "base/allocator/partition_allocator/partition_alloc.h"
@@ -25,9 +26,6 @@
 namespace base {
 
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
-template <typename>
-class NoDestructor;
-
 namespace internal {
 
 // Represents allocator that contains memory for data-like objects (that don't
@@ -53,7 +51,7 @@
   void EnablePCScan();
 
  private:
-  template <typename>
+  template <typename, typename>
   friend class base::NoDestructor;
 
   NonScannableAllocator();
diff --git a/base/no_destructor.h b/base/no_destructor.h
index 21cfef8..be083ff 100644
--- a/base/no_destructor.h
+++ b/base/no_destructor.h
@@ -6,9 +6,15 @@
 #define BASE_NO_DESTRUCTOR_H_
 
 #include <new>
+#include <type_traits>
 #include <utility>
 
 namespace base {
+// A tag type used for NoDestructor to allow it to be created for a type that
+// has a trivial destructor. Use for cases where the same class might have
+// different implementations that vary on destructor triviality or when the
+// LSan hiding properties of NoDestructor are needed.
+struct AllowForTriviallyDestructibleType;
 
 // A wrapper that makes it easy to create an object of type T with static
 // storage duration that:
@@ -44,9 +50,20 @@
 // Note that since the destructor is never run, this *will* leak memory if used
 // as a stack or member variable. Furthermore, a NoDestructor<T> should never
 // have global scope as that may require a static initializer.
-template <typename T>
+template <typename T, typename O = std::nullptr_t>
 class NoDestructor {
  public:
+  static_assert(
+      !std::is_trivially_destructible<T>::value ||
+          std::is_same<O, AllowForTriviallyDestructibleType>::value,
+      "base::NoDestructor is not needed because the templated class has a "
+      "trivial destructor");
+
+  static_assert(std::is_same<O, AllowForTriviallyDestructibleType>::value ||
+                    std::is_same<O, std::nullptr_t>::value,
+                "AllowForTriviallyDestructibleType is the only valid option "
+                "for the second template parameter of NoDestructor");
+
   // Not constexpr; just write static constexpr T x = ...; if the value should
   // be a constexpr.
   template <typename... Args>
diff --git a/base/no_destructor_unittest.cc b/base/no_destructor_unittest.cc
index 5c83366..ebe19be2 100644
--- a/base/no_destructor_unittest.cc
+++ b/base/no_destructor_unittest.cc
@@ -39,6 +39,7 @@
   UncopyableUnmovable& operator=(const UncopyableUnmovable&) = delete;
 
   int value = 1;
+  std::string something_with_a_nontrivial_destructor;
 };
 
 struct CopyOnly {
@@ -63,6 +64,8 @@
 
 struct ForwardingTestStruct {
   ForwardingTestStruct(const CopyOnly&, MoveOnly&&) {}
+
+  std::string something_with_a_nontrivial_destructor;
 };
 
 TEST(NoDestructorTest, UncopyableUnmovable) {
@@ -89,6 +92,11 @@
   EXPECT_EQ(0, awesome.get()->compare("awesome"));
 }
 
+TEST(NoDestructorTest, AllowForTriviallyDestructibleType) {
+  static NoDestructor<bool, AllowForTriviallyDestructibleType>
+      trivially_destructible_type;
+}
+
 // Passing initializer list to a NoDestructor like in this test
 // is ambiguous in GCC.
 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84849
diff --git a/base/no_destructor_unittest.nc b/base/no_destructor_unittest.nc
new file mode 100644
index 0000000..30b7dbd3
--- /dev/null
+++ b/base/no_destructor_unittest.nc
@@ -0,0 +1,30 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a "No Compile Test" suite.
+// http://dev.chromium.org/developers/testing/no-compile-tests
+
+#include "base/no_destructor.h"
+
+#include <string>
+
+namespace base {
+
+#if defined(NCTEST_NODESTRUCTOR_REQUIRES_NONTRIVIAL_DESTRUCTOR) // [r"fatal error: static_assert failed due to requirement '!std::is_trivially_destructible<bool>::value || std::is_same<nullptr_t, base::AllowForTriviallyDestructibleType>::value' \"base::NoDestructor is not needed because the templated class has a trivial destructor\""]
+
+// Attempt to make a NoDestructor for a type with a trivial destructor.
+void WontCompile() {
+  NoDestructor<bool> nd;
+}
+
+#elif defined(NCTEST_NODESTRUCTOR_PARAMETER) // [r"fatal error: static_assert failed due to requirement 'std::is_same<std::string, base::AllowForTriviallyDestructibleType>::value || std::is_same<std::string, nullptr_t>::value' \"AllowForTriviallyDestructibleType is the only valid option for the second template parameter of NoDestructor\""]
+
+// Attempt to make a NoDestructor for a type with an invalid option.
+void WontCompile() {
+  NoDestructor<std::string, std::string> nd;
+}
+
+#endif
+
+}  // namespace base
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.h b/base/sampling_heap_profiler/poisson_allocation_sampler.h
index f0f06a6..52ae551 100644
--- a/base/sampling_heap_profiler/poisson_allocation_sampler.h
+++ b/base/sampling_heap_profiler/poisson_allocation_sampler.h
@@ -10,15 +10,13 @@
 #include "base/base_export.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/sampling_heap_profiler/lock_free_address_hash_set.h"
 #include "base/synchronization/lock.h"
 #include "base/thread_annotations.h"
 
 namespace base {
 
-template <typename T>
-class NoDestructor;
-
 // This singleton class implements Poisson sampling of the incoming allocations
 // stream. It hooks onto base::allocator and base::PartitionAlloc.
 // An extra custom allocator can be hooked via SetHooksInstallCallback method.
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.h b/base/sampling_heap_profiler/sampling_heap_profiler.h
index 34b7cab..9908c19 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler.h
+++ b/base/sampling_heap_profiler/sampling_heap_profiler.h
@@ -12,6 +12,7 @@
 
 #include "base/base_export.h"
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/sampling_heap_profiler/poisson_allocation_sampler.h"
 #include "base/synchronization/lock.h"
 #include "base/thread_annotations.h"
@@ -19,9 +20,6 @@
 
 namespace base {
 
-template <typename T>
-class NoDestructor;
-
 // The class implements sampling profiling of native memory heap.
 // It uses PoissonAllocationSampler to aggregate the heap allocations and
 // record samples.
diff --git a/base/strings/string_number_conversions_internal.h b/base/strings/string_number_conversions_internal.h
index d35f3cf..ffc50f8 100644
--- a/base/strings/string_number_conversions_internal.h
+++ b/base/strings/string_number_conversions_internal.h
@@ -14,7 +14,6 @@
 
 #include "base/check_op.h"
 #include "base/logging.h"
-#include "base/no_destructor.h"
 #include "base/numerics/safe_math.h"
 #include "base/strings/string_util.h"
 #include "base/third_party/double_conversion/double-conversion/double-conversion.h"
@@ -229,10 +228,10 @@
 
 static const double_conversion::DoubleToStringConverter*
 GetDoubleToStringConverter() {
-  static NoDestructor<double_conversion::DoubleToStringConverter> converter(
+  static double_conversion::DoubleToStringConverter converter(
       double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
       nullptr, nullptr, 'e', -6, 12, 0, 0);
-  return converter.get();
+  return &converter;
 }
 
 // Converts a given (data, size) pair to a desired string type. For
@@ -258,14 +257,14 @@
 
 template <typename STRING, typename CHAR>
 bool StringToDoubleImpl(STRING input, const CHAR* data, double& output) {
-  static NoDestructor<double_conversion::StringToDoubleConverter> converter(
+  static double_conversion::StringToDoubleConverter converter(
       double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES |
           double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK,
       0.0, 0, nullptr, nullptr);
 
   int processed_characters_count;
-  output = converter->StringToDouble(data, input.size(),
-                                     &processed_characters_count);
+  output =
+      converter.StringToDouble(data, input.size(), &processed_characters_count);
 
   // Cases to return false:
   //  - If the input string is empty, there was nothing to parse.
diff --git a/base/system/sys_info.cc b/base/system/sys_info.cc
index 81edd158..b0e5f815 100644
--- a/base/system/sys_info.cc
+++ b/base/system/sys_info.cc
@@ -11,7 +11,6 @@
 #include "base/callback.h"
 #include "base/command_line.h"
 #include "base/location.h"
-#include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "base/system/sys_info_internal.h"
 #include "base/task/post_task.h"
@@ -76,10 +75,8 @@
 
 // static
 bool SysInfo::IsLowEndDeviceImpl() {
-  static base::NoDestructor<
-      internal::LazySysInfoValue<bool, DetectLowEndDevice>>
-      instance;
-  return instance->value();
+  static internal::LazySysInfoValue<bool, DetectLowEndDevice> instance;
+  return instance.value();
 }
 #endif
 
diff --git a/base/task/thread_pool/worker_thread.cc b/base/task/thread_pool/worker_thread.cc
index 9165822..3787c58 100644
--- a/base/task/thread_pool/worker_thread.cc
+++ b/base/task/thread_pool/worker_thread.cc
@@ -364,10 +364,23 @@
       continue;
     }
 
+    // Alias pointer for investigation of memory corruption. crbug.com/1218384
+    TaskSource* task_source_before_run = task_source.get();
+    base::debug::Alias(&task_source_before_run);
+
     task_source = task_tracker_->RunAndPopNextTask(std::move(task_source));
 
+    // Alias pointer for investigation of memory corruption. crbug.com/1218384
+    TaskSource* task_source_before_move = task_source.get();
+    base::debug::Alias(&task_source_before_move);
+
     delegate_->DidProcessTask(std::move(task_source));
 
+    // Check that task_source is always cleared, to help investigation of memory
+    // corruption where task_source is non-null after being moved.
+    // crbug.com/1218384
+    CHECK(!task_source);
+
     // Calling WakeUp() guarantees that this WorkerThread will run Tasks from
     // TaskSources returned by the GetWork() method of |delegate_| until it
     // returns nullptr. Resetting |wake_up_event_| here doesn't break this
diff --git a/base/threading/platform_thread_posix.cc b/base/threading/platform_thread_posix.cc
index dbf96f6..d37b50b 100644
--- a/base/threading/platform_thread_posix.cc
+++ b/base/threading/platform_thread_posix.cc
@@ -19,7 +19,6 @@
 #include "base/debug/activity_tracker.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
-#include "base/no_destructor.h"
 #include "base/threading/platform_thread_internal_posix.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/threading/thread_id_name_manager.h"
@@ -195,7 +194,7 @@
 #if defined(OS_APPLE)
   return pthread_mach_thread_np(pthread_self());
 #elif defined(OS_LINUX) || defined(OS_CHROMEOS)
-  static NoDestructor<InitAtFork> init_at_fork;
+  static InitAtFork init_at_fork;
   if (g_thread_id == -1) {
     g_thread_id = syscall(__NR_gettid);
   } else {
diff --git a/base/threading/thread.cc b/base/threading/thread.cc
index 8834896..0454b1b0 100644
--- a/base/threading/thread.cc
+++ b/base/threading/thread.cc
@@ -122,6 +122,22 @@
   other.moved_from = true;
 }
 
+Thread::Options& Thread::Options::operator=(Thread::Options&& other) {
+  DCHECK_NE(this, &other);
+
+  message_pump_type = std::move(other.message_pump_type);
+  delegate = std::move(other.delegate);
+  timer_slack = std::move(other.timer_slack);
+  task_queue_time_domain = std::move(other.task_queue_time_domain);
+  message_pump_factory = std::move(other.message_pump_factory);
+  stack_size = std::move(other.stack_size);
+  priority = std::move(other.priority);
+  joinable = std::move(other.joinable);
+  other.moved_from = true;
+
+  return *this;
+}
+
 Thread::Options::~Options() = default;
 
 Thread::Thread(const std::string& name)
@@ -149,10 +165,10 @@
   if (com_status_ == STA)
     options.message_pump_type = MessagePumpType::UI;
 #endif
-  return StartWithOptions(options);
+  return StartWithOptions(std::move(options));
 }
 
-bool Thread::StartWithOptions(const Options& options) {
+bool Thread::StartWithOptions(Options options) {
   DCHECK(options.IsValid());
   DCHECK(owning_sequence_checker_.CalledOnValidSequence());
   DCHECK(!delegate_);
@@ -175,7 +191,7 @@
   if (options.delegate) {
     DCHECK(!options.message_pump_factory);
     DCHECK(!options.task_queue_time_domain);
-    delegate_ = WrapUnique(options.delegate);
+    delegate_ = std::move(options.delegate);
   } else if (options.message_pump_factory) {
     delegate_ = std::make_unique<SequenceManagerThreadDelegate>(
         MessagePumpType::CUSTOM, options.message_pump_factory,
diff --git a/base/threading/thread.h b/base/threading/thread.h
index 72af7e3..27ef5ff 100644
--- a/base/threading/thread.h
+++ b/base/threading/thread.h
@@ -80,7 +80,7 @@
     Options();
     Options(MessagePumpType type, size_t size);
     Options(Options&& other);
-    Options& operator=(const Options&& other) = delete;
+    Options& operator=(Options&& other);
     ~Options();
 
     // Specifies the type of message pump that will be allocated on the thread.
@@ -89,8 +89,7 @@
 
     // An unbound Delegate that will be bound to the thread. Ownership
     // of |delegate| will be transferred to the thread.
-    // TODO(alexclarke): This should be a std::unique_ptr
-    Delegate* delegate = nullptr;
+    std::unique_ptr<Delegate> delegate = nullptr;
 
     // Specifies timer slack for thread message loop.
     TimerSlack timer_slack = TIMER_SLACK_NONE;
@@ -171,7 +170,7 @@
   // Note: This function can't be called on Windows with the loader lock held;
   // i.e. during a DllMain, global object construction or destruction, atexit()
   // callback.
-  bool StartWithOptions(const Options& options);
+  bool StartWithOptions(Options options);
 
   // Starts the thread and wait for the thread to start and run initialization
   // before returning. It's same as calling Start() and then
diff --git a/base/threading/thread_unittest.cc b/base/threading/thread_unittest.cc
index 1a71f85..229bcf3 100644
--- a/base/threading/thread_unittest.cc
+++ b/base/threading/thread_unittest.cc
@@ -157,7 +157,7 @@
 #else
   options.stack_size = 3072 * sizeof(uintptr_t);
 #endif
-  EXPECT_TRUE(a.StartWithOptions(options));
+  EXPECT_TRUE(a.StartWithOptions(std::move(options)));
   EXPECT_TRUE(a.task_runner());
   EXPECT_TRUE(a.IsRunning());
 
@@ -180,7 +180,7 @@
 
   Thread::Options options;
   options.joinable = false;
-  EXPECT_TRUE(a->StartWithOptions(options));
+  EXPECT_TRUE(a->StartWithOptions(std::move(options)));
   EXPECT_TRUE(a->task_runner());
   EXPECT_TRUE(a->IsRunning());
 
@@ -246,7 +246,7 @@
     Thread a("DestroyWhileRunningNonJoinableIsSafe");
     Thread::Options options;
     options.joinable = false;
-    EXPECT_TRUE(a.StartWithOptions(options));
+    EXPECT_TRUE(a.StartWithOptions(std::move(options)));
     EXPECT_TRUE(a.WaitUntilThreadStarted());
   }
 
@@ -362,7 +362,7 @@
 
   Thread::Options options;
   options.joinable = false;
-  EXPECT_TRUE(a->StartWithOptions(options));
+  EXPECT_TRUE(a->StartWithOptions(std::move(options)));
   EXPECT_TRUE(a->task_runner());
   EXPECT_TRUE(a->IsRunning());
 
@@ -585,14 +585,15 @@
 TEST_F(ThreadTest, ProvidedThreadDelegate) {
   Thread thread("ThreadDelegate");
   base::Thread::Options options;
-  options.delegate = new SequenceManagerThreadDelegate();
-  thread.StartWithOptions(options);
+  options.delegate = std::make_unique<SequenceManagerThreadDelegate>();
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      options.delegate->GetDefaultTaskRunner();
+  thread.StartWithOptions(std::move(options));
 
   base::WaitableEvent event;
-
-  options.delegate->GetDefaultTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&event)));
+  task_runner->PostTask(FROM_HERE, base::BindOnce(&base::WaitableEvent::Signal,
+                                                  base::Unretained(&event)));
   event.Wait();
 
   thread.Stop();
diff --git a/base/time/time.cc b/base/time/time.cc
index d098b444..e8355ae 100644
--- a/base/time/time.cc
+++ b/base/time/time.cc
@@ -17,7 +17,6 @@
 #include <tuple>
 #include <utility>
 
-#include "base/no_destructor.h"
 #include "base/strings/stringprintf.h"
 #include "base/third_party/nspr/prtime.h"
 #include "base/time/time_override.h"
@@ -334,11 +333,11 @@
 
 // static
 TimeTicks TimeTicks::UnixEpoch() {
-  static const NoDestructor<TimeTicks> epoch([]() {
+  static const TimeTicks epoch([]() {
     return subtle::TimeTicksNowIgnoringOverride() -
            (subtle::TimeNowIgnoringOverride() - Time::UnixEpoch());
   }());
-  return *epoch;
+  return epoch;
 }
 
 TimeTicks TimeTicks::SnappedToNextTick(TimeTicks tick_phase,
diff --git a/base/trace_event/trace_log.h b/base/trace_event/trace_log.h
index 8e6c712..bbf35dc 100644
--- a/base/trace_event/trace_log.h
+++ b/base/trace_event/trace_log.h
@@ -18,6 +18,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/no_destructor.h"
 #include "base/single_thread_task_runner.h"
 #include "base/time/time_override.h"
 #include "base/trace_event/category_registry.h"
@@ -36,9 +37,6 @@
 namespace base {
 class RefCountedString;
 
-template <typename T>
-class NoDestructor;
-
 namespace tracing {
 class PerfettoPlatform;
 }  // namespace tracing
diff --git a/base/unguessable_token.cc b/base/unguessable_token.cc
index 432a2f2b..f74579e3e 100644
--- a/base/unguessable_token.cc
+++ b/base/unguessable_token.cc
@@ -7,7 +7,6 @@
 #include <ostream>
 
 #include "base/format_macros.h"
-#include "base/no_destructor.h"
 #include "base/rand_util.h"
 
 namespace base {
@@ -21,8 +20,8 @@
 
 // static
 const UnguessableToken& UnguessableToken::Null() {
-  static const NoDestructor<UnguessableToken> null_token;
-  return *null_token;
+  static const UnguessableToken null_token{};
+  return null_token;
 }
 
 // static
diff --git a/base/win/windows_version.cc b/base/win/windows_version.cc
index 72d4d0d..97789dd 100644
--- a/base/win/windows_version.cc
+++ b/base/win/windows_version.cc
@@ -57,12 +57,12 @@
 }
 
 const _SYSTEM_INFO& GetSystemInfoStorage() {
-  static const NoDestructor<_SYSTEM_INFO> system_info([] {
+  static const _SYSTEM_INFO system_info = [] {
     _SYSTEM_INFO info = {};
     ::GetNativeSystemInfo(&info);
     return info;
-  }());
-  return *system_info;
+  }();
+  return system_info;
 }
 
 }  // namespace
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 819c3edc2..189cce11 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1671,6 +1671,7 @@
         "javatests/src/org/chromium/chrome/browser/vr/WebXrArDepthSensingTest.java",
         "javatests/src/org/chromium/chrome/browser/vr/WebXrArHitTestTest.java",
         "javatests/src/org/chromium/chrome/browser/vr/WebXrArLightEstimationTest.java",
+        "javatests/src/org/chromium/chrome/browser/vr/WebXrArSanityTest.java",
         "javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java",
         "javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java",
         "javatests/src/org/chromium/chrome/browser/vr/WebXrArViewportScaleTest.java",
diff --git a/chrome/android/features/tab_ui/java/res/values/colors.xml b/chrome/android/features/tab_ui/java/res/values/colors.xml
index f2fcb3d..423e7c1 100644
--- a/chrome/android/features/tab_ui/java/res/values/colors.xml
+++ b/chrome/android/features/tab_ui/java/res/values/colors.xml
@@ -8,8 +8,11 @@
     <color name="tab_grid_card_view_tint_color">@color/popup_bg_color</color>
     <color name="tab_grid_card_view_tint_color_incognito">@color/default_bg_color_dark_elev_4</color>
 
-    <color name="tab_grid_card_title_text_color">@color/default_text_color</color>
-    <color name="tab_grid_card_title_text_color_incognito">@color/default_text_color_light</color>
+    <color name="tab_grid_card_title_text_color">@color/default_text_color_list</color>
+    <color name="tab_grid_card_title_text_color_incognito">@color/default_text_color_light_list</color>
+
+    <color name="tab_group_number_text_color">@color/default_text_color</color>
+    <color name="tab_group_number_text_color_incognito">@color/default_text_color_light</color>
 
     <color name="tab_grid_card_action_button_tint_color">@color/default_icon_color</color>
     <color name="tab_grid_card_action_button_tint_color_incognito">@color/default_icon_color_light</color>
@@ -40,4 +43,15 @@
 
     <color name="new_tab_tile_plus_color">@color/default_icon_color_secondary</color>
     <color name="new_tab_tile_plus_color_incognito">@color/default_icon_color_secondary_light</color>
+
+    <!-- Incognito colors for theme refactor 2021. -->
+    <!-- TODO(https://crbug.com/1223976): Use semantic colors for incognito. -->
+    <color name="incognito_tab_action_button_color">@color/baseline_neutral_variant_200</color>
+    <color name="incognito_tab_action_button_selected_color">@color/baseline_primary_800</color>
+
+    <color name="incognito_tab_bg_color">@color/baseline_neutral_900_with_neutral_200_alpha_12_with_primary_200_alpha_2</color>
+    <color name="incognito_tab_bg_selected_color">@color/baseline_primary_200</color>
+
+    <color name="incognito_tab_title_color">@color/baseline_neutral_100</color>
+    <color name="incognito_tab_title_selected_color">@color/baseline_primary_800</color>
 </resources>
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml
index fe0d4904..0c182f74 100644
--- a/chrome/android/features/tab_ui/java/res/values/dimens.xml
+++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -45,4 +45,7 @@
     <dimen name="price_tracking_dialog_text_top_margin">24dp</dimen>
     <dimen name="price_tracking_dialog_items_margin">20dp</dimen>
     <dimen name="price_tracking_dialog_items_bottom_margin">34dp</dimen>
+
+    <!-- Elevation -->
+    <dimen name="tab_bg_elevation">@dimen/default_elevation_4</dimen>
 </resources>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
index 7b9879d..83d65f9 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
@@ -301,7 +301,8 @@
                         TabUiColorProvider.getMiniThumbnailPlaceHolderColor(context, isIncognito));
                 mThumbnailFramePaint.setColor(
                         TabUiColorProvider.getMiniThumbnailFrameColor(context, isIncognito));
-                mTextPaint.setColor(TabUiColorProvider.getTitleTextColor(context, isIncognito));
+                mTextPaint.setColor(
+                        TabUiColorProvider.getTabGroupNumberTextColor(context, isIncognito));
                 mFaviconBackgroundPaint.setColor(
                         TabUiColorProvider.getFaviconBackgroundColor(context, isIncognito));
             }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
index 8e37d92..f0d4866 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
@@ -42,11 +42,9 @@
     private final PropertyModelChangeProcessor mModelChangeProcessor;
     private final ViewGroup mRootView;
     private TabSelectionEditorCoordinator mTabSelectionEditorCoordinator;
-    private ViewGroup mContainerView;
     private TabGridDialogView mDialogView;
-    private boolean mIsInitialized;
 
-    TabGridDialogCoordinator(Context context, TabModelSelector tabModelSelector,
+    TabGridDialogCoordinator(Activity activity, TabModelSelector tabModelSelector,
             TabContentManager tabContentManager, TabCreatorManager tabCreatorManager,
             ViewGroup containerView, TabSwitcherMediator.ResetHandler resetHandler,
             TabListMediator.GridCardOnClickListenerProvider gridCardOnClickListenerProvider,
@@ -57,42 +55,40 @@
                                                              : "TabGridDialogInSwitcher";
 
         mModel = new PropertyModel(TabGridPanelProperties.ALL_KEYS);
-        mContainerView = containerView;
         mRootView = rootView;
 
         mDialogView = containerView.findViewById(R.id.dialog_parent_view);
         if (mDialogView == null) {
-            LayoutInflater.from(context).inflate(
+            LayoutInflater.from(activity).inflate(
                     R.layout.tab_grid_dialog_layout, containerView, true);
             mDialogView = containerView.findViewById(R.id.dialog_parent_view);
             mDialogView.setupScrimCoordinator(scrimCoordinator);
         }
-        Activity activity = (Activity) context;
         SnackbarManager snackbarManager =
                 new SnackbarManager(activity, mDialogView.getSnackBarContainer(), null);
 
-        mMediator = new TabGridDialogMediator(context, this, mModel, tabModelSelector,
+        mMediator = new TabGridDialogMediator(activity, this, mModel, tabModelSelector,
                 tabCreatorManager, resetHandler, animationSourceViewProvider, shareDelegateSupplier,
                 snackbarManager, mComponentName);
 
         // TODO(crbug.com/1031349) : Remove the inline mode logic here, make the constructor to take
         // in a mode parameter instead.
         mTabListCoordinator = new TabListCoordinator(
-                TabUiFeatureUtilities.isTabGroupsAndroidContinuationEnabled(context)
+                TabUiFeatureUtilities.isTabGroupsAndroidContinuationEnabled(activity)
                                 && SysUtils.isLowEndDevice()
                         ? TabListCoordinator.TabListMode.LIST
                         : TabListCoordinator.TabListMode.GRID,
-                context, tabModelSelector, tabContentManager::getTabThumbnailWithCallback, null,
+                activity, tabModelSelector, tabContentManager::getTabThumbnailWithCallback, null,
                 false, gridCardOnClickListenerProvider, mMediator.getTabGridDialogHandler(),
                 TabProperties.UiType.CLOSABLE, null, null, containerView, false, mComponentName,
                 rootView);
         TabListRecyclerView recyclerView = mTabListCoordinator.getContainerView();
 
         TabGroupUiToolbarView toolbarView =
-                (TabGroupUiToolbarView) LayoutInflater.from(context).inflate(
+                (TabGroupUiToolbarView) LayoutInflater.from(activity).inflate(
                         R.layout.bottom_tab_grid_toolbar, recyclerView, false);
         toolbarView.setupDialogToolbarLayout();
-        if (!TabUiFeatureUtilities.isTabGroupsAndroidContinuationEnabled(context)) {
+        if (!TabUiFeatureUtilities.isTabGroupsAndroidContinuationEnabled(activity)) {
             toolbarView.hideTabGroupsContinuationWidgets();
         }
         mModelChangeProcessor = PropertyModelChangeProcessor.create(mModel,
@@ -102,8 +98,6 @@
 
     public void initWithNative(Context context, TabModelSelector tabModelSelector,
             TabContentManager tabContentManager, TabGroupTitleEditor tabGroupTitleEditor) {
-        if (mIsInitialized) return;
-
         TabSelectionEditorCoordinator.TabSelectionEditorController controller = null;
         if (TabUiFeatureUtilities.isTabGroupsAndroidContinuationEnabled(context)) {
             @TabListCoordinator.TabListMode
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogView.java
index 39496fa7..ceb4a275 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogView.java
@@ -634,6 +634,8 @@
         ApiCompatibilityUtils.setTextAppearance(
                 (TextView) (mAnimationCardView.findViewById(R.id.tab_title)),
                 R.style.TextAppearance_TextMediumThick_Primary);
+        ((TextView) (mAnimationCardView.findViewById(R.id.tab_title)))
+                .setTextColor(((TextView) (view.findViewById(R.id.tab_title))).getTextColors());
 
         ((ImageView) (mAnimationCardView.findViewById(R.id.tab_thumbnail)))
                 .setImageDrawable(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index 52c02fb..e603e59 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -27,6 +27,8 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.base.MathUtils;
+import org.chromium.chrome.browser.flags.CachedFeatureFlags;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -108,25 +110,30 @@
             tabTitleView.setContentDescription(
                     view.getResources().getString(R.string.accessibility_tabstrip_tab, title));
         } else if (TabProperties.IS_SELECTED == propertyKey) {
-            int selectedTabBackground =
-                    model.get(TabProperties.SELECTED_TAB_BACKGROUND_DRAWABLE_ID);
-            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
-                if (model.get(TabProperties.IS_SELECTED)) {
-                    view.fastFindViewById(R.id.selected_view_below_lollipop)
-                            .setBackgroundResource(selectedTabBackground);
-                    view.fastFindViewById(R.id.selected_view_below_lollipop)
-                            .setVisibility(View.VISIBLE);
-                } else {
-                    view.fastFindViewById(R.id.selected_view_below_lollipop)
-                            .setVisibility(View.GONE);
-                }
+            if (CachedFeatureFlags.isEnabled(ChromeFeatureList.THEME_REFACTOR_ANDROID)) {
+                updateColor(view, model.get(TabProperties.IS_INCOGNITO),
+                        model.get(TabProperties.IS_SELECTED));
             } else {
-                Resources res = view.getResources();
-                Resources.Theme theme = view.getContext().getTheme();
-                Drawable drawable = new InsetDrawable(
-                        ResourcesCompat.getDrawable(res, selectedTabBackground, theme),
-                        (int) res.getDimension(R.dimen.tab_list_selected_inset));
-                view.setForeground(model.get(TabProperties.IS_SELECTED) ? drawable : null);
+                int selectedTabBackground =
+                        model.get(TabProperties.SELECTED_TAB_BACKGROUND_DRAWABLE_ID);
+                if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+                    if (model.get(TabProperties.IS_SELECTED)) {
+                        view.fastFindViewById(R.id.selected_view_below_lollipop)
+                                .setBackgroundResource(selectedTabBackground);
+                        view.fastFindViewById(R.id.selected_view_below_lollipop)
+                                .setVisibility(View.VISIBLE);
+                    } else {
+                        view.fastFindViewById(R.id.selected_view_below_lollipop)
+                                .setVisibility(View.GONE);
+                    }
+                } else {
+                    Resources res = view.getResources();
+                    Resources.Theme theme = view.getContext().getTheme();
+                    Drawable drawable = new InsetDrawable(
+                            ResourcesCompat.getDrawable(res, selectedTabBackground, theme),
+                            (int) res.getDimension(R.dimen.tab_list_selected_inset));
+                    view.setForeground(model.get(TabProperties.IS_SELECTED) ? drawable : null);
+                }
             }
             if (TabUiFeatureUtilities.ENABLE_SEARCH_CHIP.getValue()) {
                 ChipView pageInfoButton = (ChipView) view.fastFindViewById(R.id.page_info_button);
@@ -204,7 +211,10 @@
                     .scaleTabGridCardView(
                             model.get(TabProperties.CARD_ANIMATION_STATUS), isSelected);
         } else if (TabProperties.IS_INCOGNITO == propertyKey) {
-            updateColor(view, model.get(TabProperties.IS_INCOGNITO), TabProperties.UiType.CLOSABLE);
+            updateColor(view, model.get(TabProperties.IS_INCOGNITO),
+                    model.get(TabProperties.IS_SELECTED));
+            updateColorForActionButton(view, model.get(TabProperties.IS_INCOGNITO),
+                    model.get(TabProperties.IS_SELECTED));
         } else if (TabProperties.ACCESSIBILITY_DELEGATE == propertyKey) {
             view.setAccessibilityDelegate(model.get(TabProperties.ACCESSIBILITY_DELEGATE));
         } else if (TabProperties.SEARCH_QUERY == propertyKey) {
@@ -287,6 +297,8 @@
             pageInfoButton.setIcon(iconDrawableId, shouldTint);
         } else if (TabProperties.IS_SELECTED == propertyKey) {
             view.setSelected(model.get(TabProperties.IS_SELECTED));
+            updateColorForActionButton(view, model.get(TabProperties.IS_INCOGNITO),
+                    model.get(TabProperties.IS_SELECTED));
         } else if (TabUiFeatureUtilities.isLaunchPolishEnabled()
                 && TabProperties.CLOSE_BUTTON_DESCRIPTION_STRING == propertyKey) {
             view.fastFindViewById(R.id.action_button)
@@ -334,8 +346,8 @@
                     .setSelectionDelegate(model.get(TabProperties.TAB_SELECTION_DELEGATE));
             ((SelectableTabGridView) view).setItem(tabId);
         } else if (TabProperties.IS_INCOGNITO == propertyKey) {
-            updateColor(
-                    view, model.get(TabProperties.IS_INCOGNITO), TabProperties.UiType.SELECTABLE);
+            updateColor(view, model.get(TabProperties.IS_INCOGNITO),
+                    model.get(TabProperties.IS_SELECTED));
         }
     }
 
@@ -380,24 +392,25 @@
         }
     }
 
-    private static void updateColor(ViewLookupCachingFrameLayout rootView, boolean isIncognito,
-            @TabProperties.UiType int viewType) {
+    private static void updateColor(
+            ViewLookupCachingFrameLayout rootView, boolean isIncognito, boolean isSelected) {
         View cardView = rootView.fastFindViewById(R.id.card_view);
         View dividerView = rootView.fastFindViewById(R.id.divider_view);
+        TextView titleView = (TextView) rootView.fastFindViewById(R.id.tab_title);
         ImageView thumbnail = (ImageView) rootView.fastFindViewById(R.id.tab_thumbnail);
-        ImageView actionButton = (ImageView) rootView.fastFindViewById(R.id.action_button);
         ChromeImageView backgroundView =
                 (ChromeImageView) rootView.fastFindViewById(R.id.background_view);
 
+        cardView.getBackground().mutate();
         ViewCompat.setBackgroundTintList(cardView,
-                TabUiColorProvider.getCardViewTintList(cardView.getContext(), isIncognito));
+                TabUiColorProvider.getCardViewTintList(
+                        cardView.getContext(), isIncognito, isSelected));
 
         dividerView.setBackgroundColor(
                 TabUiColorProvider.getDividerColor(dividerView.getContext(), isIncognito));
 
-        ApiCompatibilityUtils.setTextAppearance(
-                ((TextView) rootView.fastFindViewById(R.id.tab_title)),
-                TabUiColorProvider.getTitleTextAppearance(isIncognito));
+        titleView.setTextColor(TabUiColorProvider.getTitleTextColor(
+                titleView.getContext(), isIncognito, isSelected));
 
         if (thumbnail.getDrawable() == null) {
             thumbnail.setImageResource(
@@ -409,12 +422,14 @@
                     TabUiColorProvider.getHoveredCardBackgroundTintList(
                             backgroundView.getContext(), isIncognito));
         }
+    }
 
-        if (viewType == TabProperties.UiType.CLOSABLE) {
-            ApiCompatibilityUtils.setImageTintList(actionButton,
-                    TabUiColorProvider.getActionButtonTintList(
-                            actionButton.getContext(), isIncognito));
-        }
+    private static void updateColorForActionButton(
+            ViewLookupCachingFrameLayout rootView, boolean isIncognito, boolean isSelected) {
+        ImageView actionButton = (ImageView) rootView.fastFindViewById(R.id.action_button);
+        ApiCompatibilityUtils.setImageTintList(actionButton,
+                TabUiColorProvider.getActionButtonTintList(
+                        actionButton.getContext(), isIncognito, isSelected));
     }
 
     @VisibleForTesting
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
index 07205fe..b3246c8 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
@@ -151,7 +151,7 @@
         if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled(activity)
                 && mScrimCoordinator != null) {
             mTabGridDialogCoordinator =
-                    new TabGridDialogCoordinator(mContext, mTabModelSelector, mTabContentManager,
+                    new TabGridDialogCoordinator(mActivity, mTabModelSelector, mTabContentManager,
                             mTabCreatorManager, mActivity.findViewById(R.id.coordinator), null,
                             null, null, mShareDelegateSupplier, mScrimCoordinator, mRootView);
             mTabGridDialogCoordinator.initWithNative(mContext, mTabModelSelector,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiColorProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiColorProvider.java
index 89ef600..8828958 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiColorProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiColorProvider.java
@@ -9,27 +9,54 @@
 import android.graphics.drawable.Drawable;
 
 import androidx.annotation.ColorInt;
+import androidx.annotation.ColorRes;
 import androidx.appcompat.content.res.AppCompatResources;
 
+import com.google.android.material.color.MaterialColors;
+import com.google.android.material.elevation.ElevationOverlayProvider;
+
 import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.chrome.browser.flags.CachedFeatureFlags;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.tab_ui.R;
 
 /**
  * Color utility class for a Tab Grid card.
  */
 public class TabUiColorProvider {
+    private static final String TAG = "TabUiColorProvider";
     /**
      * Returns the {@link ColorStateList} to use for the tab grid card view background based on
      * incognito mode.
      *
      * @param context {@link Context} used to retrieve color.
      * @param isIncognito Whether the color is used for incognito mode.
+     * @param isSelected Whether the tab is currently selected.
      * @return The {@link ColorStateList} for tab grid card view background.
      */
-    public static ColorStateList getCardViewTintList(Context context, boolean isIncognito) {
-        return AppCompatResources.getColorStateList(context,
-                isIncognito ? R.color.tab_grid_card_view_tint_color_incognito
-                            : R.color.tab_grid_card_view_tint_color);
+    public static ColorStateList getCardViewTintList(
+            Context context, boolean isIncognito, boolean isSelected) {
+        if (!themeRefactorEnabled()) {
+            return AppCompatResources.getColorStateList(context,
+                    isIncognito ? R.color.tab_grid_card_view_tint_color_incognito
+                                : R.color.tab_grid_card_view_tint_color);
+        }
+
+        if (isIncognito) {
+            // Incognito does not use dynamic colors, so it can use colors from resources.
+            @ColorRes
+            int colorRes = isSelected ? R.color.incognito_tab_bg_selected_color
+                                      : R.color.incognito_tab_bg_color;
+            return AppCompatResources.getColorStateList(context, colorRes);
+        } else {
+            float tabElevation = context.getResources().getDimension(R.dimen.tab_bg_elevation);
+            @ColorInt
+            int colorInt = isSelected
+                    ? MaterialColors.getColor(context, R.attr.colorPrimary, TAG)
+                    : new ElevationOverlayProvider(context)
+                              .compositeOverlayWithThemeSurfaceColorIfNeeded(tabElevation);
+            return ColorStateList.valueOf(colorInt);
+        }
     }
 
     /**
@@ -45,28 +72,44 @@
     }
 
     /**
-     * Returns the title text color for the tab grid card based on the incognito mode.
+     * Returns the text color for the number used on the tab group cards based on the incognito
+     * mode.
      *
      * @param context {@link Context} used to retrieve color.
      * @param isIncognito Whether the color is used for incognito mode.
-     * @return The text color for the tab grid card title.
+     * @return The text color for the number used on the tab group cards.
      */
     @ColorInt
-    public static int getTitleTextColor(Context context, boolean isIncognito) {
+    public static int getTabGroupNumberTextColor(Context context, boolean isIncognito) {
         return ApiCompatibilityUtils.getColor(context.getResources(),
-                isIncognito ? R.color.tab_grid_card_title_text_color_incognito
-                            : R.color.tab_grid_card_title_text_color);
+                isIncognito ? R.color.tab_group_number_text_color_incognito
+                            : R.color.tab_group_number_text_color);
     }
 
     /**
      * Returns the title text appearance for the tab grid card based on the incognito mode.
      *
      * @param isIncognito Whether the text appearance is used for incognito mode.
+     * @param isSelected Whether the tab is currently selected.
      * @return The text appearance for the tab grid card title.
      */
-    public static int getTitleTextAppearance(boolean isIncognito) {
-        return isIncognito ? R.style.TextAppearance_TextMediumThick_Primary_Light
-                           : R.style.TextAppearance_TextMediumThick_Primary;
+    @ColorInt
+    public static int getTitleTextColor(Context context, boolean isIncognito, boolean isSelected) {
+        if (!themeRefactorEnabled()) {
+            return ApiCompatibilityUtils.getColor(context.getResources(),
+                    isIncognito ? R.color.tab_grid_card_title_text_color_incognito
+                                : R.color.tab_grid_card_title_text_color);
+        }
+
+        if (isIncognito) {
+            @ColorRes
+            int colorRes = isSelected ? R.color.incognito_tab_title_selected_color
+                                      : R.color.incognito_tab_title_color;
+            return ApiCompatibilityUtils.getColor(context.getResources(), colorRes);
+        } else {
+            return isSelected ? MaterialColors.getColor(context, R.attr.colorOnPrimary, TAG)
+                              : MaterialColors.getColor(context, R.attr.colorOnSurface, TAG);
+        }
     }
 
     /**
@@ -75,12 +118,29 @@
      *
      * @param context {@link Context} used to retrieve color.
      * @param isIncognito Whether the color is used for incognito mode.
+     * @param isSelected Whether the tab is currently selected.
      * @return The {@link ColorStateList} for tab grid card action button.
      */
-    public static ColorStateList getActionButtonTintList(Context context, boolean isIncognito) {
-        return AppCompatResources.getColorStateList(context,
-                isIncognito ? R.color.tab_grid_card_action_button_tint_color_incognito
-                            : R.color.tab_grid_card_action_button_tint_color);
+    public static ColorStateList getActionButtonTintList(
+            Context context, boolean isIncognito, boolean isSelected) {
+        if (!themeRefactorEnabled()) {
+            return AppCompatResources.getColorStateList(context,
+                    isIncognito ? R.color.tab_grid_card_action_button_tint_color_incognito
+                                : R.color.tab_grid_card_action_button_tint_color);
+        }
+
+        if (isIncognito) {
+            @ColorRes
+            int colorRes = isSelected ? R.color.incognito_tab_action_button_selected_color
+                                      : R.color.incognito_tab_action_button_color;
+            return AppCompatResources.getColorStateList(context, colorRes);
+        } else {
+            @ColorInt
+            int colorInt = isSelected
+                    ? MaterialColors.getColor(context, R.attr.colorOnPrimary, TAG)
+                    : MaterialColors.getColor(context, R.attr.colorOnSurfaceVariant, TAG);
+            return ColorStateList.valueOf(colorInt);
+        }
     }
 
     /**
@@ -227,4 +287,8 @@
         return AppCompatResources.getColorStateList(context,
                 isIncognito ? R.color.default_icon_color_light : R.color.default_icon_color);
     }
+
+    private static boolean themeRefactorEnabled() {
+        return CachedFeatureFlags.isEnabled(ChromeFeatureList.THEME_REFACTOR_ANDROID);
+    }
 }
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
index 22a358e..40eb48e2 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
@@ -868,9 +868,8 @@
     @Test
     @MediumTest
     @UiThreadTest
-    @CommandLineFlags.
-    Add({"force-fieldtrial-params=Study.Group:price_tracking_with_optimization_guide/true"})
     public void testPriceDropEndToEnd() {
+        ShoppingPersistedTabData.enablePriceTrackingWithOptimizationGuideForTesting();
         PersistedTabDataConfiguration.setUseTestConfig(true);
         PriceTrackingUtilities.ENABLE_PRICE_TRACKING.setForTesting(true);
         mockCurrencyFormatter();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
index fc29c6eb..54ac527 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
@@ -19,7 +19,6 @@
 import org.chromium.chrome.browser.page_annotations.PageAnnotationsServiceConfig;
 import org.chromium.chrome.browser.paint_preview.StartupPaintPreviewHelper;
 import org.chromium.chrome.browser.subscriptions.CommerceSubscriptionsServiceConfig;
-import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
 import org.chromium.chrome.browser.tasks.ConditionalTabStripUtils;
 import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil;
 import org.chromium.chrome.browser.tasks.tab_management.PriceTrackingUtilities;
@@ -126,10 +125,6 @@
                 MerchantViewerConfig.TRUST_SIGNALS_SHEET_USE_PAGE_TITLE,
                 PageAnnotationsServiceConfig.PAGE_ANNOTATIONS_BASE_URL,
                 ReturnToChromeExperimentsUtil.TAB_SWITCHER_ON_RETURN_MS,
-                ShoppingPersistedTabData.TIME_TO_LIVE_MS,
-                ShoppingPersistedTabData.DISPLAY_TIME_MS,
-                ShoppingPersistedTabData.STALE_TAB_THRESHOLD_SECONDS,
-                ShoppingPersistedTabData.PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE,
                 StartSurfaceConfiguration.HOME_BUTTON_ON_GRID_TAB_SWITCHER,
                 StartSurfaceConfiguration.NEW_SURFACE_FROM_HOME_BUTTON,
                 StartSurfaceConfiguration.OMNIBOX_FOCUSED_ON_NEW_TAB,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java
index a2bf660f..88f56e7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java
@@ -42,7 +42,7 @@
         // we should only do it if the user is eligible for the feature (e.g. has sync enabled).
         if (!tab.isIncognito() && !((TabImpl) tab).isCustomTab()
                 && PriceTrackingUtilities.isPriceTrackingEligible()
-                && ShoppingPersistedTabData.PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE.getValue()) {
+                && ShoppingPersistedTabData.isPriceTrackingWithOptimizationGuideEnabled()) {
             ShoppingPersistedTabData.from(tab);
         }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSanityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSanityTest.java
new file mode 100644
index 0000000..e70e0f3
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSanityTest.java
@@ -0,0 +1,112 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.vr;
+
+import static org.chromium.chrome.browser.vr.WebXrArTestFramework.PAGE_LOAD_TIMEOUT_S;
+import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_MS;
+
+import android.os.Build;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.params.ParameterAnnotations.ClassParameter;
+import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
+import org.chromium.base.test.params.ParameterSet;
+import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.vr.rules.ArPlaybackFile;
+import org.chromium.chrome.browser.vr.rules.XrActivityRestriction;
+import org.chromium.chrome.browser.vr.util.ArTestRuleUtils;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * End-to-end test that enables all AR-related features and ensures that the session is stable.
+ */
+@RunWith(ParameterizedRunner.class)
+@UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
+        "enable-features=WebXRIncubations,LogJsConsoleMessages"})
+@MinAndroidSdkLevel(Build.VERSION_CODES.N) // WebXR for AR is only supported on N+
+public class WebXrArSanityTest {
+    @ClassParameter
+    private static List<ParameterSet> sClassParams =
+            ArTestRuleUtils.generateDefaultTestRuleParameters();
+    @Rule
+    public RuleChain mRuleChain;
+
+    private ChromeActivityTestRule mTestRule;
+    private WebXrArTestFramework mWebXrArTestFramework;
+
+    public WebXrArSanityTest(Callable<ChromeActivityTestRule> callable) throws Exception {
+        mTestRule = callable.call();
+        mRuleChain = ArTestRuleUtils.wrapRuleInActivityRestrictionRule(mTestRule);
+    }
+
+    @Before
+    public void setUp() {
+        mWebXrArTestFramework = new WebXrArTestFramework(mTestRule);
+    }
+
+    /**
+     * Tests that a session is functional with all AR-related features enabled - short recording.
+     */
+    @Test
+    @MediumTest
+    @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL})
+    @ArPlaybackFile("chrome/test/data/xr/ar_playback_datasets/floor_session_12s_30fps.mp4")
+    public void testShortRecording() {
+        mWebXrArTestFramework.loadFileAndAwaitInitialization(
+                "webxr_test_basic_all_ar_features", PAGE_LOAD_TIMEOUT_S);
+
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+            mWebXrArTestFramework.runJavaScriptOrFail(
+                    "disableCameraAccess()", POLL_TIMEOUT_SHORT_MS);
+        }
+
+        mWebXrArTestFramework.enterSessionWithUserGestureOrFail();
+        // The recording is 12 seconds long, let's tell the test to run for 10 seconds and wait for
+        // a bit more than that before timing out.
+        mWebXrArTestFramework.executeStepAndWait("stepStartTest(10)", 15 * 1000);
+        mWebXrArTestFramework.endTest();
+    }
+
+    /**
+     * Tests that a session is functional with all AR-related features enabled - long recording.
+     */
+    @Test
+    @LargeTest
+    @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL})
+    @ArPlaybackFile(
+            "chrome/test/data/xr/ar_playback_datasets/floor_session_with_tracking_loss_37s_30fps.mp4")
+    public void
+    testLongRecording() {
+        mWebXrArTestFramework.loadFileAndAwaitInitialization(
+                "webxr_test_basic_all_ar_features", PAGE_LOAD_TIMEOUT_S);
+
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+            mWebXrArTestFramework.runJavaScriptOrFail(
+                    "disableCameraAccess()", POLL_TIMEOUT_SHORT_MS);
+        }
+
+        mWebXrArTestFramework.enterSessionWithUserGestureOrFail();
+        // The recording is 37 seconds long, let's tell the test to run for 30 seconds and wait for
+        // a bit more than that before timing out.
+        mWebXrArTestFramework.executeStepAndWait("stepStartTest(30)", 40 * 1000);
+        mWebXrArTestFramework.endTest();
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
index 2872989..77c1ecdc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
@@ -163,7 +163,10 @@
      */
     public static boolean pollJavaScriptBoolean(
             final String boolExpression, int timeoutMs, final WebContents webContents) {
-        if (DEBUG_LOGS) Log.i(TAG, "pollJavaScriptBoolean " + boolExpression);
+        if (DEBUG_LOGS) {
+            Log.i(TAG, "pollJavaScriptBoolean " + boolExpression + ", timeoutMs=" + timeoutMs);
+        }
+
         try {
             CriteriaHelper.pollInstrumentationThread(() -> {
                 String result = "false";
@@ -288,7 +291,7 @@
      * @param timeoutMs Timeout (in milliseconds) to wait for the JavaScript step.
      */
     public static void waitOnJavaScriptStep(WebContents webContents, int timeoutMs) {
-        if (DEBUG_LOGS) Log.i(TAG, "waitOnJavaScriptStep");
+        if (DEBUG_LOGS) Log.i(TAG, "waitOnJavaScriptStep, timeoutMs=" + timeoutMs);
         // Make sure we aren't trying to wait on a JavaScript test step without the code to do so.
         Assert.assertTrue("Attempted to wait on a JavaScript step without the code to do so. You "
                         + "either forgot to import webxr_e2e.js or are incorrectly using a "
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 5b259cd..792b13e 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -119,7 +119,7 @@
 #include "ash/constants/ash_paths.h"
 #include "ash/constants/ash_switches.h"
 #include "base/system/sys_info.h"
-#include "chrome/browser/ash/dbus/dbus_helper.h"
+#include "chrome/browser/ash/dbus/ash_dbus_helper.h"
 #include "chrome/browser/chromeos/boot_times_recorder.h"
 #include "chrome/browser/chromeos/startup_settings_cache.h"
 #include "chromeos/dbus/constants/dbus_paths.h"
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index a81cbc7..d306a16 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -779,6 +779,24 @@
   <message name="IDS_OS_SETTINGS_DARK_MODE_THEMED_OFF" desc="Label for the checkbox which set the color mode to default color than calculated from the current wallpaper.">
     Neutral
   </message>
+    <message name="IDS_SETTINGS_DARK_MODE_ON_AT_SUNSET" desc="The sub label for the automatic schedule which explains that Dark Mode will turn on automatically at sunset.">
+    Dark mode will turn on automatically at sunset
+  </message>
+  <message name="IDS_SETTINGS_DARK_MODE_OFF_AT_SUNRISE" desc="The sub label for the automatic schedule which explains that Dark Mode will turn off automatically at sunrise.">
+    Dark mode will turn off automatically at sunrise
+  </message>
+  <message name="IDS_SETTINGS_DARK_MODE_SCHEDULE_CUSTOM" desc="The label of the option to set a custom schedule of the Dark Mode feature.">
+    Custom
+  </message>
+  <message name="IDS_SETTINGS_DARK_MODE_SCHEDULE_LABEL" desc="The label for the automatic schedule section of the Dark Mode feature.">
+    Schedule
+  </message>
+  <message name="IDS_SETTINGS_DARK_MODE_SCHEDULE_NEVER" desc="The label of the option to turn off the automatic schedule of the Dark Mode feature.">
+    Never
+  </message>
+  <message name="IDS_SETTINGS_DARK_MODE_SCHEDULE_SUNSET_TO_SUNRISE" desc="The label of the option to set the automatic schedule of the Dark Mode feature to turn on at sunset and off at sunrise.">
+    Sunset to sunrise
+  </message>
 
   <!-- Search and Assistant section. -->
   <message name="IDS_OS_SETTINGS_SEARCH_ENGINE_LABEL" desc="Label in OS settings describing search engine behavior.">
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_OFF_AT_SUNRISE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_OFF_AT_SUNRISE.png.sha1
new file mode 100644
index 0000000..77a60d9
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_OFF_AT_SUNRISE.png.sha1
@@ -0,0 +1 @@
+2ae74995dc84c04a05374fc051fb2d5c5221da4a
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_ON_AT_SUNSET.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_ON_AT_SUNSET.png.sha1
new file mode 100644
index 0000000..a86088d0
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_ON_AT_SUNSET.png.sha1
@@ -0,0 +1 @@
+b31d7bb3136a7b999f3bfb2e514e1b85dc2f9b3e
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_SCHEDULE_CUSTOM.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_SCHEDULE_CUSTOM.png.sha1
new file mode 100644
index 0000000..87bba5d9
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_SCHEDULE_CUSTOM.png.sha1
@@ -0,0 +1 @@
+45f495409c99f9abf07d4000779a9e4617be5712
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_SCHEDULE_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_SCHEDULE_LABEL.png.sha1
new file mode 100644
index 0000000..87bba5d9
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_SCHEDULE_LABEL.png.sha1
@@ -0,0 +1 @@
+45f495409c99f9abf07d4000779a9e4617be5712
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_SCHEDULE_NEVER.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_SCHEDULE_NEVER.png.sha1
new file mode 100644
index 0000000..a86088d0
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_SCHEDULE_NEVER.png.sha1
@@ -0,0 +1 @@
+b31d7bb3136a7b999f3bfb2e514e1b85dc2f9b3e
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_SCHEDULE_SUNSET_TO_SUNRISE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_SCHEDULE_SUNSET_TO_SUNRISE.png.sha1
new file mode 100644
index 0000000..a86088d0
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DARK_MODE_SCHEDULE_SUNSET_TO_SUNRISE.png.sha1
@@ -0,0 +1 @@
+b31d7bb3136a7b999f3bfb2e514e1b85dc2f9b3e
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index a9bf108..332ffb9 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1465,10 +1465,6 @@
     "safe_browsing/metrics/safe_browsing_metrics_provider.h",
     "search/search.cc",
     "search/search.h",
-    "search/suggestions/suggestions_service_factory.cc",
-    "search/suggestions/suggestions_service_factory.h",
-    "search/suggestions/suggestions_ui.cc",
-    "search/suggestions/suggestions_ui.h",
     "search_engines/chrome_template_url_service_client.cc",
     "search_engines/chrome_template_url_service_client.h",
     "search_engines/template_url_fetcher_factory.cc",
@@ -2210,7 +2206,6 @@
     "//components/subresource_filter/core/browser",
     "//components/subresource_filter/core/common",
     "//components/subresource_redirect/common",
-    "//components/suggestions",
     "//components/sync",
     "//components/sync_bookmarks",
     "//components/sync_preferences",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 929ddd9..d666d047 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -305,7 +305,6 @@
   "+components/subresource_filter/core/common",
   "+components/subresource_filter/core/mojom",
   "+components/subresource_redirect/common",
-  "+components/suggestions",
   "+components/supervised_user_error_page",
   "+components/sync",
   "+components/sync_bookmarks",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 4e8e5b3..77034b30 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4589,6 +4589,10 @@
      flag_descriptions::kTabGroupsNewBadgePromoDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kTabGroupsNewBadgePromo)},
 
+    {"tab-groups-save", flag_descriptions::kTabGroupsSaveName,
+     flag_descriptions::kTabGroupsSaveDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(features::kTabGroupsSave)},
+
     {"new-tabstrip-animation", flag_descriptions::kNewTabstripAnimationName,
      flag_descriptions::kNewTabstripAnimationDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kNewTabstripAnimation)},
@@ -5550,6 +5554,10 @@
      flag_descriptions::kEnableNetworkingInDiagnosticsAppDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kEnableNetworkingInDiagnosticsApp)},
 
+    {"enable-oauth-ipp", flag_descriptions::kEnableOAuthIppName,
+     flag_descriptions::kEnableOAuthIppDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kEnableOAuthIpp)},
+
     {"enable-shortcut-customization-app",
      flag_descriptions::kEnableShortcutCustomizationAppName,
      flag_descriptions::kEnableShortcutCustomizationAppDescription, kOsCrOS,
@@ -7391,6 +7399,15 @@
      FEATURE_VALUE_TYPE(features::kWin10TabSearchCaptionButton)},
 #endif  // defined(OS_WIN)
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    {"enable-holding-space-in-progress-downloads-integration",
+     flag_descriptions::kHoldingSpaceInProgressDownloadsIntegrationName,
+     flag_descriptions::kHoldingSpaceInProgressDownloadsIntegrationDescription,
+     kOsCrOS,
+     FEATURE_VALUE_TYPE(
+         ash::features::kHoldingSpaceInProgressDownloadsIntegration)},
+#endif
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/profile_key_startup_accessor.cc b/chrome/browser/android/profile_key_startup_accessor.cc
index b5c59e1..f33266d 100644
--- a/chrome/browser/android/profile_key_startup_accessor.cc
+++ b/chrome/browser/android/profile_key_startup_accessor.cc
@@ -5,14 +5,13 @@
 #include "chrome/browser/android/profile_key_startup_accessor.h"
 
 #include "base/check.h"
-#include "base/no_destructor.h"
 
 ProfileKeyStartupAccessor::ProfileKeyStartupAccessor() : key_(nullptr) {}
 
 // static
 ProfileKeyStartupAccessor* ProfileKeyStartupAccessor::GetInstance() {
-  static base::NoDestructor<ProfileKeyStartupAccessor> instance;
-  return instance.get();
+  static ProfileKeyStartupAccessor instance;
+  return &instance;
 }
 
 void ProfileKeyStartupAccessor::SetProfileKey(ProfileKey* key) {
diff --git a/chrome/browser/ash/accessibility/dictation.cc b/chrome/browser/ash/accessibility/dictation.cc
index ca4bb73..577452e 100644
--- a/chrome/browser/ash/accessibility/dictation.cc
+++ b/chrome/browser/ash/accessibility/dictation.cc
@@ -279,6 +279,7 @@
                               true);
     no_speech_timeout_ = kDeviceNoSpeechTimeout;
     no_new_speech_timeout_ = kDeviceNoNewSpeechTimeout;
+    used_on_device_speech_ = true;
   } else {
     speech_recognizer_ = std::make_unique<NetworkSpeechRecognizer>(
         weak_ptr_factory_.GetWeakPtr(),
@@ -293,7 +294,9 @@
             ? kDeviceNoSpeechTimeout
             : kNetworkNoSpeechTimeout;
     no_new_speech_timeout_ = kNetworkNoNewSpeechTimeout;
+    used_on_device_speech_ = false;
   }
+  listening_duration_timer_ = base::ElapsedTimer();
   return true;
 }
 
@@ -402,6 +405,17 @@
       AccessibilityNotificationType::kToggleDictation, false /* enabled */);
   AccessibilityManager::Get()->NotifyAccessibilityStatusChanged(details);
   speech_recognizer_.reset();
+
+  // Duration matches the lifetime of the speech recognizer.
+  if (used_on_device_speech_) {
+    base::UmaHistogramLongTimes(
+        "Accessibility.CrosDictation.ListeningDuration.OnDeviceRecognition",
+        listening_duration_timer_.Elapsed());
+  } else {
+    base::UmaHistogramLongTimes(
+        "Accessibility.CrosDictation.ListeningDuration.NetworkRecognition",
+        listening_duration_timer_.Elapsed());
+  }
 }
 
 void Dictation::CommitCurrentText() {
diff --git a/chrome/browser/ash/accessibility/dictation.h b/chrome/browser/ash/accessibility/dictation.h
index 8c51880..08a7fb6 100644
--- a/chrome/browser/ash/accessibility/dictation.h
+++ b/chrome/browser/ash/accessibility/dictation.h
@@ -10,6 +10,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
+#include "base/timer/elapsed_timer.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/speech/speech_recognizer_delegate.h"
 #include "content/public/browser/speech_recognition_session_preamble.h"
@@ -90,6 +91,10 @@
   base::TimeDelta no_speech_timeout_;
   base::TimeDelta no_new_speech_timeout_;
 
+  // Used for metrics.
+  bool used_on_device_speech_ = false;
+  base::ElapsedTimer listening_duration_timer_;
+
   base::WeakPtrFactory<Dictation> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(Dictation);
diff --git a/chrome/browser/ash/account_manager/account_manager_migrator.h b/chrome/browser/ash/account_manager/account_manager_migrator.h
index cbe10f4c..11c82f2 100644
--- a/chrome/browser/ash/account_manager/account_manager_migrator.h
+++ b/chrome/browser/ash/account_manager/account_manager_migrator.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/no_destructor.h"
 #include "chrome/browser/ash/account_manager/account_migration_runner.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -16,11 +17,6 @@
 
 class Profile;
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 namespace ash {
 
 class AccountManagerMigrator : public KeyedService {
diff --git a/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.h b/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.h
index e8f62a0b..b1ab2aa 100644
--- a/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.h
+++ b/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.h
@@ -6,13 +6,9 @@
 #define CHROME_BROWSER_ASH_ACCOUNT_MANAGER_ACCOUNT_MANAGER_POLICY_CONTROLLER_FACTORY_H_
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 namespace ash {
 class AccountManagerPolicyController;
 
diff --git a/chrome/browser/ash/crosapi/test_controller_ash.cc b/chrome/browser/ash/crosapi/test_controller_ash.cc
index 5dbddf0..c30d10c 100644
--- a/chrome/browser/ash/crosapi/test_controller_ash.cc
+++ b/chrome/browser/ash/crosapi/test_controller_ash.cc
@@ -88,13 +88,15 @@
 void TestControllerAsh::EnterOverviewMode(EnterOverviewModeCallback callback) {
   overview_waiters_.push_back(std::make_unique<OverviewWaiter>(
       /*wait_for_enter=*/true, std::move(callback), this));
-  ash::Shell::Get()->overview_controller()->StartOverview();
+  ash::Shell::Get()->overview_controller()->StartOverview(
+      ash::OverviewStartAction::kTests);
 }
 
 void TestControllerAsh::ExitOverviewMode(ExitOverviewModeCallback callback) {
   overview_waiters_.push_back(std::make_unique<OverviewWaiter>(
       /*wait_for_enter=*/false, std::move(callback), this));
-  ash::Shell::Get()->overview_controller()->EndOverview();
+  ash::Shell::Get()->overview_controller()->EndOverview(
+      ash::OverviewEndAction::kTests);
 }
 
 void TestControllerAsh::EnterTabletMode(EnterTabletModeCallback callback) {
diff --git a/chrome/browser/ash/dbus/dbus_helper.cc b/chrome/browser/ash/dbus/ash_dbus_helper.cc
similarity index 99%
rename from chrome/browser/ash/dbus/dbus_helper.cc
rename to chrome/browser/ash/dbus/ash_dbus_helper.cc
index d660edb..f724925b 100644
--- a/chrome/browser/ash/dbus/dbus_helper.cc
+++ b/chrome/browser/ash/dbus/ash_dbus_helper.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/ash/dbus/dbus_helper.h"
+#include "chrome/browser/ash/dbus/ash_dbus_helper.h"
 
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_paths.h"
diff --git a/chrome/browser/ash/dbus/dbus_helper.h b/chrome/browser/ash/dbus/ash_dbus_helper.h
similarity index 78%
rename from chrome/browser/ash/dbus/dbus_helper.h
rename to chrome/browser/ash/dbus/ash_dbus_helper.h
index 051ec5b..bc6cbd6 100644
--- a/chrome/browser/ash/dbus/dbus_helper.h
+++ b/chrome/browser/ash/dbus/ash_dbus_helper.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_ASH_DBUS_DBUS_HELPER_H_
-#define CHROME_BROWSER_ASH_DBUS_DBUS_HELPER_H_
+#ifndef CHROME_BROWSER_ASH_DBUS_ASH_DBUS_HELPER_H_
+#define CHROME_BROWSER_ASH_DBUS_ASH_DBUS_HELPER_H_
 
 namespace chromeos {
 
@@ -19,4 +19,4 @@
 
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_ASH_DBUS_DBUS_HELPER_H_
+#endif  // CHROME_BROWSER_ASH_DBUS_ASH_DBUS_HELPER_H_
diff --git a/chrome/browser/ash/file_manager/file_manager_jstest.cc b/chrome/browser/ash/file_manager/file_manager_jstest.cc
index 8fe7ed1..12d47da 100644
--- a/chrome/browser/ash/file_manager/file_manager_jstest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_jstest.cc
@@ -98,7 +98,8 @@
   RunTestURL("common/js/files_app_entry_types_unittest.m_gen.html");
 }
 
-IN_PROC_BROWSER_TEST_F(FileManagerJsTest, FilesDisplayPanel) {
+// Test is flaky. See crbug.com/1226141
+IN_PROC_BROWSER_TEST_F(FileManagerJsTest, DISABLED_FilesDisplayPanel) {
   RunTestURL("foreground/elements/files_xf_elements_unittest.m_gen.html");
 }
 
diff --git a/chrome/browser/ash/login/session/user_session_manager.cc b/chrome/browser/ash/login/session/user_session_manager.cc
index 383c24f..9c92729 100644
--- a/chrome/browser/ash/login/session/user_session_manager.cc
+++ b/chrome/browser/ash/login/session/user_session_manager.cc
@@ -632,6 +632,10 @@
     // TODO(nkostylev): Fix this hack by improving Authenticator dependencies.
     authenticator_->SetConsumer(consumer);
   }
+
+  for (auto& observer : authenticator_observer_list_) {
+    observer.OnAuthAttemptStarted();
+  }
   return authenticator_;
 }
 
@@ -984,6 +988,16 @@
   session_state_observer_list_.RemoveObserver(observer);
 }
 
+void UserSessionManager::AddUserAuthenticatorObserver(
+    ash::UserAuthenticatorObserver* observer) {
+  authenticator_observer_list_.AddObserver(observer);
+}
+
+void UserSessionManager::RemoveUserAuthenticatorObserver(
+    ash::UserAuthenticatorObserver* observer) {
+  authenticator_observer_list_.RemoveObserver(observer);
+}
+
 void UserSessionManager::OnSessionRestoreStateChanged(
     Profile* user_profile,
     OAuth2LoginManager::SessionRestoreState state) {
diff --git a/chrome/browser/ash/login/session/user_session_manager.h b/chrome/browser/ash/login/session/user_session_manager.h
index 9fdcc5b..816045d 100644
--- a/chrome/browser/ash/login/session/user_session_manager.h
+++ b/chrome/browser/ash/login/session/user_session_manager.h
@@ -92,6 +92,12 @@
   virtual ~UserSessionStateObserver();
 };
 
+class UserAuthenticatorObserver : public base::CheckedObserver {
+ public:
+  // Called when authentication is started.
+  virtual void OnAuthAttemptStarted() {}
+};
+
 // UserSessionManager is responsible for starting user session which includes:
 // * load and initialize Profile (including custom Profile preferences),
 // * mark user as logged in and notify observers,
@@ -276,6 +282,10 @@
   void AddSessionStateObserver(ash::UserSessionStateObserver* observer);
   void RemoveSessionStateObserver(ash::UserSessionStateObserver* observer);
 
+  void AddUserAuthenticatorObserver(ash::UserAuthenticatorObserver* observer);
+  void RemoveUserAuthenticatorObserver(
+      ash::UserAuthenticatorObserver* observer);
+
   void ActiveUserChanged(user_manager::User* active_user) override;
 
   // Returns default IME state for user session.
@@ -588,6 +598,9 @@
   base::ObserverList<ash::UserSessionStateObserver>::Unchecked
       session_state_observer_list_;
 
+  base::ObserverList<ash::UserAuthenticatorObserver>
+      authenticator_observer_list_;
+
   // Set of user_id for those users that we should restore authentication
   // session when notified about online state change.
   SigninSessionRestoreStateSet pending_signin_restore_sessions_;
diff --git a/chrome/browser/ash/notifications/request_system_proxy_credentials_view.cc b/chrome/browser/ash/notifications/request_system_proxy_credentials_view.cc
index e842169..baa1a2fa9 100644
--- a/chrome/browser/ash/notifications/request_system_proxy_credentials_view.cc
+++ b/chrome/browser/ash/notifications/request_system_proxy_credentials_view.cc
@@ -19,6 +19,7 @@
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/base/models/image_model.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/events/event.h"
 #include "ui/gfx/color_palette.h"
@@ -30,6 +31,35 @@
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/widget/widget.h"
 
+namespace {
+
+class ErrorLabelView : public views::Label {
+ public:
+  METADATA_HEADER(ErrorLabelView);
+
+  explicit ErrorLabelView(bool show_error_label)
+      : Label(l10n_util::GetStringUTF16(
+            IDS_SYSTEM_PROXY_AUTH_DIALOG_ERROR_LABEL)) {
+    SetEnabled(true);
+    SetVisible(show_error_label);
+  }
+  ErrorLabelView(const ErrorLabelView&) = delete;
+  ErrorLabelView& operator=(const ErrorLabelView&) = delete;
+  ~ErrorLabelView() override = default;
+
+  // views::View:
+  void OnThemeChanged() override {
+    Label::OnThemeChanged();
+    SetEnabledColor(GetNativeTheme()->GetSystemColor(
+        ui::NativeTheme::kColorId_AlertSeverityHigh));
+  }
+};
+
+BEGIN_METADATA(ErrorLabelView, views::Label)
+END_METADATA
+
+}  // namespace
+
 namespace ash {
 
 RequestSystemProxyCredentialsView::RequestSystemProxyCredentialsView(
@@ -155,20 +185,15 @@
                               related_vertical_spacing);
   auto error_icon = std::make_unique<views::ImageView>();
   const int kIconSize = 18;
-  error_icon->SetImage(
-      gfx::CreateVectorIcon(vector_icons::kInfoOutlineIcon, kIconSize,
-                            GetNativeTheme()->GetSystemColor(
-                                ui::NativeTheme::kColorId_AlertSeverityHigh)));
+  error_icon->SetImage(ui::ImageModel::FromVectorIcon(
+      vector_icons::kInfoOutlineIcon,
+      ui::NativeTheme::kColorId_AlertSeverityHigh, kIconSize));
   error_icon->SetImageSize(gfx::Size(kIconSize, kIconSize));
   error_icon->SetVisible(show_error_label_);
   layout->AddView(std::move(error_icon));
-  auto error_label = std::make_unique<views::Label>(
-      l10n_util::GetStringUTF16(IDS_SYSTEM_PROXY_AUTH_DIALOG_ERROR_LABEL));
-  error_label->SetEnabled(true);
-  error_label->SetVisible(show_error_label_);
-  error_label->SetEnabledColor(error_label->GetNativeTheme()->GetSystemColor(
-      ui::NativeTheme::kColorId_AlertSeverityHigh));
-  error_label_ = layout->AddView(std::move(error_label));
+
+  error_label_ =
+      layout->AddView(std::make_unique<ErrorLabelView>(show_error_label_));
 }
 
 BEGIN_METADATA(RequestSystemProxyCredentialsView, views::DialogDelegateView)
diff --git a/chrome/browser/autofill/mock_autofill_popup_controller.h b/chrome/browser/autofill/mock_autofill_popup_controller.h
index 1466d9d4..25c6f88 100644
--- a/chrome/browser/autofill/mock_autofill_popup_controller.h
+++ b/chrome/browser/autofill/mock_autofill_popup_controller.h
@@ -37,8 +37,8 @@
   MOCK_CONST_METHOD0(container_view, gfx::NativeView());
   MOCK_CONST_METHOD0(GetWebContents, content::WebContents*());
   const gfx::RectF& element_bounds() const override {
-    static base::NoDestructor<gfx::RectF> bounds({100, 100, 250, 50});
-    return *bounds;
+    static const gfx::RectF bounds(100, 100, 250, 50);
+    return bounds;
   }
   MOCK_CONST_METHOD0(IsRTL, bool());
 
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 504f3f3..95e64ad4 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1175,14 +1175,14 @@
     "../ash/customization/customization_wallpaper_downloader.h",
     "../ash/customization/customization_wallpaper_util.cc",
     "../ash/customization/customization_wallpaper_util.h",
+    "../ash/dbus/ash_dbus_helper.cc",
+    "../ash/dbus/ash_dbus_helper.h",
     "../ash/dbus/chrome_features_service_provider.cc",
     "../ash/dbus/chrome_features_service_provider.h",
     "../ash/dbus/component_updater_service_provider.cc",
     "../ash/dbus/component_updater_service_provider.h",
     "../ash/dbus/cryptohome_key_delegate_service_provider.cc",
     "../ash/dbus/cryptohome_key_delegate_service_provider.h",
-    "../ash/dbus/dbus_helper.cc",
-    "../ash/dbus/dbus_helper.h",
     "../ash/dbus/dlp_files_policy_service_provider.cc",
     "../ash/dbus/dlp_files_policy_service_provider.h",
     "../ash/dbus/drive_file_stream_service_provider.cc",
@@ -2929,8 +2929,6 @@
     "policy/server_backed_state/server_backed_state_keys_broker.h",
     "policy/status_collector/activity_storage.cc",
     "policy/status_collector/activity_storage.h",
-    "policy/status_collector/affiliated_session_service.cc",
-    "policy/status_collector/affiliated_session_service.h",
     "policy/status_collector/app_info_generator.cc",
     "policy/status_collector/app_info_generator.h",
     "policy/status_collector/child_activity_storage.cc",
@@ -2942,6 +2940,8 @@
     "policy/status_collector/enterprise_activity_storage.cc",
     "policy/status_collector/enterprise_activity_storage.h",
     "policy/status_collector/interval_map.h",
+    "policy/status_collector/managed_session_service.cc",
+    "policy/status_collector/managed_session_service.h",
     "policy/status_collector/status_collector.cc",
     "policy/status_collector/status_collector.h",
     "policy/status_collector/status_collector_state.cc",
@@ -4213,10 +4213,10 @@
     "policy/server_backed_state/device_cloud_state_keys_uploader_unittest.cc",
     "policy/server_backed_state/server_backed_state_keys_broker_unittest.cc",
     "policy/status_collector/activity_storage_unittest.cc",
-    "policy/status_collector/affiliated_session_service_unittest.cc",
     "policy/status_collector/app_info_generator_unittest.cc",
     "policy/status_collector/enterprise_activity_storage_unittest.cc",
     "policy/status_collector/interval_map_unittest.cc",
+    "policy/status_collector/managed_session_service_unittest.cc",
     "policy/uploading/heartbeat_scheduler_unittest.cc",
     "policy/uploading/status_uploader_unittest.cc",
     "policy/uploading/system_log_uploader_unittest.cc",
@@ -4609,6 +4609,7 @@
       "//chromeos/dbus:dbus",
       "//chromeos/tpm",
       "//components:components_tests_pak",
+      "//components/policy/core/browser:internal",
       "//third_party/libprotobuf-mutator",
       "//ui/resources:ui_test_pak",
     ]
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 3b89aff..cb84c03e 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -54,10 +54,10 @@
 #include "chrome/browser/ash/crosapi/crosapi_manager.h"
 #include "chrome/browser/ash/crostini/crostini_unsupported_action_notifier.h"
 #include "chrome/browser/ash/crostini/crosvm_metrics.h"
+#include "chrome/browser/ash/dbus/ash_dbus_helper.h"
 #include "chrome/browser/ash/dbus/chrome_features_service_provider.h"
 #include "chrome/browser/ash/dbus/component_updater_service_provider.h"
 #include "chrome/browser/ash/dbus/cryptohome_key_delegate_service_provider.h"
-#include "chrome/browser/ash/dbus/dbus_helper.h"
 #include "chrome/browser/ash/dbus/dlp_files_policy_service_provider.h"
 #include "chrome/browser/ash/dbus/drive_file_stream_service_provider.h"
 #include "chrome/browser/ash/dbus/encrypted_reporting_service_provider.h"
diff --git a/chrome/browser/chromeos/input_method/component_extension_ime_manager_delegate_impl.cc b/chrome/browser/chromeos/input_method/component_extension_ime_manager_delegate_impl.cc
index 67986cb0..8d5997b 100644
--- a/chrome/browser/chromeos/input_method/component_extension_ime_manager_delegate_impl.cc
+++ b/chrome/browser/chromeos/input_method/component_extension_ime_manager_delegate_impl.cc
@@ -19,7 +19,6 @@
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
-#include "base/trace_event/trace_event.h"
 #include "build/branding_buildflags.h"
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -110,7 +109,6 @@
                      const std::string& extension_id,
                      const std::string& manifest,
                      const base::FilePath& file_path) {
-  TRACE_EVENT1("ime", "DoLoadExtension", "ext_id", extension_id);
   extensions::ExtensionRegistry* extension_registry =
       extensions::ExtensionRegistry::Get(profile);
   DCHECK(extension_registry);
@@ -156,7 +154,18 @@
                        const base::FilePath* file_path,
                        bool result) {
   if (result) {
-    DoLoadExtension(profile, *extension_id, *manifest, *file_path);
+    std::string manifest_str = *manifest;
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+    // Load Mojo-only background page for ChromeOS IME extension when feature
+    // 'ImeMojoDecoder' is enabled. See http://b/181170189 for more details.
+    // TODO(http://b/170278753): Remove this once NaCl decoder is removed.
+    if ((*extension_id == extension_ime_util::kXkbExtensionId) &&
+        base::FeatureList::IsEnabled(chromeos::features::kImeMojoDecoder)) {
+      base::ReplaceFirstSubstringAfterOffset(
+          &manifest_str, 0, "background.html", "background_mojo.html");
+    }
+#endif
+    DoLoadExtension(profile, *extension_id, manifest_str, *file_path);
   } else {
     LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS())
         << "IME extension file path does not exist: " << file_path->value();
@@ -185,26 +194,6 @@
     const std::string& extension_id,
     const std::string& manifest,
     const base::FilePath& file_path) {
-  TRACE_EVENT0("ime", "ComponentExtensionIMEManagerDelegateImpl::Load");
-  std::string* manifest_cp = new std::string(manifest);
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  // Skip checking the path of the Chrome OS IME component extension when it's
-  // Google Chrome brand, since it is bundled resource on Chrome OS image. This
-  // will improve the IME extension load latency a lot.
-  // See http://b/192032670 for more details.
-  if (extension_id == extension_ime_util::kXkbExtensionId) {
-    // Update manifest content inplace to load Mojo background page for ChromeOS
-    // IME extension when the feature 'ImeMojoDecoder' is enabled.
-    // See http://b/181170189 for more details.
-    // TODO(http://b/170278753): Remove this once NaCl decoder is removed.
-    if (base::FeatureList::IsEnabled(chromeos::features::kImeMojoDecoder)) {
-      base::ReplaceFirstSubstringAfterOffset(manifest_cp, 0, "background.html",
-                                             "background_mojo.html");
-    }
-    DoLoadExtension(profile, extension_id, *manifest_cp, file_path);
-    return;
-  }
-#endif
   // Check the existence of file path to avoid unnecessary extension loading
   // and InputMethodEngine creation, so that the virtual keyboard web content
   // url won't be override by IME component extensions.
@@ -216,7 +205,8 @@
       base::BindOnce(&CheckFilePath, base::Unretained(copied_file_path)),
       base::BindOnce(&OnFilePathChecked, base::Unretained(profile),
                      base::Owned(new std::string(extension_id)),
-                     base::Owned(manifest_cp), base::Owned(copied_file_path)));
+                     base::Owned(new std::string(manifest)),
+                     base::Owned(copied_file_path)));
 }
 
 bool ComponentExtensionIMEManagerDelegateImpl::IsInLoginLayoutAllowlist(
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
index 0f6168b..0ca55730 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
@@ -26,7 +26,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info.h"
 #include "base/time/time.h"
-#include "base/trace_event/trace_event.h"
 #include "chrome/browser/ash/login/session/user_session_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
@@ -1162,11 +1161,8 @@
   // image. If specified component extension IME no longer exists, falling back
   // to an existing IME.
   DCHECK(state);
-  TRACE_EVENT0("ime",
-               "InputMethodManagerImpl::LoadNecessaryComponentExtensions");
   std::vector<std::string> unfiltered_input_method_ids;
   unfiltered_input_method_ids.swap(state->active_input_method_ids);
-  std::set<std::string> ext_loaded;
   for (const auto& unfiltered_input_method_id : unfiltered_input_method_ids) {
     if (!extension_ime_util::IsComponentExtensionIME(
             unfiltered_input_method_id)) {
@@ -1176,7 +1172,7 @@
                    unfiltered_input_method_id)) {
       if (enable_extension_loading_) {
         component_extension_ime_manager_->LoadComponentExtensionIME(
-            state->profile, unfiltered_input_method_id, &ext_loaded);
+            state->profile, unfiltered_input_method_id);
       }
 
       state->active_input_method_ids.push_back(unfiltered_input_method_id);
diff --git a/chrome/browser/chromeos/input_method/ui/candidate_view.cc b/chrome/browser/chromeos/input_method/ui/candidate_view.cc
index cca6f35..6af89b7 100644
--- a/chrome/browser/chromeos/input_method/ui/candidate_view.cc
+++ b/chrome/browser/chromeos/input_method/ui/candidate_view.cc
@@ -48,47 +48,83 @@
 BEGIN_METADATA(VerticalCandidateLabel, views::Label)
 END_METADATA
 
-// Creates the shortcut label, and returns it (never returns nullptr).
-// The label text is not set in this function.
-std::unique_ptr<views::Label> CreateShortcutLabel(
-    ui::CandidateWindow::Orientation orientation,
-    const ui::NativeTheme& theme) {
-  auto shortcut_label = std::make_unique<views::Label>();
+// The label text is not set in this class.
+class ShortcutLabel : public views::Label {
+ public:
+  METADATA_HEADER(ShortcutLabel);
+  explicit ShortcutLabel(ui::CandidateWindow::Orientation orientation)
+      : orientation_(orientation) {
+    // TODO(tapted): Get this FontList from views::style.
+    if (orientation == ui::CandidateWindow::VERTICAL) {
+      SetFontList(font_list().Derive(kFontSizeDelta, gfx::Font::NORMAL,
+                                     gfx::Font::Weight::BOLD));
+    } else {
+      SetFontList(font_list().DeriveWithSizeDelta(kFontSizeDelta));
+    }
+    // TODO(satorux): Maybe we need to use language specific fonts for
+    // candidate_label, like Chinese font for Chinese input method?
 
-  // TODO(tapted): Get this FontList from views::style.
-  if (orientation == ui::CandidateWindow::VERTICAL) {
-    shortcut_label->SetFontList(shortcut_label->font_list().Derive(
-        kFontSizeDelta, gfx::Font::NORMAL, gfx::Font::Weight::BOLD));
-  } else {
-    shortcut_label->SetFontList(
-        shortcut_label->font_list().DeriveWithSizeDelta(kFontSizeDelta));
+    // Setup paddings.
+    const gfx::Insets kVerticalShortcutLabelInsets(1, 6, 1, 6);
+    const gfx::Insets kHorizontalShortcutLabelInsets(1, 3, 1, 0);
+    const gfx::Insets insets = (orientation == ui::CandidateWindow::VERTICAL
+                                    ? kVerticalShortcutLabelInsets
+                                    : kHorizontalShortcutLabelInsets);
+    SetBorder(views::CreateEmptyBorder(insets.top(), insets.left(),
+                                       insets.bottom(), insets.right()));
+
+    SetElideBehavior(gfx::NO_ELIDE);
   }
-  // TODO(satorux): Maybe we need to use language specific fonts for
-  // candidate_label, like Chinese font for Chinese input method?
+  ShortcutLabel(const ShortcutLabel&) = delete;
+  ShortcutLabel& operator=(const ShortcutLabel&) = delete;
+  ~ShortcutLabel() override = default;
 
-  // Setup paddings.
-  const gfx::Insets kVerticalShortcutLabelInsets(1, 6, 1, 6);
-  const gfx::Insets kHorizontalShortcutLabelInsets(1, 3, 1, 0);
-  const gfx::Insets insets = (orientation == ui::CandidateWindow::VERTICAL
-                                  ? kVerticalShortcutLabelInsets
-                                  : kHorizontalShortcutLabelInsets);
-  shortcut_label->SetBorder(views::CreateEmptyBorder(
-      insets.top(), insets.left(), insets.bottom(), insets.right()));
-
-  // Add decoration based on the orientation.
-  if (orientation == ui::CandidateWindow::VERTICAL) {
-    // Set the background color.
-    SkColor blackish = color_utils::AlphaBlend(
-        SK_ColorBLACK,
-        theme.GetSystemColor(ui::NativeTheme::kColorId_WindowBackground),
-        0.25f);
-    shortcut_label->SetBackground(
-        views::CreateSolidBackground(SkColorSetA(blackish, 0xE0)));
+  // views::Label:
+  void OnThemeChanged() override {
+    Label::OnThemeChanged();
+    // Add decoration based on the orientation.
+    if (orientation_ == ui::CandidateWindow::VERTICAL) {
+      // Set the background color.
+      SkColor blackish = color_utils::AlphaBlend(
+          SK_ColorBLACK,
+          GetNativeTheme()->GetSystemColor(
+              ui::NativeTheme::kColorId_WindowBackground),
+          0.25f);
+      SetBackground(views::CreateSolidBackground(SkColorSetA(blackish, 0xE0)));
+    }
   }
-  shortcut_label->SetElideBehavior(gfx::NO_ELIDE);
 
-  return shortcut_label;
-}
+ private:
+  const ui::CandidateWindow::Orientation orientation_;
+};
+
+BEGIN_METADATA(ShortcutLabel, views::Label)
+END_METADATA
+
+// The label text is not set in this class.
+class AnnotationLabel : public views::Label {
+ public:
+  METADATA_HEADER(AnnotationLabel);
+  AnnotationLabel() {
+    // Change the font size and color.
+    SetFontList(font_list().DeriveWithSizeDelta(kFontSizeDelta));
+    SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    SetElideBehavior(gfx::NO_ELIDE);
+  }
+  AnnotationLabel(const AnnotationLabel&) = delete;
+  AnnotationLabel& operator=(const AnnotationLabel&) = delete;
+  ~AnnotationLabel() override = default;
+
+  // views::Label:
+  void OnThemeChanged() override {
+    Label::OnThemeChanged();
+    SetEnabledColor(GetNativeTheme()->GetSystemColor(
+        ui::NativeTheme::kColorId_LabelSecondaryColor));
+  }
+};
+
+BEGIN_METADATA(AnnotationLabel, views::Label)
+END_METADATA
 
 // Creates the candidate label, and returns it (never returns nullptr).
 // The label text is not set in this function.
@@ -111,24 +147,6 @@
   return candidate_label;
 }
 
-// Creates the annotation label, and return it (never returns nullptr).
-// The label text is not set in this function.
-std::unique_ptr<views::Label> CreateAnnotationLabel(
-    ui::CandidateWindow::Orientation orientation,
-    const ui::NativeTheme& theme) {
-  auto annotation_label = std::make_unique<views::Label>();
-
-  // Change the font size and color.
-  annotation_label->SetFontList(
-      annotation_label->font_list().DeriveWithSizeDelta(kFontSizeDelta));
-  annotation_label->SetEnabledColor(
-      theme.GetSystemColor(ui::NativeTheme::kColorId_LabelSecondaryColor));
-  annotation_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  annotation_label->SetElideBehavior(gfx::NO_ELIDE);
-
-  return annotation_label;
-}
-
 }  // namespace
 
 CandidateView::CandidateView(PressedCallback callback,
@@ -136,17 +154,12 @@
     : views::Button(std::move(callback)), orientation_(orientation) {
   SetBorder(views::CreateEmptyBorder(1, 1, 1, 1));
 
-  const ui::NativeTheme& theme = *GetNativeTheme();
-  shortcut_label_ = AddChildView(CreateShortcutLabel(orientation, theme));
+  shortcut_label_ = AddChildView(std::make_unique<ShortcutLabel>(orientation));
   candidate_label_ = AddChildView(CreateCandidateLabel(orientation));
-  annotation_label_ = AddChildView(CreateAnnotationLabel(orientation, theme));
+  annotation_label_ = AddChildView(std::make_unique<AnnotationLabel>());
 
-  if (orientation == ui::CandidateWindow::VERTICAL) {
-    auto infolist_icon = std::make_unique<views::View>();
-    infolist_icon->SetBackground(views::CreateSolidBackground(
-        theme.GetSystemColor(ui::NativeTheme::kColorId_FocusedBorderColor)));
-    infolist_icon_ = AddChildView(std::move(infolist_icon));
-  }
+  if (orientation == ui::CandidateWindow::VERTICAL)
+    infolist_icon_ = AddChildView(std::make_unique<views::View>());
 
   SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
 }
@@ -300,6 +313,15 @@
                              total_candidates_);
 }
 
+void CandidateView::OnThemeChanged() {
+  Button::OnThemeChanged();
+  if (infolist_icon_) {
+    infolist_icon_->SetBackground(
+        views::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
+            ui::NativeTheme::kColorId_FocusedBorderColor)));
+  }
+}
+
 BEGIN_METADATA(CandidateView, views::Button)
 END_METADATA
 
diff --git a/chrome/browser/chromeos/input_method/ui/candidate_view.h b/chrome/browser/chromeos/input_method/ui/candidate_view.h
index f330871b..72a40448 100644
--- a/chrome/browser/chromeos/input_method/ui/candidate_view.h
+++ b/chrome/browser/chromeos/input_method/ui/candidate_view.h
@@ -53,6 +53,7 @@
   void Layout() override;
   gfx::Size CalculatePreferredSize() const override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  void OnThemeChanged() override;
 
   // The orientation of the candidate view.
   ui::CandidateWindow::Orientation orientation_;
diff --git a/chrome/browser/chromeos/input_method/ui/candidate_window_view.cc b/chrome/browser/chromeos/input_method/ui/candidate_window_view.cc
index 8db9e14..3a333fe 100644
--- a/chrome/browser/chromeos/input_method/ui/candidate_window_view.cc
+++ b/chrome/browser/chromeos/input_method/ui/candidate_window_view.cc
@@ -106,16 +106,22 @@
 
     SetLayoutManager(std::make_unique<views::FillLayout>());
     AddChildView(label_);
+  }
+
+  InformationTextArea(const InformationTextArea&) = delete;
+  InformationTextArea& operator=(const InformationTextArea&) = delete;
+
+  // views::View:
+  void OnThemeChanged() override {
+    View::OnThemeChanged();
     SetBackground(views::CreateSolidBackground(
         color_utils::AlphaBlend(SK_ColorBLACK,
                                 GetNativeTheme()->GetSystemColor(
                                     ui::NativeTheme::kColorId_WindowBackground),
                                 0.0625f)));
+    UpdateBorder();
   }
 
-  InformationTextArea(const InformationTextArea&) = delete;
-  InformationTextArea& operator=(const InformationTextArea&) = delete;
-
   // Sets the text alignment.
   void SetAlignment(gfx::HorizontalAlignment alignment) {
     label_->SetHorizontalAlignment(alignment);
@@ -126,8 +132,15 @@
 
   // Sets the border thickness for top/bottom.
   void SetBorderFromPosition(BorderPosition position) {
+    position_ = position;
+    UpdateBorder();
+  }
+
+  void UpdateBorder() {
+    if (!position_ || !GetWidget())
+      return;
     SetBorder(views::CreateSolidSidedBorder(
-        (position == TOP) ? 1 : 0, 0, (position == BOTTOM) ? 1 : 0, 0,
+        (position_ == TOP) ? 1 : 0, 0, (position_ == BOTTOM) ? 1 : 0, 0,
         GetNativeTheme()->GetSystemColor(
             ui::NativeTheme::kColorId_MenuBorderColor)));
   }
@@ -142,6 +155,7 @@
  private:
   views::Label* label_;
   int min_width_;
+  absl::optional<BorderPosition> position_;
 };
 
 BEGIN_METADATA(InformationTextArea, views::View)
@@ -169,10 +183,6 @@
   // with what CandidateWindowView expects.
   set_use_round_corners(false);
 
-  SetBorder(views::CreateSolidBorder(
-      1, GetNativeTheme()->GetSystemColor(
-             ui::NativeTheme::kColorId_MenuBorderColor)));
-
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical));
   auxiliary_text_ = new InformationTextArea(gfx::ALIGN_RIGHT, 0);
@@ -214,6 +224,13 @@
   return widget;
 }
 
+void CandidateWindowView::OnThemeChanged() {
+  BubbleDialogDelegateView::OnThemeChanged();
+  SetBorder(views::CreateSolidBorder(
+      1, GetNativeTheme()->GetSystemColor(
+             ui::NativeTheme::kColorId_MenuBorderColor)));
+}
+
 void CandidateWindowView::UpdateVisibility() {
   if (candidate_area_->GetVisible() || auxiliary_text_->GetVisible() ||
       preedit_->GetVisible()) {
diff --git a/chrome/browser/chromeos/input_method/ui/candidate_window_view.h b/chrome/browser/chromeos/input_method/ui/candidate_window_view.h
index 9064a180..527a90e 100644
--- a/chrome/browser/chromeos/input_method/ui/candidate_window_view.h
+++ b/chrome/browser/chromeos/input_method/ui/candidate_window_view.h
@@ -39,6 +39,9 @@
   ~CandidateWindowView() override;
   views::Widget* InitWidget();
 
+  // views::BubbleDialogDelegateView:
+  void OnThemeChanged() override;
+
   // Adds the given observer. The ownership is not transferred.
   void AddObserver(Observer* observer) { observers_.AddObserver(observer); }
 
diff --git a/chrome/browser/chromeos/policy/fuzzer/policy_fuzzer.cc b/chrome/browser/chromeos/policy/fuzzer/policy_fuzzer.cc
index fff83ee9..5eb493a9 100644
--- a/chrome/browser/chromeos/policy/fuzzer/policy_fuzzer.cc
+++ b/chrome/browser/chromeos/policy/fuzzer/policy_fuzzer.cc
@@ -15,7 +15,7 @@
 #include "base/path_service.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
-#include "chrome/browser/ash/dbus/dbus_helper.h"
+#include "chrome/browser/ash/dbus/ash_dbus_helper.h"
 #include "chrome/browser/ash/settings/device_settings_provider.h"
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/chromeos/policy/fuzzer/policy_fuzzer.pb.h"
diff --git a/chrome/browser/chromeos/policy/status_collector/affiliated_session_service.cc b/chrome/browser/chromeos/policy/status_collector/affiliated_session_service.cc
deleted file mode 100644
index 5162988d..0000000
--- a/chrome/browser/chromeos/policy/status_collector/affiliated_session_service.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/policy/status_collector/affiliated_session_service.h"
-
-#include "base/logging.h"
-#include "chrome/browser/ash/profiles/profile_helper.h"
-
-namespace policy {
-
-namespace {
-
-constexpr base::TimeDelta kMinimumSuspendDuration =
-    base::TimeDelta::FromMinutes(1);
-
-bool IsPrimaryAndAffiliated(Profile* profile) {
-  user_manager::User* user =
-      chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
-  bool is_primary = chromeos::ProfileHelper::Get()->IsPrimaryProfile(profile);
-  bool is_affiliated = user && user->IsAffiliated();
-  if (!is_primary || !is_affiliated) {
-    VLOG(1) << "The profile for the primary user is not associated with an "
-               "affiliated user.";
-  }
-  return is_primary && is_affiliated;
-}
-
-}  // namespace
-
-AffiliatedSessionService::AffiliatedSessionService(base::Clock* clock)
-    : clock_(clock), session_manager_(session_manager::SessionManager::Get()) {
-  if (session_manager_) {
-    // To alleviate tight coupling in unit tests to DeviceStatusCollector.
-    session_manager_observation_.Observe(session_manager_);
-    is_session_locked_ = session_manager_->IsScreenLocked();
-  }
-  power_manager_observation_.Observe(chromeos::PowerManagerClient::Get());
-}
-
-AffiliatedSessionService::~AffiliatedSessionService() = default;
-
-void AffiliatedSessionService::AddObserver(
-    AffiliatedSessionService::Observer* observer) {
-  observers_.AddObserver(observer);
-}
-
-void AffiliatedSessionService::RemoveObserver(
-    AffiliatedSessionService::Observer* observer) {
-  observers_.RemoveObserver(observer);
-}
-
-void AffiliatedSessionService::OnSessionStateChanged() {
-  bool is_session_locked = session_manager_->IsScreenLocked();
-  if (is_session_locked_ == is_session_locked) {
-    return;
-  }
-  is_session_locked_ = is_session_locked;
-
-  if (is_session_locked_) {
-    for (auto& observer : observers_) {
-      observer.OnLocked();
-    }
-  } else {
-    for (auto& observer : observers_) {
-      observer.OnUnlocked();
-    }
-  }
-}
-
-void AffiliatedSessionService::OnUserProfileLoaded(
-    const AccountId& account_id) {
-  Profile* profile =
-      chromeos::ProfileHelper::Get()->GetProfileByAccountId(account_id);
-  if (!IsPrimaryAndAffiliated(profile)) {
-    return;
-  }
-  profile_observations_.AddObservation(profile);
-  for (auto& observer : observers_) {
-    observer.OnAffiliatedLogin(profile);
-  }
-}
-
-void AffiliatedSessionService::OnProfileWillBeDestroyed(Profile* profile) {
-  is_session_locked_ = false;
-  if (!IsPrimaryAndAffiliated(profile)) {
-    return;
-  }
-  for (auto& observer : observers_) {
-    observer.OnAffiliatedLogout(profile);
-  }
-  profile_observations_.RemoveObservation(profile);
-}
-
-void AffiliatedSessionService::SuspendDone(base::TimeDelta sleep_duration) {
-  if (sleep_duration < kMinimumSuspendDuration) {
-    return;
-  }
-  for (auto& observer : observers_) {
-    observer.OnResumeActive(clock_->Now() - sleep_duration);
-  }
-}
-
-}  // namespace policy
diff --git a/chrome/browser/chromeos/policy/status_collector/affiliated_session_service.h b/chrome/browser/chromeos/policy/status_collector/affiliated_session_service.h
deleted file mode 100644
index ca2b1b2..0000000
--- a/chrome/browser/chromeos/policy/status_collector/affiliated_session_service.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// 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 CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_AFFILIATED_SESSION_SERVICE_H_
-#define CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_AFFILIATED_SESSION_SERVICE_H_
-
-#include "base/observer_list.h"
-#include "base/observer_list_types.h"
-#include "base/scoped_multi_source_observation.h"
-#include "base/scoped_observation.h"
-#include "base/time/clock.h"
-#include "base/time/default_clock.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_observer.h"
-#include "chromeos/dbus/power/power_manager_client.h"
-#include "components/account_id/account_id.h"
-#include "components/session_manager/core/session_manager.h"
-#include "components/session_manager/core/session_manager_observer.h"
-
-namespace policy {
-
-class AffiliatedSessionService : public session_manager::SessionManagerObserver,
-                                 public ProfileObserver,
-                                 public chromeos::PowerManagerClient::Observer {
- public:
-  class Observer : public base::CheckedObserver {
-   public:
-    // Occurs when an affiliated primary user has logged in.
-    virtual void OnAffiliatedLogin(Profile* profile) {}
-
-    // Occurs when an affiliated primary user has logged out.
-    virtual void OnAffiliatedLogout(Profile* profile) {}
-
-    // Occurs when the active user has locked the user session.
-    virtual void OnLocked() {}
-
-    // Occurs when the active user has unlocked the user session.
-    virtual void OnUnlocked() {}
-
-    // Occurs when the device recovers from a suspend state, where
-    // |suspend_time| is the time when the suspend state
-    // first occurred. Short duration suspends are not reported.
-    virtual void OnResumeActive(base::Time suspend_time) {}
-  };
-
-  explicit AffiliatedSessionService(
-      base::Clock* clock = base::DefaultClock::GetInstance());
-  AffiliatedSessionService(const AffiliatedSessionService&) = delete;
-  AffiliatedSessionService& operator=(const AffiliatedSessionService&) = delete;
-  ~AffiliatedSessionService() override;
-
-  void AddObserver(Observer* observer);
-
-  void RemoveObserver(Observer* observer);
-
-  // session_manager::SessionManagerObserver::Observer
-  void OnSessionStateChanged() override;
-  void OnUserProfileLoaded(const AccountId& account_id) override;
-
-  // ProfileObserver
-  void OnProfileWillBeDestroyed(Profile* profile) override;
-
-  // chromeos::PowerManagerClient::Observer
-  void SuspendDone(base::TimeDelta sleep_duration) override;
-
- private:
-  bool is_session_locked_;
-
-  base::Clock* clock_;
-
-  base::ObserverList<Observer> observers_;
-
-  session_manager::SessionManager* const session_manager_;
-
-  base::ScopedMultiSourceObservation<Profile, ProfileObserver>
-      profile_observations_{this};
-  base::ScopedObservation<session_manager::SessionManager,
-                          session_manager::SessionManagerObserver>
-      session_manager_observation_{this};
-  base::ScopedObservation<chromeos::PowerManagerClient,
-                          chromeos::PowerManagerClient::Observer>
-      power_manager_observation_{this};
-};
-
-}  // namespace policy
-
-#endif  // CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_AFFILIATED_SESSION_SERVICE_H_
diff --git a/chrome/browser/chromeos/policy/status_collector/app_info_generator.cc b/chrome/browser/chromeos/policy/status_collector/app_info_generator.cc
index ae71d95..0bacacc3 100644
--- a/chrome/browser/chromeos/policy/status_collector/app_info_generator.cc
+++ b/chrome/browser/chromeos/policy/status_collector/app_info_generator.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/chromeos/policy/status_collector/app_info_generator.h"
 
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/web_app_provider_factory.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
@@ -19,6 +20,18 @@
 
 namespace {
 
+bool IsPrimaryAndAffiliated(Profile* profile) {
+  user_manager::User* user =
+      chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+  bool is_primary = chromeos::ProfileHelper::Get()->IsPrimaryProfile(profile);
+  bool is_affiliated = user && user->IsAffiliated();
+  if (!is_primary || !is_affiliated) {
+    VLOG(1) << "The profile for the primary user is not associated with an "
+               "affiliated user.";
+  }
+  return is_primary && is_affiliated;
+}
+
 em::AppInfo::Status ExtractStatus(const apps::mojom::Readiness readiness) {
   switch (readiness) {
     case apps::mojom::Readiness::kReady:
@@ -77,10 +90,15 @@
 AppInfoGenerator::AppInfoProvider::~AppInfoProvider() = default;
 
 AppInfoGenerator::AppInfoGenerator(
+    ManagedSessionService* managed_session_service,
     base::TimeDelta max_stored_past_activity_interval,
     base::Clock* clock)
     : max_stored_past_activity_interval_(max_stored_past_activity_interval),
-      clock_(*clock) {}
+      clock_(*clock) {
+  if (managed_session_service) {
+    managed_session_observation_.Observe(managed_session_service);
+  }
+}
 
 AppInfoGenerator::AppInstances::AppInstances(const base::Time start_time_)
     : start_time(start_time_) {}
@@ -152,7 +170,11 @@
   SetIdleDurationsToOpen();
 }
 
-void AppInfoGenerator::OnAffiliatedLogin(Profile* profile) {
+void AppInfoGenerator::OnLogin(Profile* profile) {
+  if (!IsPrimaryAndAffiliated(profile)) {
+    return;
+  }
+
   if (!apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile)) {
     VLOG(1) << "No apps available. Will not track usage.";
     return;
@@ -167,7 +189,11 @@
   }
 }
 
-void AppInfoGenerator::OnAffiliatedLogout(Profile* profile) {
+void AppInfoGenerator::OnLogout(Profile* profile) {
+  if (!IsPrimaryAndAffiliated(profile)) {
+    return;
+  }
+
   if (provider_) {
     if (should_report_) {
       provider_->app_service_proxy.InstanceRegistry().RemoveObserver(this);
diff --git a/chrome/browser/chromeos/policy/status_collector/app_info_generator.h b/chrome/browser/chromeos/policy/status_collector/app_info_generator.h
index c315a1b..89d0b601 100644
--- a/chrome/browser/chromeos/policy/status_collector/app_info_generator.h
+++ b/chrome/browser/chromeos/policy/status_collector/app_info_generator.h
@@ -13,7 +13,7 @@
 #include "base/time/default_clock.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/chromeos/policy/status_collector/activity_storage.h"
-#include "chrome/browser/chromeos/policy/status_collector/affiliated_session_service.h"
+#include "chrome/browser/chromeos/policy/status_collector/managed_session_service.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/services/app_service/public/cpp/instance.h"
@@ -30,11 +30,12 @@
 // A class that is responsible for collecting application inventory and usage
 // information.
 class AppInfoGenerator : public apps::InstanceRegistry::Observer,
-                         public AffiliatedSessionService::Observer {
+                         public ManagedSessionService::Observer {
  public:
   using Result = absl::optional<std::vector<enterprise_management::AppInfo>>;
 
   explicit AppInfoGenerator(
+      ManagedSessionService* managed_session_service,
       base::TimeDelta max_stored_past_activity_interval,
       base::Clock* clock = base::DefaultClock::GetInstance());
   AppInfoGenerator(const AppInfoGenerator&) = delete;
@@ -59,9 +60,9 @@
   // up until the current time, so it may be reported.
   void OnWillReport();
 
-  // AffiliatedSessionManager::Observer
-  void OnAffiliatedLogin(Profile* profile) override;
-  void OnAffiliatedLogout(Profile* profile) override;
+  // ManagedSessionService::Observer
+  void OnLogin(Profile* profile) override;
+  void OnLogout(Profile* profile) override;
   void OnLocked() override;
   void OnUnlocked() override;
   void OnResumeActive(base::Time suspend_time) override;
@@ -122,6 +123,10 @@
   base::TimeDelta max_stored_past_activity_interval_;
 
   const base::Clock& clock_;
+
+  base::ScopedObservation<ManagedSessionService,
+                          ManagedSessionService::Observer>
+      managed_session_observation_{this};
 };
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/status_collector/app_info_generator_unittest.cc b/chrome/browser/chromeos/policy/status_collector/app_info_generator_unittest.cc
index 5b646ea..cb504b0e 100644
--- a/chrome/browser/chromeos/policy/status_collector/app_info_generator_unittest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/app_info_generator_unittest.cc
@@ -166,11 +166,24 @@
     GetInstanceRegistry().OnInstances(deltas);
   }
 
+  std::unique_ptr<TestingProfile> CreateProfile(const AccountId& account_id,
+                                                bool is_affiliated = true) {
+    TestingProfile::Builder profile_builder;
+    profile_builder.SetProfileName(account_id.GetUserEmail());
+    auto profile = profile_builder.Build();
+    user_manager_->AddUserWithAffiliationAndTypeAndProfile(
+        account_id, is_affiliated, user_manager::UserType::USER_TYPE_REGULAR,
+        profile.get());
+    return profile;
+  }
+
   void SetUp() override {
     auto user_manager = std::make_unique<ash::FakeChromeUserManager>();
+    user_manager_ = user_manager.get();
     user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
         std::move(user_manager));
-    profile_ = std::make_unique<TestingProfile>();
+    account_id_ = AccountId::FromUserEmail("affiliated@managed.com");
+    profile_ = CreateProfile(account_id_);
     test_clock().SetNow(MakeLocalTime("25-MAR-2020 1:30am"));
 
     web_app::WebAppProviderFactory::GetInstance()->SetTestingFactoryAndUse(
@@ -206,13 +219,13 @@
   std::unique_ptr<AppInfoGenerator> GetGenerator(
       base::TimeDelta max_stored_past_activity_interval =
           base::TimeDelta::FromDays(0)) {
-    return std::make_unique<AppInfoGenerator>(max_stored_past_activity_interval,
-                                              &test_clock());
+    return std::make_unique<AppInfoGenerator>(
+        nullptr, max_stored_past_activity_interval, &test_clock());
   }
 
   std::unique_ptr<AppInfoGenerator> GetReadyGenerator() {
     auto generator = GetGenerator();
-    generator->OnAffiliatedLogin(profile());
+    generator->OnLogin(profile());
     generator->OnReportingChanged(true);
     return generator;
   }
@@ -239,6 +252,10 @@
 
   Profile* profile() { return profile_.get(); }
 
+  ash::FakeChromeUserManager* user_manager() { return user_manager_; }
+
+  AccountId account_id() { return account_id_; }
+
   static auto EqActivity(const base::Time& start_time,
                          const base::Time& end_time) {
     return AllOf(
@@ -265,8 +282,10 @@
   apps::ScopedOmitPluginVmAppsForTesting
       scoped_omit_plugin_vm_apps_for_testing_;
   content::BrowserTaskEnvironment task_environment_;
+  AccountId account_id_;
   std::unique_ptr<TestingProfile> profile_;
   web_app::WebAppRegistrarMutable* app_registrar_;
+  ash::FakeChromeUserManager* user_manager_;
   std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
   TestingPrefServiceSimple pref_service_;
 
@@ -283,6 +302,7 @@
   PushApp("c", "ThirdApp", apps::mojom::Readiness::kUninstalledByUser, "",
           apps::mojom::AppType::kCrostini);
 
+  user_manager()->LoginUser(account_id(), true);
   auto generator = GetReadyGenerator();
   auto result = generator->Generate();
 
@@ -297,6 +317,7 @@
 }
 
 TEST_F(AppInfoGeneratorTest, GenerateWebApp) {
+  user_manager()->LoginUser(account_id(), true);
   auto generator = GetReadyGenerator();
   PushApp("c", "App", apps::mojom::Readiness::kUninstalledByUser, "",
           apps::mojom::AppType::kWeb);
@@ -321,6 +342,7 @@
 }
 
 TEST_F(AppInfoGeneratorTest, MultipleInstances) {
+  user_manager()->LoginUser(account_id(), true);
   auto generator = GetReadyGenerator();
   PushApp("a", "FirstApp", apps::mojom::Readiness::kDisabledByPolicy, "1.1",
           apps::mojom::AppType::kArc);
@@ -347,18 +369,54 @@
 }
 
 TEST_F(AppInfoGeneratorTest, ShouldNotReport) {
+  user_manager()->LoginUser(account_id(), true);
   PushApp("a", "FirstApp", apps::mojom::Readiness::kDisabledByPolicy, "1.1",
           apps::mojom::AppType::kArc);
 
   auto generator = GetGenerator();
   generator->OnReportingChanged(false);
-  generator->OnAffiliatedLogin(profile());
+  generator->OnLogin(profile());
+  auto result = generator->Generate();
+
+  EXPECT_FALSE(result.has_value());
+}
+
+TEST_F(AppInfoGeneratorTest, UnaffiliatedUser) {
+  auto unaffiliated_account_id =
+      AccountId::FromUserEmail("unaffiliated@unmanaged.com");
+  auto unaffiliated_profile =
+      CreateProfile(unaffiliated_account_id, /* is_affiliated= */ false);
+  user_manager()->LoginUser(unaffiliated_account_id, true);
+  PushApp("a", "FirstApp", apps::mojom::Readiness::kDisabledByPolicy, "1.1",
+          apps::mojom::AppType::kArc);
+
+  auto generator = GetGenerator();
+  generator->OnReportingChanged(true);
+  generator->OnLogin(unaffiliated_profile.get());
+  auto result = generator->Generate();
+
+  EXPECT_FALSE(result.has_value());
+}
+
+TEST_F(AppInfoGeneratorTest, SecondaryUser) {
+  user_manager()->LoginUser(account_id(), true);
+  auto secondary_account_id = AccountId::FromUserEmail("secondary@managed.com");
+  auto secondary_profile =
+      CreateProfile(secondary_account_id, /* is_affiliated= */ true);
+  user_manager()->LoginUser(secondary_account_id, true);
+  PushApp("a", "FirstApp", apps::mojom::Readiness::kDisabledByPolicy, "1.1",
+          apps::mojom::AppType::kArc);
+
+  auto generator = GetGenerator();
+  generator->OnReportingChanged(true);
+  generator->OnLogin(secondary_profile.get());
   auto result = generator->Generate();
 
   EXPECT_FALSE(result.has_value());
 }
 
 TEST_F(AppInfoGeneratorTest, OnReportedSuccessfully) {
+  user_manager()->LoginUser(account_id(), true);
   auto generator = GetReadyGenerator();
   PushApp("a", "FirstApp", apps::mojom::Readiness::kDisabledByPolicy, "1.1",
           apps::mojom::AppType::kArc);
@@ -388,6 +446,7 @@
 }
 
 TEST_F(AppInfoGeneratorTest, OnWillReport) {
+  user_manager()->LoginUser(account_id(), true);
   auto generator = GetReadyGenerator();
   PushApp("a", "FirstApp", apps::mojom::Readiness::kDisabledByPolicy, "1.1",
           apps::mojom::AppType::kArc);
@@ -426,12 +485,13 @@
 }
 
 TEST_F(AppInfoGeneratorTest, OnLogoutOnLogin) {
+  user_manager()->LoginUser(account_id(), true);
   PushApp("a", "FirstApp", apps::mojom::Readiness::kDisabledByPolicy, "1.1",
           apps::mojom::AppType::kArc);
   auto generator = GetGenerator();
   generator->OnReportingChanged(true);
-  generator->OnAffiliatedLogin(profile());
-  generator->OnAffiliatedLogout(profile());
+  generator->OnLogin(profile());
+  generator->OnLogout(profile());
   Instance app_instance("a");
   test_clock().SetNow(MakeLocalTime("29-MAR-2020 1:30pm"));
   PushAppInstance(app_instance, apps::InstanceState::kStarted);
@@ -443,7 +503,7 @@
 
   EXPECT_FALSE(result.has_value());
 
-  generator->OnAffiliatedLogin(profile());
+  generator->OnLogin(profile());
 
   test_clock().SetNow(MakeLocalTime("30-MAR-2020 2:30pm"));
   PushAppInstance(app_instance, apps::InstanceState::kStarted);
@@ -462,6 +522,7 @@
 }
 
 TEST_F(AppInfoGeneratorTest, OnLocked) {
+  user_manager()->LoginUser(account_id(), true);
   auto generator = GetReadyGenerator();
   PushApp("a", "FirstApp", apps::mojom::Readiness::kDisabledByPolicy, "1.1",
           apps::mojom::AppType::kArc);
@@ -484,6 +545,7 @@
 }
 
 TEST_F(AppInfoGeneratorTest, OnUnlocked) {
+  user_manager()->LoginUser(account_id(), true);
   auto generator = GetReadyGenerator();
   PushApp("a", "FirstApp", apps::mojom::Readiness::kDisabledByPolicy, "1.1",
           apps::mojom::AppType::kArc);
@@ -512,6 +574,7 @@
 }
 
 TEST_F(AppInfoGeneratorTest, OnResumeActive) {
+  user_manager()->LoginUser(account_id(), true);
   auto generator = GetReadyGenerator();
   PushApp("a", "FirstApp", apps::mojom::Readiness::kDisabledByPolicy, "1.1",
           apps::mojom::AppType::kArc);
@@ -540,6 +603,7 @@
 }
 
 TEST_F(AppInfoGeneratorTest, OnLoginRemoveOldUsage) {
+  user_manager()->LoginUser(account_id(), true);
   PushApp("a", "FirstApp", apps::mojom::Readiness::kDisabledByPolicy, "1.1",
           apps::mojom::AppType::kArc);
   PushApp("b", "SecondApp", apps::mojom::Readiness::kReady, "1.2",
@@ -548,7 +612,7 @@
       1);  // Exclude all past usage except for UTC today and yesterday.
   auto generator = GetGenerator(max_days_past);
   generator->OnReportingChanged(true);
-  generator->OnAffiliatedLogin(profile());
+  generator->OnLogin(profile());
 
   Instance app_instance1("a");
   test_clock().SetNow(MakeLocalTime("28-MAR-2020 1:30am"));
@@ -561,9 +625,9 @@
   test_clock().SetNow(MakeLocalTime("29-MAR-2020 3:30am"));
   PushAppInstance(app_instance2, apps::InstanceState::kDestroyed);
 
-  generator->OnAffiliatedLogout(profile());
+  generator->OnLogout(profile());
   test_clock().SetNow(MakeLocalTime("30-MAR-2020 11:00am"));
-  generator->OnAffiliatedLogin(profile());
+  generator->OnLogin(profile());
 
   auto result = generator->Generate();
 
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
index 127983a4..3d8f2a4 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
@@ -1443,7 +1443,9 @@
       graphics_status_fetcher_(graphics_status_fetcher),
       crash_report_info_fetcher_(crash_report_info_fetcher),
       power_manager_(chromeos::PowerManagerClient::Get()),
-      app_info_generator_(kMaxStoredPastActivityInterval, clock_) {
+      app_info_generator_(&managed_session_service_,
+                          kMaxStoredPastActivityInterval,
+                          clock_) {
   // protected fields of `StatusCollector`.
   max_stored_past_activity_interval_ = kMaxStoredPastActivityInterval;
   max_stored_future_activity_interval_ = kMaxStoredFutureActivityInterval;
@@ -1547,8 +1549,7 @@
   stats_reporting_pref_subscription_ = cros_settings_->AddSettingsObserver(
       chromeos::kStatsReportingPref, callback);
 
-  affiliated_session_service_.AddObserver(&app_info_generator_);
-
+  // TODO(b/191986061):: consider using ScopedObservation instead.
   power_manager_->AddObserver(this);
 
   // Fetch the current values of the policies.
@@ -1603,7 +1604,6 @@
 
 DeviceStatusCollector::~DeviceStatusCollector() {
   power_manager_->RemoveObserver(this);
-  affiliated_session_service_.RemoveObserver(&app_info_generator_);
 }
 
 // static
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector.h b/chrome/browser/chromeos/policy/status_collector/device_status_collector.h
index b64d0cd7..7816073 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector.h
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector.h
@@ -211,8 +211,8 @@
 
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
-  AffiliatedSessionService* GetAffiliatedSessionServiceForTesting() {
-    return &affiliated_session_service_;
+  ManagedSessionService* GetManagedSessionServiceForTesting() {
+    return &managed_session_service_;
   }
 
   // How often to poll to see if the user is idle.
@@ -480,7 +480,7 @@
   base::CallbackListSubscription app_info_subscription_;
   base::CallbackListSubscription stats_reporting_pref_subscription_;
 
-  AffiliatedSessionService affiliated_session_service_;
+  ManagedSessionService managed_session_service_;
 
   AppInfoGenerator app_info_generator_;
 
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
index 01ba596..a8e183d8 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
@@ -216,7 +216,7 @@
 constexpr uint32_t kFakeTotalMemory = 1287312;
 constexpr uint32_t kFakeFreeMemory = 981239;
 constexpr uint32_t kFakeAvailableMemory = 98719321;
-constexpr uint32_t kFakePageFaults = 896123761;
+constexpr uint64_t kFakePageFaults = 896123761;
 // Backlight test values:
 constexpr char kFakeBacklightPath[] = "/sys/class/backlight/fake_backlight";
 constexpr uint32_t kFakeMaxBrightness = 769;
@@ -947,7 +947,7 @@
     options->crash_report_info_fetcher =
         base::BindRepeating(&GetEmptyCrashReportInfo);
     options->app_info_generator = std::make_unique<policy::AppInfoGenerator>(
-        base::TimeDelta::FromDays(0));
+        nullptr, base::TimeDelta::FromDays(0));
     return options;
   }
 
@@ -3608,8 +3608,8 @@
   MockRegularUserWithAffiliation(account_id, true);
   scoped_testing_cros_settings_.device_settings()->SetBoolean(
       chromeos::kReportDeviceAppInfo, true);
-  status_collector_->GetAffiliatedSessionServiceForTesting()
-      ->OnUserProfileLoaded(account_id);
+  status_collector_->GetManagedSessionServiceForTesting()->OnUserProfileLoaded(
+      account_id);
   auto* app_proxy =
       apps::AppServiceProxyFactory::GetForProfile(testing_profile_.get());
   auto app1 = apps::mojom::App::New();
diff --git a/chrome/browser/chromeos/policy/status_collector/managed_session_service.cc b/chrome/browser/chromeos/policy/status_collector/managed_session_service.cc
new file mode 100644
index 0000000..35184f3
--- /dev/null
+++ b/chrome/browser/chromeos/policy/status_collector/managed_session_service.cc
@@ -0,0 +1,110 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/policy/status_collector/managed_session_service.h"
+
+#include "base/logging.h"
+#include "chrome/browser/ash/login/existing_user_controller.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#include "components/user_manager/user_manager.h"
+
+namespace policy {
+
+namespace {
+
+constexpr base::TimeDelta kMinimumSuspendDuration =
+    base::TimeDelta::FromMinutes(1);
+
+}  // namespace
+
+ManagedSessionService::ManagedSessionService(base::Clock* clock)
+    : clock_(clock), session_manager_(session_manager::SessionManager::Get()) {
+  if (session_manager_) {
+    // To alleviate tight coupling in unit tests to DeviceStatusCollector.
+    session_manager_observation_.Observe(session_manager_);
+    is_session_locked_ = session_manager_->IsScreenLocked();
+  }
+  if (user_manager::UserManager::IsInitialized()) {
+    authenticator_observation_.Observe(ash::UserSessionManager::GetInstance());
+  }
+  power_manager_observation_.Observe(chromeos::PowerManagerClient::Get());
+}
+
+ManagedSessionService::~ManagedSessionService() {
+  if (ash::ExistingUserController::current_controller()) {
+    ash::ExistingUserController::current_controller()
+        ->RemoveLoginStatusConsumer(this);
+  }
+}
+
+void ManagedSessionService::AddObserver(
+    ManagedSessionService::Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void ManagedSessionService::RemoveObserver(
+    ManagedSessionService::Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void ManagedSessionService::OnSessionStateChanged() {
+  bool is_session_locked = session_manager_->IsScreenLocked();
+  if (is_session_locked_ == is_session_locked) {
+    return;
+  }
+  is_session_locked_ = is_session_locked;
+
+  if (is_session_locked_) {
+    for (auto& observer : observers_) {
+      observer.OnLocked();
+    }
+  } else {
+    for (auto& observer : observers_) {
+      observer.OnUnlocked();
+    }
+  }
+}
+
+void ManagedSessionService::OnUserProfileLoaded(const AccountId& account_id) {
+  Profile* profile =
+      chromeos::ProfileHelper::Get()->GetProfileByAccountId(account_id);
+  profile_observations_.AddObservation(profile);
+  for (auto& observer : observers_) {
+    observer.OnLogin(profile);
+  }
+}
+
+void ManagedSessionService::OnProfileWillBeDestroyed(Profile* profile) {
+  is_session_locked_ = false;
+  for (auto& observer : observers_) {
+    observer.OnLogout(profile);
+  }
+  profile_observations_.RemoveObservation(profile);
+}
+
+void ManagedSessionService::SuspendDone(base::TimeDelta sleep_duration) {
+  if (sleep_duration < kMinimumSuspendDuration) {
+    return;
+  }
+  for (auto& observer : observers_) {
+    observer.OnResumeActive(clock_->Now() - sleep_duration);
+  }
+}
+
+void ManagedSessionService::OnAuthAttemptStarted() {
+  if (ash::ExistingUserController::current_controller()) {
+    ash::ExistingUserController::current_controller()
+        ->RemoveLoginStatusConsumer(this);
+    ash::ExistingUserController::current_controller()->AddLoginStatusConsumer(
+        this);
+  }
+}
+
+void ManagedSessionService::OnAuthFailure(const chromeos::AuthFailure& error) {
+  for (auto& observer : observers_) {
+    observer.OnLoginFailure(error);
+  }
+}
+
+}  // namespace policy
diff --git a/chrome/browser/chromeos/policy/status_collector/managed_session_service.h b/chrome/browser/chromeos/policy/status_collector/managed_session_service.h
new file mode 100644
index 0000000..52f6d4bb
--- /dev/null
+++ b/chrome/browser/chromeos/policy/status_collector/managed_session_service.h
@@ -0,0 +1,107 @@
+// 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 CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_MANAGED_SESSION_SERVICE_H_
+#define CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_MANAGED_SESSION_SERVICE_H_
+
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
+#include "base/scoped_multi_source_observation.h"
+#include "base/scoped_observation.h"
+#include "base/time/clock.h"
+#include "base/time/default_clock.h"
+#include "chrome/browser/ash/login/session/user_session_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_observer.h"
+#include "chromeos/dbus/power/power_manager_client.h"
+#include "chromeos/login/auth/auth_status_consumer.h"
+#include "components/account_id/account_id.h"
+#include "components/session_manager/core/session_manager.h"
+#include "components/session_manager/core/session_manager_observer.h"
+
+namespace policy {
+
+class ManagedSessionService : public session_manager::SessionManagerObserver,
+                              public ProfileObserver,
+                              public chromeos::PowerManagerClient::Observer,
+                              public chromeos::AuthStatusConsumer,
+                              public ash::UserAuthenticatorObserver {
+ public:
+  class Observer : public base::CheckedObserver {
+   public:
+    // Occurs when a user's login attempt fails.
+    virtual void OnLoginFailure(const chromeos::AuthFailure& error) {}
+
+    // Occurs when a user has logged in.
+    virtual void OnLogin(Profile* profile) {}
+
+    // Occurs when a user has logged out.
+    virtual void OnLogout(Profile* profile) {}
+
+    // Occurs when the active user has locked the user session.
+    virtual void OnLocked() {}
+
+    // Occurs when the active user has unlocked the user session.
+    virtual void OnUnlocked() {}
+
+    // Occurs when the device recovers from a suspend state, where
+    // |suspend_time| is the time when the suspend state
+    // first occurred. Short duration suspends are not reported.
+    virtual void OnResumeActive(base::Time suspend_time) {}
+  };
+
+  explicit ManagedSessionService(
+      base::Clock* clock = base::DefaultClock::GetInstance());
+  ManagedSessionService(const ManagedSessionService&) = delete;
+  ManagedSessionService& operator=(const ManagedSessionService&) = delete;
+  ~ManagedSessionService() override;
+
+  void AddObserver(Observer* observer);
+
+  void RemoveObserver(Observer* observer);
+
+  // session_manager::SessionManagerObserver::Observer
+  void OnSessionStateChanged() override;
+  void OnUserProfileLoaded(const AccountId& account_id) override;
+
+  // ProfileObserver
+  void OnProfileWillBeDestroyed(Profile* profile) override;
+
+  // chromeos::PowerManagerClient::Observer
+  void SuspendDone(base::TimeDelta sleep_duration) override;
+
+  void OnAuthSuccess(const ash::UserContext& user_context) override {}
+
+  void OnAuthFailure(const chromeos::AuthFailure& error) override;
+
+  void OnAuthAttemptStarted() override;
+
+ private:
+  bool is_session_locked_;
+
+  base::Clock* clock_;
+
+  base::ObserverList<Observer> observers_;
+
+  session_manager::SessionManager* const session_manager_;
+
+  base::ScopedMultiSourceObservation<Profile, ProfileObserver>
+      profile_observations_{this};
+  base::ScopedObservation<session_manager::SessionManager,
+                          session_manager::SessionManagerObserver>
+      session_manager_observation_{this};
+  base::ScopedObservation<chromeos::PowerManagerClient,
+                          chromeos::PowerManagerClient::Observer>
+      power_manager_observation_{this};
+  base::ScopedObservation<
+      ash::UserSessionManager,
+      ash::UserAuthenticatorObserver,
+      &ash::UserSessionManager::AddUserAuthenticatorObserver,
+      &ash::UserSessionManager::RemoveUserAuthenticatorObserver>
+      authenticator_observation_{this};
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_MANAGED_SESSION_SERVICE_H_
diff --git a/chrome/browser/chromeos/policy/status_collector/affiliated_session_service_unittest.cc b/chrome/browser/chromeos/policy/status_collector/managed_session_service_unittest.cc
similarity index 73%
rename from chrome/browser/chromeos/policy/status_collector/affiliated_session_service_unittest.cc
rename to chrome/browser/chromeos/policy/status_collector/managed_session_service_unittest.cc
index cffd115..da195126 100644
--- a/chrome/browser/chromeos/policy/status_collector/affiliated_session_service_unittest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/managed_session_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/chromeos/policy/status_collector/affiliated_session_service.h"
+#include "chrome/browser/chromeos/policy/status_collector/managed_session_service.h"
 
 #include "base/test/simple_test_clock.h"
 #include "chrome/browser/ash/login/users/chrome_user_manager.h"
@@ -17,9 +17,9 @@
 
 namespace policy {
 
-class AffiliatedSessionServiceTest
+class ManagedSessionServiceTest
     : public ::testing::Test,
-      public policy::AffiliatedSessionService::Observer {
+      public policy::ManagedSessionService::Observer {
  protected:
   using SessionState = session_manager::SessionState;
 
@@ -30,12 +30,12 @@
     user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
         std::move(user_manager));
 
-    affiliated_session_service_ =
-        std::make_unique<AffiliatedSessionService>(&test_clock_);
+    managed_session_service_ =
+        std::make_unique<ManagedSessionService>(&test_clock_);
   }
 
   void TearDown() override {
-    affiliated_session_service_.reset();
+    managed_session_service_.reset();
     chromeos::PowerManagerClient::Shutdown();
   }
 
@@ -54,8 +54,8 @@
     return profile;
   }
 
-  AffiliatedSessionService* affiliated_session_service() {
-    return affiliated_session_service_.get();
+  ManagedSessionService* managed_session_service() {
+    return managed_session_service_.get();
   }
 
   session_manager::SessionManager* session_manager() {
@@ -68,14 +68,19 @@
 
   base::SimpleTestClock* test_clock() { return &test_clock_; }
 
-  void OnAffiliatedLogin(Profile* profile) override { logged_in_ = profile; }
-  void OnAffiliatedLogout(Profile* profile) override { logged_out_ = profile; }
+  void OnLoginFailure(const chromeos::AuthFailure& error) override {
+    auth_failure_ = error;
+  }
+  void OnLogin(Profile* profile) override { logged_in_ = profile; }
+  void OnLogout(Profile* profile) override { logged_out_ = profile; }
   void OnLocked() override { locked_ = true; }
   void OnUnlocked() override { unlocked_ = true; }
   void OnResumeActive(base::Time time) override {
     suspend_time_ = std::make_unique<base::Time>(time);
   }
 
+  chromeos::AuthFailure auth_failure_ =
+      chromeos::AuthFailure::AuthFailureNone();
   Profile* logged_in_ = nullptr;
   Profile* logged_out_ = nullptr;
   bool locked_ = false;
@@ -92,11 +97,11 @@
 
   base::SimpleTestClock test_clock_;
 
-  std::unique_ptr<AffiliatedSessionService> affiliated_session_service_;
+  std::unique_ptr<ManagedSessionService> managed_session_service_;
 };
 
-TEST_F(AffiliatedSessionServiceTest, OnSessionStateChanged) {
-  affiliated_session_service()->AddObserver(this);
+TEST_F(ManagedSessionServiceTest, OnSessionStateChanged) {
+  managed_session_service()->AddObserver(this);
 
   session_manager()->SetSessionState(SessionState::LOCKED);
   session_manager()->SetSessionState(SessionState::ACTIVE);
@@ -129,49 +134,49 @@
   EXPECT_TRUE(unlocked_);
 }
 
-TEST_F(AffiliatedSessionServiceTest, OnUserProfileLoadedAffiliatedAndPrimary) {
+TEST_F(ManagedSessionServiceTest, OnUserProfileLoadedAffiliatedAndPrimary) {
   AccountId affiliated_account_id =
       AccountId::FromUserEmail("user0@managed.com");
   std::unique_ptr<TestingProfile> affiliated_profile = CreateProfile(
       affiliated_account_id, true /* affiliated */, true /* login */);
-  affiliated_session_service()->AddObserver(this);
+  managed_session_service()->AddObserver(this);
 
   session_manager()->NotifyUserProfileLoaded(affiliated_account_id);
 
   EXPECT_TRUE(affiliated_profile->IsSameOrParent(logged_in_));
 }
 
-TEST_F(AffiliatedSessionServiceTest, OnUserProfileLoadedAffiliated) {
+TEST_F(ManagedSessionServiceTest, OnUserProfileLoadedAffiliated) {
   AccountId secondary_account_id =
       AccountId::FromUserEmail("user3@managed.com");
   std::unique_ptr<TestingProfile> secondary_profile = CreateProfile(
       secondary_account_id, true /* affiliated */, false /* login */);
-  affiliated_session_service()->AddObserver(this);
+  managed_session_service()->AddObserver(this);
 
   session_manager()->NotifyUserProfileLoaded(secondary_account_id);
 
-  EXPECT_EQ(logged_in_, nullptr);
+  EXPECT_TRUE(secondary_profile->IsSameOrParent(logged_in_));
 }
 
-TEST_F(AffiliatedSessionServiceTest, OnUserProfileLoadedPrimary) {
+TEST_F(ManagedSessionServiceTest, OnUserProfileLoadedPrimary) {
   AccountId unaffiliated_account_id =
       AccountId::FromUserEmail("user2@managed.com");
   std::unique_ptr<TestingProfile> unaffiliated_profile = CreateProfile(
       unaffiliated_account_id, false /* affiliated */, true /* login */);
-  affiliated_session_service()->AddObserver(this);
+  managed_session_service()->AddObserver(this);
 
   session_manager()->NotifyUserProfileLoaded(unaffiliated_account_id);
 
-  EXPECT_EQ(logged_in_, nullptr);
+  EXPECT_TRUE(unaffiliated_profile->IsSameOrParent(logged_in_));
 }
 
-TEST_F(AffiliatedSessionServiceTest,
+TEST_F(ManagedSessionServiceTest,
        OnProfileWillBeDestroyedAffiliatedAndPrimary) {
   AccountId affiliated_account_id =
       AccountId::FromUserEmail("user0@managed.com");
   std::unique_ptr<TestingProfile> affiliated_profile = CreateProfile(
       affiliated_account_id, true /* affiliated */, true /* login */);
-  affiliated_session_service()->AddObserver(this);
+  managed_session_service()->AddObserver(this);
 
   session_manager()->NotifyUserProfileLoaded(affiliated_account_id);
   affiliated_profile->MaybeSendDestroyedNotification();
@@ -179,34 +184,34 @@
   EXPECT_TRUE(affiliated_profile->IsSameOrParent(logged_out_));
 }
 
-TEST_F(AffiliatedSessionServiceTest, OnProfileWillBeDestroyedAffiliated) {
+TEST_F(ManagedSessionServiceTest, OnProfileWillBeDestroyedAffiliated) {
   AccountId secondary_account_id =
       AccountId::FromUserEmail("user3@managed.com");
   std::unique_ptr<TestingProfile> secondary_profile = CreateProfile(
       secondary_account_id, true /* affiliated */, false /* login */);
-  affiliated_session_service()->AddObserver(this);
+  managed_session_service()->AddObserver(this);
 
   session_manager()->NotifyUserProfileLoaded(secondary_account_id);
   secondary_profile->MaybeSendDestroyedNotification();
 
-  EXPECT_EQ(logged_out_, nullptr);
+  EXPECT_TRUE(secondary_profile->IsSameOrParent(logged_in_));
 }
 
-TEST_F(AffiliatedSessionServiceTest, OnProfileWillBeDestroyedPrimary) {
+TEST_F(ManagedSessionServiceTest, OnProfileWillBeDestroyedPrimary) {
   AccountId unaffiliated_account_id =
       AccountId::FromUserEmail("user2@managed.com");
   std::unique_ptr<TestingProfile> unaffiliated_profile = CreateProfile(
       unaffiliated_account_id, false /* affiliated */, true /* login */);
-  affiliated_session_service()->AddObserver(this);
+  managed_session_service()->AddObserver(this);
 
   session_manager()->NotifyUserProfileLoaded(unaffiliated_account_id);
   unaffiliated_profile->MaybeSendDestroyedNotification();
 
-  EXPECT_EQ(logged_out_, nullptr);
+  EXPECT_TRUE(unaffiliated_profile->IsSameOrParent(logged_in_));
 }
 
-TEST_F(AffiliatedSessionServiceTest, SuspendDone) {
-  affiliated_session_service()->AddObserver(this);
+TEST_F(ManagedSessionServiceTest, SuspendDone) {
+  managed_session_service()->AddObserver(this);
   test_clock()->SetNow(base::Time::Now());
   base::TimeDelta sleep_duration = base::TimeDelta::FromHours(2);
 
@@ -215,13 +220,13 @@
   EXPECT_EQ(*suspend_time_, test_clock()->Now() - sleep_duration);
 }
 
-TEST_F(AffiliatedSessionServiceTest, RemoveObserver) {
+TEST_F(ManagedSessionServiceTest, RemoveObserver) {
   AccountId account_id = AccountId::FromUserEmail("user0@managed.com");
   std::unique_ptr<TestingProfile> profile =
       CreateProfile(account_id, true /* affiliated */, true /* login */);
-  affiliated_session_service()->AddObserver(this);
+  managed_session_service()->AddObserver(this);
 
-  affiliated_session_service()->RemoveObserver(this);
+  managed_session_service()->RemoveObserver(this);
 
   session_manager()->SetSessionState(SessionState::LOCKED);
   session_manager()->SetSessionState(SessionState::ACTIVE);
@@ -236,4 +241,14 @@
   EXPECT_FALSE(profile->IsSameOrParent(logged_out_));
 }
 
+TEST_F(ManagedSessionServiceTest, LoginFailure) {
+  managed_session_service()->AddObserver(this);
+
+  managed_session_service()->OnAuthFailure(chromeos::AuthFailure(
+      chromeos::AuthFailure::FailureReason::OWNER_REQUIRED));
+
+  EXPECT_EQ(auth_failure_.reason(),
+            chromeos::AuthFailure::FailureReason::OWNER_REQUIRED);
+}
+
 }  // namespace policy
diff --git a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManager.java b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManager.java
index 83d8389..d9e5bfe 100644
--- a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManager.java
+++ b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManager.java
@@ -147,8 +147,8 @@
     private boolean isStaleTab(Tab tab) {
         long tabLastOpenTime = System.currentTimeMillis()
                 - CriticalPersistedTabData.from(tab).getTimestampMillis();
-        return tabLastOpenTime <= TimeUnit.SECONDS.toMillis(
-                       ShoppingPersistedTabData.STALE_TAB_THRESHOLD_SECONDS.getValue())
+        return tabLastOpenTime
+                <= TimeUnit.SECONDS.toMillis(ShoppingPersistedTabData.getStaleTabThresholdSeconds())
                 && tabLastOpenTime
                 >= TimeUnit.SECONDS.toMillis(CommerceSubscriptionsServiceConfig
                                                      .STALE_TAB_LOWER_BOUND_SECONDS.getValue());
diff --git a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManagerUnitTest.java b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManagerUnitTest.java
index 26dcc59..aedc7ef 100644
--- a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManagerUnitTest.java
+++ b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManagerUnitTest.java
@@ -116,8 +116,7 @@
         doReturn(OFFER1_ID).when(mShoppingPersistedTabData1).getMainOfferId();
         doReturn(OFFER2_ID).when(mShoppingPersistedTabData2).getMainOfferId();
         long fakeTimestamp = System.currentTimeMillis()
-                - TimeUnit.SECONDS.toMillis(
-                        ShoppingPersistedTabData.STALE_TAB_THRESHOLD_SECONDS.getValue())
+                - TimeUnit.SECONDS.toMillis(ShoppingPersistedTabData.getStaleTabThresholdSeconds())
                 + TimeUnit.DAYS.toMillis(7);
         doReturn(fakeTimestamp).when(mCriticalPersistedTabData1).getTimestampMillis();
         doReturn(fakeTimestamp).when(mCriticalPersistedTabData2).getTimestampMillis();
@@ -214,8 +213,7 @@
     @Test
     public void testInitialSubscription_TabTooOld() {
         doReturn(System.currentTimeMillis()
-                - TimeUnit.SECONDS.toMillis(
-                        ShoppingPersistedTabData.STALE_TAB_THRESHOLD_SECONDS.getValue())
+                - TimeUnit.SECONDS.toMillis(ShoppingPersistedTabData.getStaleTabThresholdSeconds())
                 - TimeUnit.DAYS.toMillis(7))
                 .when(mCriticalPersistedTabData1)
                 .getTimestampMillis();
diff --git a/chrome/browser/devtools/protocol/window_manager_handler.cc b/chrome/browser/devtools/protocol/window_manager_handler.cc
index 2c1df6b..a5da6ea 100644
--- a/chrome/browser/devtools/protocol/window_manager_handler.cc
+++ b/chrome/browser/devtools/protocol/window_manager_handler.cc
@@ -17,14 +17,16 @@
 
 protocol::Response WindowManagerHandler::EnterOverviewMode() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  bool success = ash::Shell::Get()->overview_controller()->StartOverview();
+  bool success = ash::Shell::Get()->overview_controller()->StartOverview(
+      ash::OverviewStartAction::kDevTools);
   return success ? protocol::Response::Success()
                  : protocol::Response::ServerError("Overview failed");
 }
 
 protocol::Response WindowManagerHandler::ExitOverviewMode() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  bool success = ash::Shell::Get()->overview_controller()->EndOverview();
+  bool success = ash::Shell::Get()->overview_controller()->EndOverview(
+      ash::OverviewEndAction::kDevTools);
   return success ? protocol::Response::Success()
                  : protocol::Response::ServerError("Overview failed");
 }
diff --git a/chrome/browser/download/simple_download_manager_coordinator_factory.h b/chrome/browser/download/simple_download_manager_coordinator_factory.h
index bead09e..6bb06a3 100644
--- a/chrome/browser/download/simple_download_manager_coordinator_factory.h
+++ b/chrome/browser/download/simple_download_manager_coordinator_factory.h
@@ -8,16 +8,12 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/core/simple_keyed_service_factory.h"
 
 class KeyedService;
 class SimpleFactoryKey;
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 namespace download {
 class SimpleDownloadManagerCoordinator;
 }  // namespace download
diff --git a/chrome/browser/extensions/activity_log/activity_actions.cc b/chrome/browser/extensions/activity_log/activity_actions.cc
index df83aa93..399915d0 100644
--- a/chrome/browser/extensions/activity_log/activity_actions.cc
+++ b/chrome/browser/extensions/activity_log/activity_actions.cc
@@ -68,7 +68,7 @@
   auto clone = base::MakeRefCounted<Action>(
       extension_id(), time(), action_type(), api_name(), action_id());
   if (args())
-    clone->set_args(base::WrapUnique(args()->DeepCopy()));
+    clone->set_args(args()->CreateDeepCopy());
   clone->set_page_url(page_url());
   clone->set_page_title(page_title());
   clone->set_page_incognito(page_incognito());
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index b8c17c17..cdd5609 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -2421,7 +2421,7 @@
                          "  \"incognito\": false,"
                          "  \"bytesReceived\": 0.0,"
                          "  \"fileSize\": 0.0,"
-                         "  \"mime\": \"\","
+                         "  \"mime\": \"text/plain\","
                          "  \"paused\": false,"
                          "  \"url\": \"%s\"}]",
                          download_url.c_str())));
@@ -2560,7 +2560,7 @@
       item, download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
       base::StringPrintf("[{\"danger\": \"safe\","
                          "  \"incognito\": false,"
-                         "  \"mime\": \"\","
+                         "  \"mime\": \"text/plain\","
                          "  \"paused\": false,"
                          "  \"id\": %d,"
                          "  \"url\": \"%s\"}]",
@@ -2601,7 +2601,7 @@
       item, download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
       base::StringPrintf("[{\"danger\": \"safe\","
                          "  \"incognito\": false,"
-                         "  \"mime\": \"\","
+                         "  \"mime\": \"text/plain\","
                          "  \"paused\": false,"
                          "  \"id\": %d,"
                          "  \"url\": \"%s\"}]",
diff --git a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
index 5179a26..93cd9dfd 100644
--- a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
+++ b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
@@ -304,10 +304,10 @@
 #if defined(OS_MAC)
   // We need this on mac so we don't loose 2x representations from browser icon
   // in transformations gfx::ImageSkia -> NSImage -> gfx::ImageSkia.
-  std::vector<ui::ScaleFactor> supported_scale_factors;
+  std::vector<ui::ResourceScaleFactor> supported_scale_factors;
   supported_scale_factors.push_back(ui::SCALE_FACTOR_100P);
   supported_scale_factors.push_back(ui::SCALE_FACTOR_200P);
-  ui::SetSupportedScaleFactors(supported_scale_factors);
+  ui::SetSupportedResourceScaleFactors(supported_scale_factors);
 #endif
 
   // We should not be creating icons asynchronously, so we don't need an
diff --git a/chrome/browser/extensions/api/notifications/notifications_api.cc b/chrome/browser/extensions/api/notifications/notifications_api.cc
index 3e5eece..7822dc72 100644
--- a/chrome/browser/extensions/api/notifications/notifications_api.cc
+++ b/chrome/browser/extensions/api/notifications/notifications_api.cc
@@ -220,8 +220,8 @@
 
   NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes();
 
-  float image_scale =
-      ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back());
+  float image_scale = ui::GetScaleForResourceScaleFactor(
+      ui::GetSupportedResourceScaleFactors().back());
 
   // Extract required fields: type, title, message, and icon.
   message_center::NotificationType type =
@@ -373,8 +373,8 @@
 #endif
 
   NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes();
-  float image_scale =
-      ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back());
+  float image_scale = ui::GetScaleForResourceScaleFactor(
+      ui::GetSupportedResourceScaleFactors().back());
 
   // Update optional fields if provided.
   if (options->type != api::notifications::TEMPLATE_TYPE_NONE)
diff --git a/chrome/browser/extensions/api/tabs/tabs_event_router.cc b/chrome/browser/extensions/api/tabs/tabs_event_router.cc
index 3f121134..fd2f9dd 100644
--- a/chrome/browser/extensions/api/tabs/tabs_event_router.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_event_router.cc
@@ -472,8 +472,7 @@
   Profile* profile = tab_strip_model->profile();
   DispatchEvent(profile, events::TABS_ON_HIGHLIGHT_CHANGED,
                 api::tabs::OnHighlightChanged::kEventName,
-                std::unique_ptr<base::ListValue>(args->DeepCopy()),
-                EventRouter::USER_GESTURE_UNKNOWN);
+                args->CreateDeepCopy(), EventRouter::USER_GESTURE_UNKNOWN);
   DispatchEvent(profile, events::TABS_ON_HIGHLIGHTED,
                 api::tabs::OnHighlighted::kEventName, std::move(args),
                 EventRouter::USER_GESTURE_UNKNOWN);
diff --git a/chrome/browser/extensions/chrome_content_verifier_delegate.cc b/chrome/browser/extensions/chrome_content_verifier_delegate.cc
index e7eb2db..e0a7b87 100644
--- a/chrome/browser/extensions/chrome_content_verifier_delegate.cc
+++ b/chrome/browser/extensions/chrome_content_verifier_delegate.cc
@@ -51,10 +51,9 @@
 
 absl::optional<ChromeContentVerifierDelegate::VerifyInfo::Mode>&
 GetModeForTesting() {
-  static base::NoDestructor<
-      absl::optional<ChromeContentVerifierDelegate::VerifyInfo::Mode>>
+  static absl::optional<ChromeContentVerifierDelegate::VerifyInfo::Mode>
       testing_mode;
-  return *testing_mode;
+  return testing_mode;
 }
 
 const char kContentVerificationExperimentName[] =
diff --git a/chrome/browser/extensions/extension_icon_manager_unittest.cc b/chrome/browser/extensions/extension_icon_manager_unittest.cc
index 77baff7..e3035d0e 100644
--- a/chrome/browser/extensions/extension_icon_manager_unittest.cc
+++ b/chrome/browser/extensions/extension_icon_manager_unittest.cc
@@ -217,7 +217,7 @@
   ASSERT_TRUE(extension);
 
   constexpr int kMaxIconSizeInManifest = 32;
-  std::vector<std::vector<ui::ScaleFactor>> supported_scales = {
+  std::vector<std::vector<ui::ResourceScaleFactor>> supported_scales = {
       // Base case.
       {ui::SCALE_FACTOR_100P},
       // Two scale factors.
@@ -235,8 +235,9 @@
     // the logic in this test work, we need to set the scale factor to one of
     // the "supported" scales.
     ScopedSetDeviceScaleFactor scoped_dsf(
-        ui::GetScaleForScaleFactor(supported_scales[i][0]));
-    ui::test::ScopedSetSupportedScaleFactors scoped(supported_scales[i]);
+        ui::GetScaleForResourceScaleFactor(supported_scales[i][0]));
+    ui::test::ScopedSetSupportedResourceScaleFactors scoped(
+        supported_scales[i]);
     ExtensionIconManager icon_manager;
     icon_manager.set_observer(this);
 
@@ -248,7 +249,8 @@
     // icon.
     bool should_fall_back_to_default = true;
     for (auto supported_scale : supported_scales[i]) {
-      if (gfx::kFaviconSize * ui::GetScaleForScaleFactor(supported_scale) <=
+      if (gfx::kFaviconSize *
+              ui::GetScaleForResourceScaleFactor(supported_scale) <=
           kMaxIconSizeInManifest) {
         should_fall_back_to_default = false;
         break;
@@ -263,8 +265,9 @@
 
     for (int scale_factor_iter = ui::SCALE_FACTOR_NONE + 1;
          scale_factor_iter < ui::NUM_SCALE_FACTORS; ++scale_factor_iter) {
-      auto scale_factor = static_cast<ui::ScaleFactor>(scale_factor_iter);
-      float scale = ui::GetScaleForScaleFactor(scale_factor);
+      auto scale_factor =
+          static_cast<ui::ResourceScaleFactor>(scale_factor_iter);
+      float scale = ui::GetScaleForResourceScaleFactor(scale_factor);
       SCOPED_TRACE(testing::Message() << "Scale: " << scale);
 
       const bool has_representation = image_skia.HasRepresentation(scale);
diff --git a/chrome/browser/extensions/extension_web_ui.cc b/chrome/browser/extensions/extension_web_ui.cc
index f23292d2..fa5dc9b 100644
--- a/chrome/browser/extensions/extension_web_ui.cc
+++ b/chrome/browser/extensions/extension_web_ui.cc
@@ -592,7 +592,8 @@
                                                pixel_size,
                                                ExtensionIconSet::MATCH_BIGGER);
 
-    ui::ScaleFactor resource_scale_factor = ui::GetSupportedScaleFactor(scale);
+    ui::ResourceScaleFactor resource_scale_factor =
+        ui::GetSupportedResourceScaleFactor(scale);
     if (!icon_resource.empty()) {
       info_list.push_back(extensions::ImageLoader::ImageRepresentation(
           icon_resource,
@@ -609,10 +610,11 @@
     gfx::ImageSkia placeholder_skia(placeholder_image.AsImageSkia());
     // Ensure the ImageSkia has representation at all scales we would use for
     // favicons.
-    std::vector<ui::ScaleFactor> scale_factors = ui::GetSupportedScaleFactors();
+    std::vector<ui::ResourceScaleFactor> scale_factors =
+        ui::GetSupportedResourceScaleFactors();
     for (const auto& scale_factor : scale_factors) {
       placeholder_skia.GetRepresentation(
-          ui::GetScaleForScaleFactor(scale_factor));
+          ui::GetScaleForResourceScaleFactor(scale_factor));
     }
     RunFaviconCallbackAsync(std::move(callback), gfx::Image(placeholder_skia));
   } else {
diff --git a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source_unittest.cc b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source_unittest.cc
index 225bf2e..196e521c 100644
--- a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source_unittest.cc
+++ b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/run_loop.h"
 #include "base/test/bind.h"
+#include "build/branding_buildflags.h"
 #include "build/build_config.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
@@ -16,6 +17,10 @@
 #include "base/mac/mac_util.h"
 #endif
 
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && !defined(OS_CHROMEOS)
+#include "chrome/test/base/scoped_channel_override.h"
+#endif
+
 namespace system_logs {
 namespace {
 
@@ -42,6 +47,20 @@
       response->at("CHROME VERSION"));
 }
 
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && !defined(OS_CHROMEOS)
+TEST_F(ChromeInternalLogSourceTest, VersionTagContainsExtendedLabel) {
+  chrome::ScopedChannelOverride channel_override(
+      chrome::ScopedChannelOverride::Channel::kExtendedStable);
+
+  ASSERT_TRUE(chrome::IsExtendedStableChannel());
+  auto response = GetChromeInternalLogs();
+  EXPECT_PRED_FORMAT2(
+      testing::IsSubstring,
+      chrome::GetVersionString(chrome::WithExtendedStable(true)),
+      response->at("CHROME VERSION"));
+}
+#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING) && !defined(OS_CHROMEOS)
+
 #if defined(OS_MAC)
 TEST_F(ChromeInternalLogSourceTest, CpuTypePresentAndValid) {
   auto response = GetChromeInternalLogs();
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index b508ca8..9ed4ef9 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1976,6 +1976,11 @@
     "expiry_milestone": 80
   },
   {
+    "name": "enable-holding-space-in-progress-downloads-integration",
+    "owners": [ "//ash/public/cpp/holding_space/OWNERS" ],
+    "expiry_milestone": 95
+  },
+  {
     "name": "enable-hostname-setting",
     "owners": [ "jhawkins" ],
     "expiry_milestone": 92
@@ -2202,9 +2207,14 @@
     "expiry_milestone": 91
   },
   {
-  "name": "enable-ntp-memory-enhancement",
-  "owners": [ "adamta", "sczs" ],
-  "expiry_milestone": 96
+    "name": "enable-ntp-memory-enhancement",
+    "owners": [ "adamta", "sczs" ],
+    "expiry_milestone": 96
+  },
+  {
+    "name": "enable-oauth-ipp",
+    "owners": [ "batrapranav", "pawliczek" ],
+    "expiry_milestone": 99
   },
   {
     "name": "enable-offline-previews",
@@ -5127,6 +5137,11 @@
     "expiry_milestone": 90
   },
   {
+    "name": "tab-groups-save",
+    "owners": [ "chrome-desktop-ui-sea@google.com", "cyan" ],
+    "expiry_milestone": 95
+  },
+  {
     "name": "tab-hover-card-images",
     "owners": [ "dfried", "corising", "//chrome/browser/ui/views/tabs/OWNERS" ],
     "expiry_milestone": 96
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 834cbaaf..743a8f60 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2515,6 +2515,10 @@
     "Causes a 'New' badge to appear on the entry point for creating a tab "
     "group in the tab context menu.";
 
+const char kTabGroupsSaveName[] = "Tab Groups Save";
+const char kTabGroupsSaveDescription[] =
+    "Enables users to explicitly save and recall tab groups.";
+
 const char kTabHoverCardImagesName[] = "Tab Hover Card Images";
 const char kTabHoverCardImagesDescription[] =
     "Shows a preview image in tab hover cards, if tab hover cards are enabled.";
@@ -4522,6 +4526,11 @@
 const char kEnableNetworkingInDiagnosticsAppDescription[] =
     "Enable networking cards in the Diagnostics App";
 
+const char kEnableOAuthIppName[] =
+    "Enable OAuth when printing via the IPP protocol";
+const char kEnableOAuthIppDescription[] =
+    "Enable OAuth when printing via the IPP protocol";
+
 const char kEnableSuggestedFilesName[] = "Enable Suggested Files";
 const char kEnableSuggestedFilesDescription[] =
     "Enable Suggested Files feature in Launcher, which will show file "
@@ -4682,6 +4691,12 @@
     "Hides media notifications for ARC apps. Requires "
     "#enable-media-session-notifications to be enabled.";
 
+const char kHoldingSpaceInProgressDownloadsIntegrationName[] =
+    "Enable showing in-progress downloads in Tote.";
+const char kHoldingSpaceInProgressDownloadsIntegrationDescription[] =
+    "Show in-progress download functionality in Tote to increase productivity "
+    "by giving users one place to go to monitor and access their downloads.";
+
 const char kImeAssistAutocorrectName[] = "Enable assistive autocorrect";
 const char kImeAssistAutocorrectDescription[] =
     "Enable assistive auto-correct features for native IME";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index a92d8bc..f82b18e 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1444,6 +1444,9 @@
 extern const char kTabGroupsNewBadgePromoName[];
 extern const char kTabGroupsNewBadgePromoDescription[];
 
+extern const char kTabGroupsSaveName[];
+extern const char kTabGroupsSaveDescription[];
+
 extern const char kTabHoverCardImagesName[];
 extern const char kTabHoverCardImagesDescription[];
 
@@ -2598,6 +2601,9 @@
 extern const char kEnableNetworkingInDiagnosticsAppName[];
 extern const char kEnableNetworkingInDiagnosticsAppDescription[];
 
+extern const char kEnableOAuthIppName[];
+extern const char kEnableOAuthIppDescription[];
+
 extern const char kEnableSuggestedFilesName[];
 extern const char kEnableSuggestedFilesDescription[];
 
@@ -2692,6 +2698,9 @@
 extern const char kHideArcMediaNotificationsName[];
 extern const char kHideArcMediaNotificationsDescription[];
 
+extern const char kHoldingSpaceInProgressDownloadsIntegrationName[];
+extern const char kHoldingSpaceInProgressDownloadsIntegrationDescription[];
+
 extern const char kImeAssistAutocorrectName[];
 extern const char kImeAssistAutocorrectDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index e32c4f3..dbc4fb3a 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -268,7 +268,6 @@
     &kVoiceButtonInTopToolbar,
     &kVrBrowsingFeedback,
     &kWebOtpCrossDeviceSimpleString,
-    &kPrefetchNotificationSchedulingIntegration,
     &content_creation::kWebNotesStylizeEnabled,
     &features::kDnsOverHttps,
     &notifications::features::kUseChimeAndroidSdk,
@@ -739,10 +738,6 @@
     "UpdateNotificationSchedulingIntegration",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kPrefetchNotificationSchedulingIntegration{
-    "PrefetchNotificationSchedulingIntegration",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kUpdateNotificationScheduleServiceImmediateShowOption{
     "UpdateNotificationScheduleServiceImmediateShowOption",
     base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index df73f6b..47d31558 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -154,7 +154,6 @@
 extern const base::Feature kVoiceButtonInTopToolbar;
 extern const base::Feature kVrBrowsingFeedback;
 extern const base::Feature kWebOtpCrossDeviceSimpleString;
-extern const base::Feature kPrefetchNotificationSchedulingIntegration;
 
 }  // namespace android
 }  // namespace chrome
diff --git a/chrome/browser/media/webrtc/desktop_media_picker_manager.h b/chrome/browser/media/webrtc/desktop_media_picker_manager.h
index 3919941..b51b51f 100644
--- a/chrome/browser/media/webrtc/desktop_media_picker_manager.h
+++ b/chrome/browser/media/webrtc/desktop_media_picker_manager.h
@@ -6,13 +6,9 @@
 #define CHROME_BROWSER_MEDIA_WEBRTC_DESKTOP_MEDIA_PICKER_MANAGER_H_
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/observer_list.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}
-
 // A singleton that acts as a rendezvous for dialog observers to register and
 // the dialog managers/delegates to post their activities.
 // TODO(crbug/953495): Merge this into DesktopMediaPickerFactoryImpl.
diff --git a/chrome/browser/metrics/perf/windowed_incognito_observer.h b/chrome/browser/metrics/perf/windowed_incognito_observer.h
index 4dbcd10f..cea83ddc 100644
--- a/chrome/browser/metrics/perf/windowed_incognito_observer.h
+++ b/chrome/browser/metrics/perf/windowed_incognito_observer.h
@@ -6,17 +6,13 @@
 #define CHROME_BROWSER_METRICS_PERF_WINDOWED_INCOGNITO_OBSERVER_H_
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/sequence_checker.h"
 #include "base/synchronization/lock.h"
 #include "chrome/browser/ui/browser_list_observer.h"
 
 class Browser;
 
-namespace base {
-template <class T>
-class NoDestructor;
-}  // namespace base
-
 namespace metrics {
 
 class WindowedIncognitoMonitor;
diff --git a/chrome/browser/nearby_sharing/certificates/test_util.cc b/chrome/browser/nearby_sharing/certificates/test_util.cc
index def351d..c99e370 100644
--- a/chrome/browser/nearby_sharing/certificates/test_util.cc
+++ b/chrome/browser/nearby_sharing/certificates/test_util.cc
@@ -181,15 +181,15 @@
 }
 
 base::Time GetNearbyShareTestNotBefore() {
-  static const base::NoDestructor<base::Time> not_before(
-      base::Time::FromJavaTime(kTestNotBeforeMillis));
-  return *not_before;
+  static const base::Time not_before =
+      base::Time::FromJavaTime(kTestNotBeforeMillis);
+  return not_before;
 }
 
 base::TimeDelta GetNearbyShareTestValidityOffset() {
-  static const base::NoDestructor<base::TimeDelta> offset(
-      base::TimeDelta::FromMilliseconds(kTestValidityOffsetMillis));
-  return *offset;
+  static const base::TimeDelta offset =
+      base::TimeDelta::FromMilliseconds(kTestValidityOffsetMillis);
+  return offset;
 }
 
 const nearbyshare::proto::EncryptedMetadata& GetNearbyShareTestMetadata() {
diff --git a/chrome/browser/nearby_sharing/client/nearby_share_client_impl.cc b/chrome/browser/nearby_sharing/client/nearby_share_client_impl.cc
index 26b8e09..92c27c9 100644
--- a/chrome/browser/nearby_sharing/client/nearby_share_client_impl.cc
+++ b/chrome/browser/nearby_sharing/client/nearby_share_client_impl.cc
@@ -10,7 +10,6 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
-#include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl.h"
 #include "chrome/browser/nearby_sharing/client/nearby_share_http_notifier.h"
@@ -95,10 +94,10 @@
 // TODO(crbug.com/1103471): Update "chrome_policy" when a Nearby Share
 // enterprise policy is created.
 const net::PartialNetworkTrafficAnnotationTag& GetUpdateDeviceAnnotation() {
-  static const base::NoDestructor<net::PartialNetworkTrafficAnnotationTag>
-      annotation(net::DefinePartialNetworkTrafficAnnotation(
-          "nearby_share_update_device", "oauth2_api_call_flow",
-          R"(
+  static const net::PartialNetworkTrafficAnnotationTag annotation =
+      net::DefinePartialNetworkTrafficAnnotation("nearby_share_update_device",
+                                                 "oauth2_api_call_flow",
+                                                 R"(
       semantics {
         sender: "Nearby Share"
         description:
@@ -137,17 +136,17 @@
             SigninAllowed: false
           }
         }
-      })"));
-  return *annotation;
+      })");
+  return annotation;
 }
 
 // TODO(crbug.com/1103471): Update "chrome_policy" when a Nearby Share
 // enterprise policy is created.
 const net::PartialNetworkTrafficAnnotationTag& GetContactsAnnotation() {
-  static const base::NoDestructor<net::PartialNetworkTrafficAnnotationTag>
-      annotation(net::DefinePartialNetworkTrafficAnnotation(
-          "nearby_share_contacts", "oauth2_api_call_flow",
-          R"(
+  static const net::PartialNetworkTrafficAnnotationTag annotation =
+      net::DefinePartialNetworkTrafficAnnotation("nearby_share_contacts",
+                                                 "oauth2_api_call_flow",
+                                                 R"(
       semantics {
         sender: "Nearby Share"
         description:
@@ -173,16 +172,16 @@
             SigninAllowed: false
           }
         }
-          })"));
-  return *annotation;
+          })");
+  return annotation;
 }
 
 // TODO(crbug.com/1103471): Update "chrome_policy" when a Nearby Share
 // enterprise policy is created.
 const net::PartialNetworkTrafficAnnotationTag&
 GetListPublicCertificatesAnnotation() {
-  static const base::NoDestructor<net::PartialNetworkTrafficAnnotationTag>
-      annotation(net::DefinePartialNetworkTrafficAnnotation(
+  static const net::PartialNetworkTrafficAnnotationTag annotation =
+      net::DefinePartialNetworkTrafficAnnotation(
           "nearby_share_list_public_certificates", "oauth2_api_call_flow",
           R"(
       semantics {
@@ -213,8 +212,8 @@
             SigninAllowed: false
           }
         }
-          })"));
-  return *annotation;
+          })");
+  return annotation;
 }
 
 }  // namespace
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_factory.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_factory.cc
index f921c05..3d3018d 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_factory.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_factory.cc
@@ -9,7 +9,6 @@
 
 #include "base/feature_list.h"
 #include "base/memory/singleton.h"
-#include "base/no_destructor.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/chromeos/nearby/nearby_process_manager_factory.h"
@@ -34,8 +33,8 @@
 constexpr char kServiceName[] = "NearbySharingService";
 
 absl::optional<bool>& IsSupportedTesting() {
-  static base::NoDestructor<absl::optional<bool>> is_supported;
-  return *is_supported;
+  static absl::optional<bool> is_supported;
+  return is_supported;
 }
 
 }  // namespace
diff --git a/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc b/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc
index 5198b699..cea656a 100644
--- a/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc
+++ b/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/ntp_tiles/chrome_custom_links_manager_factory.h"
 #include "chrome/browser/ntp_tiles/chrome_popular_sites_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search/suggestions/suggestions_service_factory.h"
 #include "chrome/common/buildflags.h"
 #include "components/history/core/browser/top_sites.h"
 #include "components/image_fetcher/core/image_fetcher_impl.h"
@@ -38,8 +37,6 @@
 #include "chrome/browser/supervised_user/supervised_user_url_filter.h"
 #endif
 
-using suggestions::SuggestionsServiceFactory;
-
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 namespace {
 
@@ -119,7 +116,6 @@
 
   auto most_visited_sites = std::make_unique<ntp_tiles::MostVisitedSites>(
       profile->GetPrefs(), TopSitesFactory::GetForProfile(profile),
-      SuggestionsServiceFactory::GetForProfile(profile),
 #if defined(OS_ANDROID)
       ChromePopularSitesFactory::NewForProfile(profile),
 #else
diff --git a/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestIntegrationTest.java b/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestIntegrationTest.java
index 29542630..574defe 100644
--- a/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestIntegrationTest.java
+++ b/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestIntegrationTest.java
@@ -91,6 +91,10 @@
 
         ShadowPaymentFeatureList.setFeatureEnabled(
                 PaymentFeatureList.WEB_PAYMENTS_SINGLE_APP_UI_SKIP, true);
+        ShadowPaymentFeatureList.setFeatureEnabled(
+                PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION, true);
+        ShadowPaymentFeatureList.setFeatureEnabled(
+                PaymentFeatureList.WEB_PAYMENTS_EXPERIMENTAL_FEATURES, true);
         PaymentRequestService.resetShowingPaymentRequestForTest();
         PaymentAppService.getInstance().resetForTest();
 
diff --git a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_prober.cc b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_prober.cc
index 5c8cb6b..8c4c5b9 100644
--- a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_prober.cc
+++ b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_prober.cc
@@ -6,7 +6,6 @@
 
 #include "base/bind.h"
 #include "base/feature_list.h"
-#include "base/no_destructor.h"
 #include "base/strings/string_util.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/availability/availability_prober.h"
@@ -224,13 +223,13 @@
 };
 
 CanaryCheckDelegate* GetCanaryCheckDelegate() {
-  static base::NoDestructor<CanaryCheckDelegate> delegate;
-  return delegate.get();
+  static CanaryCheckDelegate delegate;
+  return &delegate;
 }
 
 OriginProbeDelegate* GetOriginProbeDelegate() {
-  static base::NoDestructor<OriginProbeDelegate> delegate;
-  return delegate.get();
+  static OriginProbeDelegate delegate;
+  return &delegate;
 }
 
 // Allows probing to start after a delay so that browser start isn't slowed.
diff --git a/chrome/browser/printing/print_job.cc b/chrome/browser/printing/print_job.cc
index cbe686c..7f35eb1 100644
--- a/chrome/browser/printing/print_job.cc
+++ b/chrome/browser/printing/print_job.cc
@@ -43,7 +43,7 @@
 
 namespace {
 
-// Helper function to ensure |job| is valid until at least |callback| returns.
+// Helper function to ensure `job` is valid until at least `callback` returns.
 void HoldRefCallback(scoped_refptr<PrintJob> job, base::OnceClosure callback) {
   std::move(callback).Run();
 }
@@ -138,18 +138,19 @@
     document()->DebugDumpData(print_data.get(), FILE_PATH_LITERAL(".pdf"));
 
   const PrintSettings& settings = document()->settings();
-  if (settings.printer_is_textonly()) {
+  if (settings.printer_language_is_textonly()) {
     StartPdfToTextConversion(print_data, page_size);
-  } else if (settings.printer_is_ps2() || settings.printer_is_ps3()) {
+  } else if (settings.printer_language_is_ps2() ||
+             settings.printer_language_is_ps3()) {
     StartPdfToPostScriptConversion(print_data, content_area, physical_offsets,
-                                   settings.printer_is_ps2());
+                                   settings.printer_language_is_ps2());
   } else {
     StartPdfToEmfConversion(print_data, page_size, content_area);
   }
 
   // Indicate that the PDF is fully rendered and we no longer need the renderer
   // and web contents, so the print job does not need to be cancelled if they
-  // die. This is needed on Windows because the PrintedDocument will not be
+  // die. This is needed on Windows because the `PrintedDocument` will not be
   // considered complete until PDF conversion finishes.
   document()->SetConvertingPdf();
 }
@@ -179,7 +180,7 @@
     return;
   }
 
-  // Real work is done in PrintJobWorker::StartPrinting().
+  // Real work is done in `PrintJobWorker::StartPrinting()`.
   worker_->PostTask(
       FROM_HERE, base::BindOnce(&HoldRefCallback, base::WrapRefCounted(this),
                                 base::BindOnce(&PrintJobWorker::StartPrinting,
@@ -231,7 +232,7 @@
     // InvokeLater since it would take too much time.
     worker_->Cancel();
   }
-  // Make sure a Cancel() is broadcast.
+  // Make sure a `Cancel()` is broadcast.
   auto details = base::MakeRefCounted<JobEventDetails>(JobEventDetails::FAILED,
                                                        0, nullptr);
   content::NotificationService::current()->Notify(
@@ -349,11 +350,11 @@
   // seems to work with the fix for this bug applied.
   const PrintSettings& settings = document()->settings();
   bool print_text_with_gdi =
-      settings.print_text_with_gdi() && !settings.printer_is_xps() &&
+      settings.print_text_with_gdi() && !settings.printer_language_is_xps() &&
       base::FeatureList::IsEnabled(::features::kGdiTextPrinting);
 
   // TODO(thestig): Figure out why crbug.com/1083911 occurred, which is likely
-  // because |web_contents| was null. As a result, this section has many more
+  // because `web_contents` was null. As a result, this section has many more
   // pointer checks to avoid crashing.
   content::WebContents* web_contents = worker_->GetWebContents();
   content::BrowserContext* context =
@@ -516,7 +517,7 @@
       break;
     }
     case JobEventDetails::DOC_DONE: {
-      // This will call Stop() and broadcast a JOB_DONE message.
+      // This will call `Stop()` and broadcast a `JOB_DONE` message.
       content::GetUIThreadTaskRunner({})->PostTask(
           FROM_HERE, base::BindOnce(&PrintJob::OnDocumentDone, this));
       break;
@@ -541,7 +542,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   // Be sure to live long enough. The instance could be destroyed by the
-  // JOB_DONE broadcast.
+  // `JOB_DONE` broadcast.
   scoped_refptr<PrintJob> handle(this);
 
   // Stop the worker thread.
diff --git a/chrome/browser/printing/print_view_manager_unittest.cc b/chrome/browser/printing/print_view_manager_unittest.cc
index 1e236cc..000c2ce 100644
--- a/chrome/browser/printing/print_view_manager_unittest.cc
+++ b/chrome/browser/printing/print_view_manager_unittest.cc
@@ -26,6 +26,10 @@
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 
+#if defined(OS_WIN)
+#include "printing/mojom/print.mojom.h"
+#endif
+
 namespace printing {
 
 using PrintViewManagerTest = BrowserWithTestWindowTest;
@@ -34,16 +38,18 @@
  public:
   explicit TestPrintViewManager(content::WebContents* web_contents)
       : PrintViewManagerBase(web_contents) {}
+  TestPrintViewManager(const TestPrintViewManager&) = delete;
+  TestPrintViewManager& operator=(const TestPrintViewManager&) = delete;
 
   ~TestPrintViewManager() override {
-    // Set this null here. Otherwise, the PrintViewManagerBase destructor will
-    // try to de-register for notifications that were not registered for in
-    // CreateNewPrintJob().
+    // Set this null here. Otherwise, the `PrintViewManagerBase` destructor
+    // will try to de-register for notifications that were not registered for
+    // in `CreateNewPrintJob()`.
     print_job_ = nullptr;
   }
 
-  // Mostly copied from PrintViewManager::PrintPreviewNow(). We can't override
-  // PrintViewManager since it is a user data class.
+  // Mostly copied from `PrintViewManager::PrintPreviewNow()`. We can't
+  // override `PrintViewManager` since it is a user data class.
   bool PrintPreviewNow(content::RenderFrameHost* rfh, bool has_selection) {
     // Don't print / print preview crashed tabs.
     if (IsCrashed())
@@ -66,7 +72,7 @@
   }
 
 #if defined(OS_WIN)
-  PrintSettings::PrinterType type() { return test_job()->type(); }
+  mojom::PrinterLanguageType type() { return test_job()->type(); }
 #endif
 
   // Ends the run loop.
@@ -84,7 +90,7 @@
   }
 
  protected:
-  // Override to create a TestPrintJob instead of a real one.
+  // Override to create a `TestPrintJob` instead of a real one.
   bool CreateNewPrintJob(std::unique_ptr<PrinterQuery> query) override {
     print_job_ = base::MakeRefCounted<TestPrintJob>();
     print_job_->Initialize(std::move(query), RenderSourceName(),
@@ -115,8 +121,6 @@
   }
 
   base::RunLoop* run_loop_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(TestPrintViewManager);
 };
 
 TEST_F(PrintViewManagerTest, PrintSubFrameAndDestroy) {
@@ -142,14 +146,15 @@
 }
 
 #if defined(OS_WIN)
-// Verifies that StartPdfToPostScriptConversion is called with the correct
+// Verifies that `StartPdfToPostScriptConversion` is called with the correct
 // printable area offsets. See crbug.com/821485.
 TEST_F(PrintViewManagerTest, PostScriptHasCorrectOffsets) {
   scoped_refptr<TestPrintQueriesQueue> queue =
       base::MakeRefCounted<TestPrintQueriesQueue>();
 
   // Setup PostScript printer with printable area offsets of 0.1in.
-  queue->SetupPrinterType(PrintSettings::PrinterType::TYPE_POSTSCRIPT_LEVEL2);
+  queue->SetupPrinterLanguageType(
+      mojom::PrinterLanguageType::kPostscriptLevel2);
   int offset_in_pixels = static_cast<int>(kTestPrinterDpi * 0.1f);
   queue->SetupPrinterOffsets(offset_in_pixels, offset_in_pixels);
   g_browser_process->print_job_manager()->SetQueueForTest(queue);
@@ -181,7 +186,7 @@
 
   EXPECT_EQ(gfx::Point(60, 60), print_view_manager->physical_offsets());
   EXPECT_EQ(gfx::Rect(0, 0, 5100, 6600), print_view_manager->content_area());
-  EXPECT_EQ(PrintSettings::PrinterType::TYPE_POSTSCRIPT_LEVEL2,
+  EXPECT_EQ(mojom::PrinterLanguageType::kPostscriptLevel2,
             print_view_manager->type());
 }
 #endif
diff --git a/chrome/browser/printing/test_print_job.cc b/chrome/browser/printing/test_print_job.cc
index 022bcc33..2b6ee7e 100644
--- a/chrome/browser/printing/test_print_job.cc
+++ b/chrome/browser/printing/test_print_job.cc
@@ -14,6 +14,10 @@
 #include "printing/printed_document.h"
 #include "ui/gfx/geometry/size.h"
 
+#if defined(OS_WIN)
+#include "printing/mojom/print.mojom.h"
+#endif
+
 namespace printing {
 
 void TestPrintJob::Initialize(std::unique_ptr<PrinterQuery> query,
@@ -54,7 +58,7 @@
     const gfx::Rect& content_area) {
   page_size_ = page_size;
   content_area_ = content_area;
-  type_ = PrintSettings::PrinterType::TYPE_NONE;
+  type_ = mojom::PrinterLanguageType::kNone;
 }
 
 void TestPrintJob::StartPdfToPostScriptConversion(
@@ -64,15 +68,15 @@
     bool ps_level2) {
   content_area_ = content_area;
   physical_offsets_ = physical_offsets;
-  type_ = ps_level2 ? PrintSettings::PrinterType::TYPE_POSTSCRIPT_LEVEL2
-                    : PrintSettings::PrinterType::TYPE_POSTSCRIPT_LEVEL3;
+  type_ = ps_level2 ? mojom::PrinterLanguageType::kPostscriptLevel2
+                    : mojom::PrinterLanguageType::kPostscriptLevel3;
 }
 
 void TestPrintJob::StartPdfToTextConversion(
     scoped_refptr<base::RefCountedMemory> bytes,
     const gfx::Size& page_size) {
   page_size_ = page_size;
-  type_ = PrintSettings::PrinterType::TYPE_TEXTONLY;
+  type_ = mojom::PrinterLanguageType::kTextOnly;
 }
 #endif  // defined(OS_WIN)
 
diff --git a/chrome/browser/printing/test_print_job.h b/chrome/browser/printing/test_print_job.h
index c1fa0d2..491e65d 100644
--- a/chrome/browser/printing/test_print_job.h
+++ b/chrome/browser/printing/test_print_job.h
@@ -13,41 +13,45 @@
 #include "chrome/browser/printing/print_job.h"
 #include "printing/print_settings.h"
 
+#if defined(OS_WIN)
+#include "printing/mojom/print.mojom.h"
+#endif
+
 namespace printing {
 
 class PrinterQuery;
 
 class TestPrintJob : public PrintJob {
  public:
-  // Create a empty PrintJob. When initializing with this constructor,
-  // post-constructor initialization must be done with Initialize().
+  // Create an empty `PrintJob`. When initializing with this constructor,
+  // post-constructor initialization must be done with `Initialize()`.
   TestPrintJob() = default;
 
-  // Getters for values stored by TestPrintJob in Start...Converter functions.
+  // Getters for values stored by `TestPrintJob` in Start...Converter functions.
   const gfx::Size& page_size() const { return page_size_; }
   const gfx::Rect& content_area() const { return content_area_; }
   const gfx::Point& physical_offsets() const { return physical_offsets_; }
 #if defined(OS_WIN)
-  PrintSettings::PrinterType type() const { return type_; }
+  mojom::PrinterLanguageType type() const { return type_; }
 #endif
 
-  // content::NotificationObserver implementation. Deliberately empty.
+  // `content::NotificationObserver` implementation. Deliberately empty.
   void Observe(int type,
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override {}
 
-  // All remaining functions are PrintJob implementation.
+  // All remaining functions are `PrintJob` implementation.
   void Initialize(std::unique_ptr<PrinterQuery> query,
                   const std::u16string& name,
                   uint32_t page_count) override;
 
-  // Sets |job_pending_| to true.
+  // Sets `job_pending_` to true.
   void StartPrinting() override;
 
-  // Sets |job_pending_| to false and deletes the worker.
+  // Sets `job_pending_` to false and deletes the worker.
   void Stop() override;
 
-  // Sets |job_pending_| to false and deletes the worker.
+  // Sets `job_pending_` to false and deletes the worker.
   void Cancel() override;
 
   // Intentional no-op, returns true.
@@ -77,7 +81,7 @@
   gfx::Rect content_area_;
   gfx::Point physical_offsets_;
 #if defined(OS_WIN)
-  PrintSettings::PrinterType type_;
+  mojom::PrinterLanguageType type_;
 #endif
 };
 
diff --git a/chrome/browser/printing/test_printer_query.cc b/chrome/browser/printing/test_printer_query.cc
index 82572a5..028bd4c 100644
--- a/chrome/browser/printing/test_printer_query.cc
+++ b/chrome/browser/printing/test_printer_query.cc
@@ -15,6 +15,10 @@
 #include "printing/print_settings_conversion.h"
 #include "printing/units.h"
 
+#if defined(OS_WIN)
+#include "printing/mojom/print.mojom.h"
+#endif
+
 namespace printing {
 
 std::unique_ptr<PrinterQuery> TestPrintQueriesQueue::CreatePrinterQuery(
@@ -23,7 +27,7 @@
   auto test_query =
       std::make_unique<TestPrinterQuery>(render_process_id, render_frame_id);
 #if defined(OS_WIN)
-  test_query->SetPrinterType(printer_type_);
+  test_query->SetPrinterLanguageType(printer_language_type_);
 #endif
   test_query->SetPrintableAreaOffsets(printable_offset_x_, printable_offset_y_);
 
@@ -36,8 +40,9 @@
 }
 
 #if defined(OS_WIN)
-void TestPrintQueriesQueue::SetupPrinterType(PrintSettings::PrinterType type) {
-  printer_type_ = type;
+void TestPrintQueriesQueue::SetupPrinterLanguageType(
+    mojom::PrinterLanguageType type) {
+  printer_language_type_ = type;
 }
 #endif
 
@@ -50,7 +55,7 @@
                                    base::OnceClosure callback) {
   DCHECK(offsets_);
 #if defined(OS_WIN)
-  DCHECK(printer_type_);
+  DCHECK(printer_language_type_);
 #endif
   std::unique_ptr<PrintSettings> settings =
       PrintSettingsFromJobSettings(new_settings);
@@ -71,15 +76,15 @@
   paper_rect.Inset(offsets_->x(), offsets_->y());
   settings->SetPrinterPrintableArea(paper_size, paper_rect, true);
 #if defined(OS_WIN)
-  settings->set_printer_type(*printer_type_);
+  settings->set_printer_language_type(*printer_language_type_);
 #endif
 
   GetSettingsDone(std::move(callback), std::move(settings), result);
 }
 
 #if defined(OS_WIN)
-void TestPrinterQuery::SetPrinterType(PrintSettings::PrinterType type) {
-  printer_type_ = type;
+void TestPrinterQuery::SetPrinterLanguageType(mojom::PrinterLanguageType type) {
+  printer_language_type_ = type;
 }
 #endif
 
diff --git a/chrome/browser/printing/test_printer_query.h b/chrome/browser/printing/test_printer_query.h
index 88ca62b..5199a20 100644
--- a/chrome/browser/printing/test_printer_query.h
+++ b/chrome/browser/printing/test_printer_query.h
@@ -14,61 +14,69 @@
 #include "chrome/browser/printing/print_job_manager.h"
 #include "chrome/browser/printing/printer_query.h"
 
+#if defined(OS_WIN)
+#include "printing/mojom/print.mojom.h"
+#endif
+
 namespace printing {
 
 class TestPrintQueriesQueue : public PrintQueriesQueue {
  public:
   TestPrintQueriesQueue() = default;
+  TestPrintQueriesQueue(const TestPrintQueriesQueue&) = delete;
+  TestPrintQueriesQueue& operator=(const TestPrintQueriesQueue&) = delete;
 
-  // Creates a TestPrinterQuery. Sets up the printer query with the printer
-  // settings indicated by |printable_offset_x_|, |printable_offset_y_|, and
-  // |printer_type_|.
+  // Creates a `TestPrinterQuery`. Sets up the printer query with the printer
+  // settings indicated by `printable_offset_x_`, `printable_offset_y_`, and
+  // `print_driver_type_`.
   std::unique_ptr<PrinterQuery> CreatePrinterQuery(
       int render_process_id,
       int render_frame_id) override;
 
-  // Sets the printer's printable area offsets to |offset_x| and |offset_y|,
+  // Sets the printer's printable area offsets to `offset_x` and `offset_y`,
   // which should be in pixels. Used to fill in printer settings that would
-  // normally be filled in by the backend |PrintingContext|.
+  // normally be filled in by the backend `PrintingContext`.
   void SetupPrinterOffsets(int offset_x, int offset_y);
 
 #if defined(OS_WIN)
-  // Sets the printer type to |type|. Used to fill in printer settings that
-  // would normally be filled in by the backend |PrintingContext|.
-  void SetupPrinterType(PrintSettings::PrinterType type);
+  // Sets the printer type to `type`. Used to fill in printer settings that
+  // would normally be filled in by the backend `PrintingContext`.
+  void SetupPrinterLanguageType(mojom::PrinterLanguageType type);
 #endif
 
  private:
   ~TestPrintQueriesQueue() override {}
 
 #if defined(OS_WIN)
-  PrintSettings::PrinterType printer_type_;
+  mojom::PrinterLanguageType printer_language_type_;
 #endif
   int printable_offset_x_;
   int printable_offset_y_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestPrintQueriesQueue);
 };
 
 class TestPrinterQuery : public PrinterQuery {
  public:
-  // Can only be called on the IO thread, since this inherits from PrinterQuery.
+  // Can only be called on the IO thread, since this inherits from
+  // `PrinterQuery`.
   TestPrinterQuery(int render_process_id, int render_frame_id);
+  TestPrinterQuery(const TestPrinterQuery&) = delete;
+  TestPrinterQuery& operator=(const TestPrinterQuery&) = delete;
   ~TestPrinterQuery() override;
 
-  // Updates the current settings with |new_settings| dictionary values. Also
-  // fills in the settings with values from |offsets_| and |printer_type_| that
-  // would normally be filled in by the PrintingContext.
+  // Updates the current settings with `new_settings` dictionary values. Also
+  // fills in the settings with values from `offsets_` and `printer_type_` that
+  // would normally be filled in by the `PrintingContext`.
   void SetSettings(base::Value new_settings,
                    base::OnceClosure callback) override;
 
 #if defined(OS_WIN)
-  // Sets |printer_type_| to |type|. Should be called before SetSettings().
-  void SetPrinterType(PrintSettings::PrinterType type);
+  // Sets `printer_language_type_` to `type`. Should be called before
+  // `SetSettings()`.
+  void SetPrinterLanguageType(mojom::PrinterLanguageType type);
 #endif
 
-  // Sets printer offsets to |offset_x| and |offset_y|, which should be in DPI.
-  // Should be called before SetSettings().
+  // Sets printer offsets to `offset_x` and `offset_y`, which should be in DPI.
+  // Should be called before `SetSettings()`.
   void SetPrintableAreaOffsets(int offset_x, int offset_y);
 
   // Intentional no-op.
@@ -77,10 +85,8 @@
  private:
   absl::optional<gfx::Point> offsets_;
 #if defined(OS_WIN)
-  absl::optional<PrintSettings::PrinterType> printer_type_;
+  absl::optional<mojom::PrinterLanguageType> printer_language_type_;
 #endif
-
-  DISALLOW_COPY_AND_ASSIGN(TestPrinterQuery);
 };
 
 }  // namespace printing
diff --git a/chrome/browser/privacy_budget/BUILD.gn b/chrome/browser/privacy_budget/BUILD.gn
index 74f90314..7aed98b 100644
--- a/chrome/browser/privacy_budget/BUILD.gn
+++ b/chrome/browser/privacy_budget/BUILD.gn
@@ -6,6 +6,7 @@
   sources = [
     "encountered_surface_tracker.h",
     "identifiability_study_state.h",
+    "mesa_distribution.h",
     "privacy_budget_metrics_provider.h",
     "privacy_budget_prefs.h",
     "privacy_budget_ukm_entry_filter.h",
@@ -51,6 +52,7 @@
     "encountered_surface_tracker_unittest.cc",
     "identifiability_study_state_unittest.cc",
     "inspectable_identifiability_study_state.h",
+    "mesa_distribution_unittest.cc",
     "privacy_budget_metrics_provider_unittest.cc",
     "privacy_budget_ukm_entry_filter_unittest.cc",
   ]
diff --git a/chrome/browser/privacy_budget/identifiability_study_state.h b/chrome/browser/privacy_budget/identifiability_study_state.h
index 583997c..79b051be 100644
--- a/chrome/browser/privacy_budget/identifiability_study_state.h
+++ b/chrome/browser/privacy_budget/identifiability_study_state.h
@@ -12,6 +12,7 @@
 #include "chrome/browser/privacy_budget/encountered_surface_tracker.h"
 #include "chrome/browser/privacy_budget/privacy_budget_prefs.h"
 #include "chrome/common/privacy_budget/privacy_budget_settings_provider.h"
+#include "chrome/common/privacy_budget/types.h"
 #include "components/prefs/pref_service.h"
 #include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
 #include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
@@ -108,10 +109,6 @@
  private:
   friend class test_utils::InspectableIdentifiabilityStudyState;
 
-  using IdentifiableSurfaceSet =
-      PrivacyBudgetSettingsProvider::IdentifiableSurfaceSet;
-  using IdentifiableSurfaceTypeSet =
-      PrivacyBudgetSettingsProvider::IdentifiableSurfaceTypeSet;
   using SurfaceSelectionRateMap =
       base::flat_map<blink::IdentifiableSurface,
                      int,
diff --git a/chrome/browser/privacy_budget/identifiability_study_state_unittest.cc b/chrome/browser/privacy_budget/identifiability_study_state_unittest.cc
index 731bd00c..a5fabcf 100644
--- a/chrome/browser/privacy_budget/identifiability_study_state_unittest.cc
+++ b/chrome/browser/privacy_budget/identifiability_study_state_unittest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/privacy_budget/privacy_budget_prefs.h"
 #include "chrome/common/privacy_budget/privacy_budget_features.h"
 #include "chrome/common/privacy_budget/scoped_privacy_budget_config.h"
+#include "chrome/common/privacy_budget/types.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -52,12 +53,6 @@
   return base::JoinString(list_as_strings, ",");
 }
 
-// Make names short
-using IdentifiableSurfaceSet =
-    test_utils::InspectableIdentifiabilityStudyState::IdentifiableSurfaceSet;
-using IdentifiableSurfaceTypeSet = test_utils::
-    InspectableIdentifiabilityStudyState::IdentifiableSurfaceTypeSet;
-
 }  // namespace
 
 class IdentifiabilityStudyStateTest : public ::testing::Test {
diff --git a/chrome/browser/privacy_budget/inspectable_identifiability_study_state.h b/chrome/browser/privacy_budget/inspectable_identifiability_study_state.h
index 8ef2525..b94506d 100644
--- a/chrome/browser/privacy_budget/inspectable_identifiability_study_state.h
+++ b/chrome/browser/privacy_budget/inspectable_identifiability_study_state.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_PRIVACY_BUDGET_INSPECTABLE_IDENTIFIABILITY_STUDY_STATE_H_
 
 #include "chrome/browser/privacy_budget/identifiability_study_state.h"
+#include "chrome/common/privacy_budget/types.h"
 
 namespace test_utils {
 
@@ -13,9 +14,6 @@
 // internals. Use this as a last resort.
 class InspectableIdentifiabilityStudyState : public IdentifiabilityStudyState {
  public:
-  using IdentifiabilityStudyState::IdentifiableSurfaceSet;
-  using IdentifiabilityStudyState::IdentifiableSurfaceTypeSet;
-
   explicit InspectableIdentifiabilityStudyState(PrefService* pref_service)
       : IdentifiabilityStudyState(pref_service) {}
 
diff --git a/chrome/browser/privacy_budget/mesa_distribution.h b/chrome/browser/privacy_budget/mesa_distribution.h
new file mode 100644
index 0000000..5b9ca44
--- /dev/null
+++ b/chrome/browser/privacy_budget/mesa_distribution.h
@@ -0,0 +1,114 @@
+// 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 CHROME_BROWSER_PRIVACY_BUDGET_MESA_DISTRIBUTION_H_
+#define CHROME_BROWSER_PRIVACY_BUDGET_MESA_DISTRIBUTION_H_
+
+#include <random>
+#include <set>
+#include <type_traits>
+
+#include "base/containers/contains.h"
+
+// Generates a set of integers drawn from a mesa shaped probability distribution
+// with replacement.
+//
+// The PDF is:
+//
+//            ⎧    0                            ... if x < 0
+//            ⎪
+//            ⎪    λ                            ... if 0 <= x < T
+//     P(x) = ⎨
+//            ⎪    λ (1 - γ)⁽ˣ⁻ᵀ⁾               ... otherwise
+//            ⎪
+//            ⎩
+// where ...
+//
+//   T = Value at which the PDF switches from a uniform to a geometric
+//       distribution. Referred to in code as the `pivot_point`.
+//
+//   τ = Ratio of probability between linear region of the PDF. I.e. if τ = 0.9,
+//       then 90% of the probability space is in the linear region. The ratio is
+//       referred to in code as `dist_ratio`.
+//
+//        τ
+//   λ = ───
+//        T
+//
+//         λ          τ
+//   γ = ───── = ───────────
+//       1 - τ   T * (1 - τ)
+//
+// In otherwords, the PDF is uniform up to T with a probability of λ, and then
+// switches to a geometric distribution with parameter λ that extends to
+// infinity.
+//
+// It looks like this in the form of a graph which should make a little bit more
+// sense.
+//
+//          P(x)   ▲
+//                 │
+//   probability  λ│┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┬,
+//   density       │    uniform     ┊ L        geometric
+//                 │  distribution  ┊  "._    distribution
+//                 │                ┊     `--..______
+//                 └────────────────┴──────────────────▶ x
+//                 0                T
+//
+// Why this odd combination of disjoint probability distributions?
+//
+// Such a distribution is useful when you want to select some set of elements
+// uniformly up to a threshold, but want to allow for a tail distribution that
+// extends arbitrarily past that range.
+//
+// The τ parameter establishes the balance between the linear region and the
+// geometric region, while T establishes the scale. Typically we set τ to
+// something close to 0.9 or so such that 0.1 of the probability space is
+// reserved for the long tail.
+//
+// Parameters:
+//   pivot_point: T as described above. Arbitrary range.
+//   dist_ratio : τ as described above. Must be in (0,1).
+template <typename ResultType,
+          std::enable_if_t<std::is_integral<ResultType>::value, int> = 0>
+class MesaDistribution {
+ public:
+  MesaDistribution(ResultType pivot_point, double dist_ratio)
+      : pivot_point_(pivot_point),
+        uniform_distribution_(0, std::ceil(pivot_point / dist_ratio)),
+        geometric_distribution_(dist_ratio /
+                                (pivot_point * (1.0l - dist_ratio))) {}
+  ~MesaDistribution() = default;
+
+  // Draws a single value from the distribution.
+  //
+  // `Generator` must satisfy `UniformRandomBitGenerator`.
+  // https://en.cppreference.com/w/cpp/named_req/UniformRandomBitGenerator
+  template <class Generator>
+  ResultType Get(Generator& g) {
+    ResultType v = uniform_distribution_(g);
+    if (v >= pivot_point_)
+      return pivot_point_ + geometric_distribution_(g);
+    return v;
+  }
+
+  // RandomNumberDistribution.
+  // https://en.cppreference.com/w/cpp/named_req/RandomNumberDistribution
+  //
+  // `Generator` must satisfy `UniformRandomBitGenerator`.
+  // https://en.cppreference.com/w/cpp/named_req/UniformRandomBitGenerator
+  template <class Generator>
+  ResultType operator()(Generator g) {
+    return Get(g);
+  }
+
+  ResultType pivot_point() const { return pivot_point_; }
+
+ private:
+  const ResultType pivot_point_;
+  std::uniform_int_distribution<ResultType> uniform_distribution_;
+  std::geometric_distribution<ResultType> geometric_distribution_;
+};
+
+#endif  // CHROME_BROWSER_PRIVACY_BUDGET_MESA_DISTRIBUTION_H_
diff --git a/chrome/browser/privacy_budget/mesa_distribution_unittest.cc b/chrome/browser/privacy_budget/mesa_distribution_unittest.cc
new file mode 100644
index 0000000..820df06e
--- /dev/null
+++ b/chrome/browser/privacy_budget/mesa_distribution_unittest.cc
@@ -0,0 +1,101 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/privacy_budget/mesa_distribution.h"
+#include <math.h>
+
+#include <array>
+#include <limits>
+#include <random>
+#include <set>
+
+#include "base/rand_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+constexpr auto kSeed = 3;
+constexpr auto kPivotPoint = 300;
+constexpr auto kDistRatio = 0.9l;
+}  // namespace
+
+TEST(MesaDistributionTest, Get) {
+  MesaDistribution<int> mesa(kPivotPoint, kDistRatio);
+  std::mt19937 g(kSeed);
+  auto v1 = mesa.Get(g);
+  g.seed(kSeed);
+  auto v2 = mesa.Get(g);
+  EXPECT_EQ(v1, v2);
+}
+
+// This test asserts that the MesaDistribution produces a near ideal
+// distribution of offset selections.
+//
+// We do this by drawing a very large number of samples from MesaDistribution in
+// the presence of a fairly good PRNG and verifying that the resulting
+// probability distribution is within a close margin of the ideal distribution.
+//
+// In order to pass the test, the simulation must result in an aggregate
+// probability distribution that's within ε of the ideal distribution.
+//
+// The PRNG is MT19937 with a fixed seed.
+TEST(MesaDistributionTest, SpreadTest_MaybeSlow) {
+  constexpr auto kTrials = 10'000'000lu;
+
+  // We truncate the distribution at kMaxOffset, or else we'd need to keep
+  // occurrence counts for over a very large range.
+  //
+  // Truncation changes the probability distribution as a side-effect. Observed
+  // probability density increases by a factor of 1 / CDF(kMaxOffset) where CDF
+  // is the cumulative distribution function for Mesa. However beyond
+  // 2 * kPivotPoint the CDF is incalculably close to 1.
+  constexpr auto kMaxOffset = kPivotPoint * 3;
+
+  // Lambda corresponds to λ in the description in mesa_distribution.h
+  // explaining the distribution. It's the probability density within the linear
+  // region of the distribution.
+  const auto kLambda = kDistRatio / kPivotPoint;
+
+  // Gamma corresponds to γ in the description in mesa_distribution.h. It's the
+  // parameter to the Geometric distribution in the tail region.
+  const auto kGamma = kLambda / (1 - kDistRatio);
+
+  // kEpsilon is the maximum absolute error in probability per element. We
+  // expect the experiment below to produce values within 5% of the linear
+  // region density.
+  const double kEpsilon = kLambda * 0.05 /* ±5% envelope */;
+
+  // occurrence[i] := the number of times we've seen `i`.
+  std::array<double, kMaxOffset + 1> occurrences = {0.0};
+
+  // The distribution under test:
+  MesaDistribution<int> mesa(kPivotPoint, kDistRatio);
+
+  std::mt19937 random_bit_generator(kSeed);
+
+  for (auto i = 0u; i < kTrials; ++i) {
+    auto v = mesa.Get(random_bit_generator);
+    if (v > kMaxOffset)
+      continue;
+    ++occurrences[v];
+  }
+
+  // `occurrences` is a histogram of seen values. This loop converts it to
+  // a probability.
+  for (double& occurrence : occurrences)
+    occurrence /= kTrials;
+
+  // Offsets from 0 thru `kPivotPoint - 1` (inclusive) should have the same
+  // probability. I.e. uniform.
+  for (int i = 0; i < kPivotPoint; ++i) {
+    ASSERT_NEAR(occurrences[i], kLambda, kEpsilon) << "at offset" << i;
+  }
+
+  // Offsets from `kPivotPoint` thru infinity should follow a geometric
+  // distribution. We compare the observed probabilities with the Geometric PDF.
+  double expected_pdf = kLambda;
+  for (int i = kPivotPoint; i <= kMaxOffset; ++i) {
+    ASSERT_NEAR(occurrences[i], expected_pdf, kEpsilon) << "at offset" << i;
+    expected_pdf *= 1.0l - kGamma;
+  }
+}
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 5699573..c220381 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -71,7 +71,6 @@
 #include "chrome/browser/profiles/guest_signin_observer_factory.h"
 #include "chrome/browser/profiles/renderer_updater_factory.h"
 #include "chrome/browser/safe_browsing/certificate_reporting_service_factory.h"
-#include "chrome/browser/search/suggestions/suggestions_service_factory.h"
 #include "chrome/browser/search_engines/template_url_fetcher_factory.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_client_service_factory.h"
@@ -470,7 +469,6 @@
 #if !defined(OS_ANDROID)
   StorageNotificationServiceFactory::GetInstance();
 #endif
-  suggestions::SuggestionsServiceFactory::GetInstance();
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   SupervisedUserServiceFactory::GetInstance();
 #endif
diff --git a/chrome/browser/resources/pdf/controller.js b/chrome/browser/resources/pdf/controller.js
index c5eed00..95dca320 100644
--- a/chrome/browser/resources/pdf/controller.js
+++ b/chrome/browser/resources/pdf/controller.js
@@ -466,7 +466,7 @@
   async load(fileName, data) {
     const url = URL.createObjectURL(new Blob([data]));
     this.plugin_.removeAttribute('headers');
-    this.plugin_.setAttribute('stream-url', url);
+    this.plugin_.setAttribute('src', url);
     this.plugin_.setAttribute('has-edits', '');
     this.plugin_.style.display = 'block';
     try {
diff --git a/chrome/browser/resources/pdf/pdf_viewer_base.js b/chrome/browser/resources/pdf/pdf_viewer_base.js
index 4c1b28c..23a3520 100644
--- a/chrome/browser/resources/pdf/pdf_viewer_base.js
+++ b/chrome/browser/resources/pdf/pdf_viewer_base.js
@@ -160,10 +160,10 @@
    * @private
    */
   createPlugin_(isPrintPreview) {
-    // Create the plugin object dynamically so we can set its src. The plugin
-    // element is sized to fill the entire window and is set to be fixed
-    // positioning, acting as a viewport. The plugin renders into this viewport
-    // according to the scroll position of the window.
+    // Create the plugin object dynamically. The plugin element is sized to
+    // fill the entire window and is set to be fixed positioning, acting as a
+    // viewport. The plugin renders into this viewport according to the scroll
+    // position of the window.
     const plugin =
         /** @type {!HTMLEmbedElement} */ (document.createElement('embed'));
 
@@ -174,9 +174,8 @@
     plugin.id = 'plugin';
     plugin.type = 'application/x-google-chrome-pdf';
 
-    plugin.setAttribute('src', this.originalUrl);
-    plugin.setAttribute(
-        'stream-url', this.browserApi.getStreamInfo().streamUrl);
+    plugin.setAttribute('original-url', this.originalUrl);
+    plugin.setAttribute('src', this.browserApi.getStreamInfo().streamUrl);
     let headers = '';
     for (const header in this.browserApi.getStreamInfo().responseHeaders) {
       headers += header + ': ' +
diff --git a/chrome/browser/resources/tab_search/infinite_list.js b/chrome/browser/resources/tab_search/infinite_list.js
index 4910483..b46a5ca9 100644
--- a/chrome/browser/resources/tab_search/infinite_list.js
+++ b/chrome/browser/resources/tab_search/infinite_list.js
@@ -252,7 +252,16 @@
    * @private
    */
   getDomItem_(index) {
-    return this.instances_[index].children[0];
+    const instance = this.instances_[index];
+    if (instance === undefined) {
+      // TODO(crbug.com/1225247): Remove this after we root cause the issue.
+      console.error(`Unexpected call to non-existing instance index: ${
+          index}. Instance count: ${this.instances_.length}. Item count: ${
+          this.items.length}`);
+      return undefined;
+    }
+
+    return instance.children[0];
   }
 
   /**
@@ -327,6 +336,9 @@
     const selectedItemIndex = this.$.selector.selected;
     if (selectedItemIndex !== undefined) {
       const selectedItem = this.getSelectableDomItem_(selectedItemIndex);
+      if (selectedItem === undefined) {
+        return false;
+      }
       const deepActiveElement = getDeepActiveElement();
 
       return selectedItem === deepActiveElement ||
diff --git a/chrome/browser/search/drive/drive_service.cc b/chrome/browser/search/drive/drive_service.cc
index fb7c18a..22a1b11 100644
--- a/chrome/browser/search/drive/drive_service.cc
+++ b/chrome/browser/search/drive/drive_service.cc
@@ -44,7 +44,10 @@
     "platform_type": "%s",
     "scenario_type": "CHROME_NTP_FILES",
     "language_code": "%s",
-    "request_type": "LIVE_REQUEST"
+    "request_type": "LIVE_REQUEST",
+    "client_tags": {
+      "name": "%s"
+    }
   },
   "max_suggestions": 3,
   "type_detail_fields": "drive_item.title,drive_item.mimeType"
@@ -247,7 +250,11 @@
                                                  kTrafficAnnotation);
   url_loader_->SetRetryOptions(0, network::SimpleURLLoader::RETRY_NEVER);
   url_loader_->AttachStringForUpload(
-      base::StringPrintf(kRequestBody, kPlatform, application_locale_.c_str()),
+      base::StringPrintf(kRequestBody, kPlatform, application_locale_.c_str(),
+                         base::GetFieldTrialParamValueByFeature(
+                             ntp_features::kNtpDriveModule,
+                             ntp_features::kNtpDriveModuleExperimentGroupParam)
+                             .c_str()),
       "application/json");
   url_loader_->DownloadToString(
       url_loader_factory_.get(),
diff --git a/chrome/browser/search/drive/drive_service_unittest.cc b/chrome/browser/search/drive/drive_service_unittest.cc
index 373999a..2bb24913 100644
--- a/chrome/browser/search/drive/drive_service_unittest.cc
+++ b/chrome/browser/search/drive/drive_service_unittest.cc
@@ -355,6 +355,31 @@
                                              base::PersistentHash("drive")));
 }
 
+TEST_F(DriveServiceTest, AddsClientTagIfRequested) {
+  // Make sure we are not in the dismissed time window.
+  prefs_.SetTime(DriveService::kLastDismissedTimePrefName, base::Time::Now());
+  task_environment_.AdvanceClock(DriveService::kDismissDuration);
+
+  // Set client tag.
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeatureWithParameters(
+      ntp_features::kNtpDriveModule,
+      {{ntp_features::kNtpDriveModuleExperimentGroupParam, "foo"}});
+
+  service_->GetDriveFiles(DriveService::GetFilesCallback());
+  identity_test_env.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+      "foo_token", base::Time());
+  EXPECT_EQ(1, test_url_loader_factory_.NumPending());
+  std::string request_body(test_url_loader_factory_.pending_requests()
+                               ->at(0)
+                               .request.request_body->elements()
+                               ->at(0)
+                               .As<network::DataElementBytes>()
+                               .AsStringPiece());
+  auto body_value = base::JSONReader::Read(request_body);
+  EXPECT_EQ("foo", *body_value->FindStringPath("client_info.client_tags.name"));
+}
+
 TEST_F(DriveServiceTest, PassesNoDataIfDismissed) {
   bool passed_no_data = false;
   base::MockCallback<DriveService::GetFilesCallback> callback;
diff --git a/chrome/browser/search/instant_service_factory.cc b/chrome/browser/search/instant_service_factory.cc
index 905b26b..038da6c 100644
--- a/chrome/browser/search/instant_service_factory.cc
+++ b/chrome/browser/search/instant_service_factory.cc
@@ -4,12 +4,9 @@
 
 #include "chrome/browser/search/instant_service_factory.h"
 
-#include "chrome/browser/history/top_sites_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/instant_service.h"
-#include "chrome/browser/search/suggestions/suggestions_service_factory.h"
-#include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/search/search.h"
@@ -31,10 +28,7 @@
     : BrowserContextKeyedServiceFactory(
         "InstantService",
         BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(suggestions::SuggestionsServiceFactory::GetInstance());
-  DependsOn(TemplateURLServiceFactory::GetInstance());
   DependsOn(ThemeServiceFactory::GetInstance());
-  DependsOn(TopSitesFactory::GetInstance());
 }
 
 InstantServiceFactory::~InstantServiceFactory() = default;
diff --git a/chrome/browser/search/suggestions/OWNERS b/chrome/browser/search/suggestions/OWNERS
deleted file mode 100644
index 919d244..0000000
--- a/chrome/browser/search/suggestions/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://components/suggestions/OWNERS
diff --git a/chrome/browser/search/suggestions/suggestions_service_factory.cc b/chrome/browser/search/suggestions/suggestions_service_factory.cc
deleted file mode 100644
index caa8de5..0000000
--- a/chrome/browser/search/suggestions/suggestions_service_factory.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/search/suggestions/suggestions_service_factory.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/task/post_task.h"
-#include "base/time/default_tick_clock.h"
-#include "chrome/browser/profiles/incognito_helpers.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/sync/sync_service_factory.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/pref_service.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
-#include "components/suggestions/blocklist_store.h"
-#include "components/suggestions/proto/suggestions.pb.h"
-#include "components/suggestions/suggestions_service_impl.h"
-#include "components/suggestions/suggestions_store.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/storage_partition.h"
-
-using content::BrowserThread;
-
-namespace suggestions {
-
-// static
-SuggestionsService* SuggestionsServiceFactory::GetForProfile(Profile* profile) {
-  return static_cast<SuggestionsService*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
-}
-
-// static
-SuggestionsServiceFactory* SuggestionsServiceFactory::GetInstance() {
-  return base::Singleton<SuggestionsServiceFactory>::get();
-}
-
-SuggestionsServiceFactory::SuggestionsServiceFactory()
-    : BrowserContextKeyedServiceFactory(
-          "SuggestionsService",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(IdentityManagerFactory::GetInstance());
-  DependsOn(SyncServiceFactory::GetInstance());
-}
-
-SuggestionsServiceFactory::~SuggestionsServiceFactory() {}
-
-KeyedService* SuggestionsServiceFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  Profile* profile = static_cast<Profile*>(context);
-
-  signin::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfile(profile);
-  syncer::SyncService* sync_service =
-      SyncServiceFactory::GetForProfile(profile);
-
-  std::unique_ptr<SuggestionsStore> suggestions_store(
-      new SuggestionsStore(profile->GetPrefs()));
-  std::unique_ptr<BlocklistStore> blocklist_store(
-      new BlocklistStore(profile->GetPrefs()));
-
-  return new SuggestionsServiceImpl(
-      identity_manager, sync_service,
-      profile->GetDefaultStoragePartition()
-          ->GetURLLoaderFactoryForBrowserProcess(),
-      std::move(suggestions_store), std::move(blocklist_store),
-      base::DefaultTickClock::GetInstance());
-}
-
-void SuggestionsServiceFactory::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
-  SuggestionsServiceImpl::RegisterProfilePrefs(registry);
-}
-
-}  // namespace suggestions
diff --git a/chrome/browser/search/suggestions/suggestions_service_factory.h b/chrome/browser/search/suggestions/suggestions_service_factory.h
deleted file mode 100644
index 18b4364..0000000
--- a/chrome/browser/search/suggestions/suggestions_service_factory.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SEARCH_SUGGESTIONS_SUGGESTIONS_SERVICE_FACTORY_H_
-#define CHROME_BROWSER_SEARCH_SUGGESTIONS_SUGGESTIONS_SERVICE_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-class Profile;
-
-namespace suggestions {
-
-class SuggestionsService;
-
-// Singleton that owns all SuggestionsServices and associates them with
-// Profiles.
-class SuggestionsServiceFactory : public BrowserContextKeyedServiceFactory {
- public:
-  // Returns the SuggestionsService for |profile|.
-  static suggestions::SuggestionsService* GetForProfile(Profile* profile);
-
-  static SuggestionsServiceFactory* GetInstance();
-
- private:
-  friend struct base::DefaultSingletonTraits<SuggestionsServiceFactory>;
-
-  SuggestionsServiceFactory();
-  ~SuggestionsServiceFactory() override;
-
-  // Overrides from BrowserContextKeyedServiceFactory:
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* profile) const override;
-  void RegisterProfilePrefs(
-      user_prefs::PrefRegistrySyncable* registry) override;
-
-  DISALLOW_COPY_AND_ASSIGN(SuggestionsServiceFactory);
-};
-
-}  // namespace suggestions
-
-#endif  // CHROME_BROWSER_SEARCH_SUGGESTIONS_SUGGESTIONS_SERVICE_FACTORY_H_
diff --git a/chrome/browser/search/suggestions/suggestions_ui.cc b/chrome/browser/search/suggestions/suggestions_ui.cc
deleted file mode 100644
index f459d4e..0000000
--- a/chrome/browser/search/suggestions/suggestions_ui.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2016 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/search/suggestions/suggestions_ui.h"
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search/suggestions/suggestions_service_factory.h"
-#include "chrome/common/url_constants.h"
-#include "components/suggestions/webui/suggestions_source.h"
-#include "content/public/browser/url_data_source.h"
-
-namespace suggestions {
-
-namespace {
-
-// Glues a SuggestionsSource instance to //chrome.
-class SuggestionsSourceWrapper : public content::URLDataSource {
- public:
-  explicit SuggestionsSourceWrapper(SuggestionsService* suggestions_service);
-  ~SuggestionsSourceWrapper() override;
-
-  // content::URLDataSource implementation.
-  std::string GetSource() override;
-  void StartDataRequest(
-      const GURL& url,
-      const content::WebContents::Getter& wc_getter,
-      content::URLDataSource::GotDataCallback callback) override;
-  std::string GetMimeType(const std::string& path) override;
-
- private:
-  SuggestionsSource suggestions_source_;
-
-  DISALLOW_COPY_AND_ASSIGN(SuggestionsSourceWrapper);
-};
-
-SuggestionsSourceWrapper::SuggestionsSourceWrapper(
-    SuggestionsService* suggestions_service)
-    : suggestions_source_(suggestions_service,
-                          chrome::kChromeUISuggestionsURL) {}
-
-SuggestionsSourceWrapper::~SuggestionsSourceWrapper() {}
-
-std::string SuggestionsSourceWrapper::GetSource() {
-  return chrome::kChromeUISuggestionsHost;
-}
-
-void SuggestionsSourceWrapper::StartDataRequest(
-    const GURL& url,
-    const content::WebContents::Getter& wc_getter,
-    content::URLDataSource::GotDataCallback callback) {
-  suggestions_source_.StartDataRequest(
-      content::URLDataSource::URLToRequestPath(url), std::move(callback));
-}
-
-std::string SuggestionsSourceWrapper::GetMimeType(const std::string& path) {
-  return "text/html";
-}
-
-}  // namespace
-
-SuggestionsUI::SuggestionsUI(content::WebUI* web_ui)
-    : content::WebUIController(web_ui) {
-  Profile* profile = Profile::FromWebUI(web_ui);
-  content::URLDataSource::Add(
-      profile, std::make_unique<SuggestionsSourceWrapper>(
-                   SuggestionsServiceFactory::GetForProfile(profile)));
-}
-
-SuggestionsUI::~SuggestionsUI() {}
-
-}  // namespace suggestions
diff --git a/chrome/browser/search/suggestions/suggestions_ui.h b/chrome/browser/search/suggestions/suggestions_ui.h
deleted file mode 100644
index feb7c51..0000000
--- a/chrome/browser/search/suggestions/suggestions_ui.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2016 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_SEARCH_SUGGESTIONS_SUGGESTIONS_UI_H_
-#define CHROME_BROWSER_SEARCH_SUGGESTIONS_SUGGESTIONS_UI_H_
-
-#include "base/macros.h"
-#include "content/public/browser/web_ui_controller.h"
-
-namespace content {
-class WebUI;
-}
-
-namespace suggestions {
-
-// The WebUIController for chrome://suggestions. Renders a webpage to list
-// SuggestionsService data.
-class SuggestionsUI : public content::WebUIController {
- public:
-  explicit SuggestionsUI(content::WebUI* web_ui);
-  ~SuggestionsUI() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SuggestionsUI);
-};
-
-}  // namespace suggestions
-
-#endif  // CHROME_BROWSER_SEARCH_SUGGESTIONS_SUGGESTIONS_UI_H_
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc
index ecc11a0..ce2b1f3 100644
--- a/chrome/browser/signin/chrome_signin_helper.cc
+++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -260,7 +260,7 @@
 
   // 2. Displaying a reauthentication window
   if (!manage_accounts_params.email.empty()) {
-    // TODO(https://crbug.com/1177728): enable this for lacros.
+    // TODO(https://crbug.com/1226055): enable this for lacros.
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     // Do not display the re-authentication dialog if this event was triggered
     // by supervision being enabled for an account.  In this situation, a
diff --git a/chrome/browser/speech/cros_speech_recognition_service_factory.h b/chrome/browser/speech/cros_speech_recognition_service_factory.h
index 5c58caf5..fa7908e 100644
--- a/chrome/browser/speech/cros_speech_recognition_service_factory.h
+++ b/chrome/browser/speech/cros_speech_recognition_service_factory.h
@@ -5,15 +5,11 @@
 #ifndef CHROME_BROWSER_SPEECH_CROS_SPEECH_RECOGNITION_SERVICE_FACTORY_H_
 #define CHROME_BROWSER_SPEECH_CROS_SPEECH_RECOGNITION_SERVICE_FACTORY_H_
 
+#include "base/no_destructor.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 
 class Profile;
 
-namespace base {
-template <class T>
-class NoDestructor;
-}  // namespace base
-
 namespace speech {
 class SpeechRecognitionService;
 }  // namespace speech
diff --git a/chrome/browser/speech/speech_recognition_client_browser_interface_factory.h b/chrome/browser/speech/speech_recognition_client_browser_interface_factory.h
index b7b31e1..f3856b6 100644
--- a/chrome/browser/speech/speech_recognition_client_browser_interface_factory.h
+++ b/chrome/browser/speech/speech_recognition_client_browser_interface_factory.h
@@ -5,15 +5,11 @@
 #ifndef CHROME_BROWSER_SPEECH_SPEECH_RECOGNITION_CLIENT_BROWSER_INTERFACE_FACTORY_H_
 #define CHROME_BROWSER_SPEECH_SPEECH_RECOGNITION_CLIENT_BROWSER_INTERFACE_FACTORY_H_
 
+#include "base/no_destructor.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 
 class Profile;
 
-namespace base {
-template <class T>
-class NoDestructor;
-}  // namespace base
-
 namespace speech {
 class SpeechRecognitionClientBrowserInterface;
 }  // namespace speech
diff --git a/chrome/browser/speech/speech_recognition_service_factory.h b/chrome/browser/speech/speech_recognition_service_factory.h
index 6c45c04..6ef7270 100644
--- a/chrome/browser/speech/speech_recognition_service_factory.h
+++ b/chrome/browser/speech/speech_recognition_service_factory.h
@@ -5,15 +5,11 @@
 #ifndef CHROME_BROWSER_SPEECH_SPEECH_RECOGNITION_SERVICE_FACTORY_H_
 #define CHROME_BROWSER_SPEECH_SPEECH_RECOGNITION_SERVICE_FACTORY_H_
 
+#include "base/no_destructor.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 
 class Profile;
 
-namespace base {
-template <class T>
-class NoDestructor;
-}  // namespace base
-
 namespace speech {
 class SpeechRecognitionService;
 }  // namespace speech
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
index b8eef03..f96d27e3 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
@@ -11,13 +11,12 @@
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import org.chromium.base.Callback;
+import org.chromium.base.FeatureList;
 import org.chromium.base.Log;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.flags.BooleanCachedFieldTrialParameter;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.flags.IntCachedFieldTrialParameter;
 import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridgeFactory;
 import org.chromium.chrome.browser.page_annotations.BuyableProductPageAnnotation;
 import org.chromium.chrome.browser.page_annotations.PageAnnotation;
@@ -66,28 +65,12 @@
     private static final long TWO_UNITS = 2 * MICROS_TO_UNITS;
     private static final long TEN_UNITS = 10 * MICROS_TO_UNITS;
     private static final int MINIMUM_DROP_PERCENTAGE = 10;
-    private static final long ONE_WEEK_MS = TimeUnit.DAYS.toMillis(7);
+    private static final int ONE_WEEK_MS = (int) TimeUnit.DAYS.toMillis(7);
 
     @VisibleForTesting
     public static final long ONE_HOUR_MS = TimeUnit.HOURS.toMillis(1);
 
-    private static final long NINETY_DAYS_SECONDS = TimeUnit.DAYS.toSeconds(90);
-
-    public static final IntCachedFieldTrialParameter STALE_TAB_THRESHOLD_SECONDS =
-            new IntCachedFieldTrialParameter(ChromeFeatureList.COMMERCE_PRICE_TRACKING,
-                    STALE_TAB_THRESHOLD_SECONDS_PARAM, (int) NINETY_DAYS_SECONDS);
-
-    public static final IntCachedFieldTrialParameter TIME_TO_LIVE_MS =
-            new IntCachedFieldTrialParameter(ChromeFeatureList.COMMERCE_PRICE_TRACKING,
-                    TIME_TO_LIVE_MS_PARAM, (int) ONE_HOUR_MS);
-
-    public static final IntCachedFieldTrialParameter DISPLAY_TIME_MS =
-            new IntCachedFieldTrialParameter(ChromeFeatureList.COMMERCE_PRICE_TRACKING,
-                    DISPLAY_TIME_MS_PARAM, (int) ONE_WEEK_MS);
-
-    public static final BooleanCachedFieldTrialParameter PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE =
-            new BooleanCachedFieldTrialParameter(ChromeFeatureList.COMMERCE_PRICE_TRACKING,
-                    PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE_PARAM, false);
+    private static final int NINETY_DAYS_SECONDS = (int) TimeUnit.DAYS.toSeconds(90);
 
     @VisibleForTesting
     public static final long NO_TRANSITIONS_OCCURRED = -1;
@@ -99,6 +82,8 @@
     protected static PageAnnotationsServiceFactory sPageAnnotationsServiceFactory =
             new PageAnnotationsServiceFactory();
 
+    private static boolean sPriceTrackingWithOptimizationGuideForTesting;
+
     public long mLastPriceChangeTimeMs = NO_TRANSITIONS_OCCURRED;
 
     private PriceDropData mPriceDropData = new PriceDropData();
@@ -127,7 +112,7 @@
         private static final OptimizationGuideBridgeFactory sOptimizationGuideBridgeFactory;
         static {
             List<HintsProto.OptimizationType> optimizationTypes;
-            if (PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE.getValue()) {
+            if (isPriceTrackingWithOptimizationGuideEnabled()) {
                 optimizationTypes =
                         Arrays.asList(HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR,
                                 HintsProto.OptimizationType.PRICE_TRACKING);
@@ -301,7 +286,7 @@
 
             @Override
             public void onDidFinishNavigation(Tab tab, NavigationHandle navigationHandle) {
-                if (ShoppingPersistedTabData.PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE.getValue()) {
+                if (isPriceTrackingWithOptimizationGuideEnabled()) {
                     prefetchOnNewNavigation(tab, navigationHandle);
                 }
             }
@@ -331,7 +316,7 @@
                 (supplierCallback)
                         -> {
                     if (getTimeSinceTabLastOpenedMs(tab)
-                            > TimeUnit.SECONDS.toMillis(STALE_TAB_THRESHOLD_SECONDS.getValue())) {
+                            > TimeUnit.SECONDS.toMillis(getStaleTabThresholdSeconds())) {
                         supplierCallback.onResult(null);
                         return;
                     }
@@ -342,7 +327,7 @@
                             return;
                         }
 
-                        if (PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE.getValue()) {
+                        if (isPriceTrackingWithOptimizationGuideEnabled()) {
                             OptimizationGuideBridgeFactoryHolder.sOptimizationGuideBridgeFactory
                                     .create()
                                     .canApplyOptimization(tab.getUrl(),
@@ -681,7 +666,7 @@
 
     private boolean isPriceChangeStale() {
         return mLastPriceChangeTimeMs != NO_TRANSITIONS_OCCURRED
-                && System.currentTimeMillis() - mLastPriceChangeTimeMs > DISPLAY_TIME_MS.getValue();
+                && System.currentTimeMillis() - mLastPriceChangeTimeMs > getDisplayTimeMs();
     }
 
     private boolean isQualifyingPriceDrop() {
@@ -774,7 +759,12 @@
 
     @Override
     public long getTimeToLiveMs() {
-        return TIME_TO_LIVE_MS.getValue();
+        if (FeatureList.isInitialized()) {
+            return ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
+                    ChromeFeatureList.COMMERCE_PRICE_TRACKING, TIME_TO_LIVE_MS_PARAM,
+                    (int) ONE_HOUR_MS);
+        }
+        return (int) ONE_HOUR_MS;
     }
 
     @VisibleForTesting
@@ -793,6 +783,47 @@
         super.destroy();
     }
 
+    /**
+     * @return the threshold in seconds at which a {@link Tab} is considered stale
+     * (and we no longer acquire shopping data for stale tabs).
+     */
+    public static int getStaleTabThresholdSeconds() {
+        if (FeatureList.isInitialized()) {
+            return ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
+                    ChromeFeatureList.COMMERCE_PRICE_TRACKING, STALE_TAB_THRESHOLD_SECONDS_PARAM,
+                    NINETY_DAYS_SECONDS);
+        }
+        return NINETY_DAYS_SECONDS;
+    }
+
+    /**
+     * @return true is acquiring pricing data using OptimizationGuide is enabled.
+     */
+    public static boolean isPriceTrackingWithOptimizationGuideEnabled() {
+        if (sPriceTrackingWithOptimizationGuideForTesting) {
+            return true;
+        }
+        if (FeatureList.isInitialized()) {
+            return ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
+                    ChromeFeatureList.COMMERCE_PRICE_TRACKING,
+                    PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE_PARAM, false);
+        }
+        return false;
+    }
+
+    @VisibleForTesting
+    public static void enablePriceTrackingWithOptimizationGuideForTesting() {
+        sPriceTrackingWithOptimizationGuideForTesting = true;
+    }
+
+    private static int getDisplayTimeMs() {
+        if (FeatureList.isInitialized()) {
+            return ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
+                    ChromeFeatureList.COMMERCE_PRICE_TRACKING, DISPLAY_TIME_MS_PARAM, ONE_WEEK_MS);
+        }
+        return ONE_WEEK_MS;
+    }
+
     private static long getTimeSinceTabLastOpenedMs(Tab tab) {
         return System.currentTimeMillis() - CriticalPersistedTabData.from(tab).getTimestampMillis();
     }
diff --git a/chrome/browser/transition_manager/full_browser_transition_manager.h b/chrome/browser/transition_manager/full_browser_transition_manager.h
index d60b2d9..8e10624 100644
--- a/chrome/browser/transition_manager/full_browser_transition_manager.h
+++ b/chrome/browser/transition_manager/full_browser_transition_manager.h
@@ -11,14 +11,10 @@
 #include "base/callback.h"
 #include "base/component_export.h"
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/sequence_checker.h"
 #include "components/keyed_service/core/simple_factory_key.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 class Profile;
 
 // FullBrowserTransitionManager allows SimpleKeyedServices to hook into the
diff --git a/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder_tab_tracker_unittest.cc b/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder_tab_tracker_unittest.cc
index 45aee46..2f10de4 100644
--- a/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder_tab_tracker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder_tab_tracker_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/public/cpp/app_list/app_list_switches.h"
 #include "base/command_line.h"
+#include "base/no_destructor.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/ui/ash/assistant/assistant_browsertest.cc b/chrome/browser/ui/ash/assistant/assistant_browsertest.cc
index b38701fd..09386388 100644
--- a/chrome/browser/ui/ash/assistant/assistant_browsertest.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_browsertest.cc
@@ -5,6 +5,7 @@
 #include "ash/components/audio/cras_audio_handler.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_run_loop_timeout.h"
 #include "base/time/time.h"
 #include "chrome/browser/ui/ash/assistant/assistant_test_mixin.h"
diff --git a/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc b/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
index 7b1a626..c846151 100644
--- a/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
@@ -17,6 +17,7 @@
 #include "base/strings/string_util.h"
 #include "base/test/bind.h"
 #include "base/test/icu_test_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/ash/assistant/assistant_test_mixin.h"
 #include "chrome/browser/ui/ash/assistant/test_support/test_util.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
@@ -189,7 +190,11 @@
 
 class AssistantTimersBrowserTest : public MixinBasedInProcessBrowserTest {
  public:
-  AssistantTimersBrowserTest() = default;
+  AssistantTimersBrowserTest() {
+    // TODO(b/190633242): enable sandbox in browser tests.
+    feature_list_.InitAndDisableFeature(
+        chromeos::assistant::features::kEnableLibAssistantSandbox);
+  }
 
   AssistantTimersBrowserTest(const AssistantTimersBrowserTest&) = delete;
   AssistantTimersBrowserTest& operator=(const AssistantTimersBrowserTest&) =
@@ -205,6 +210,7 @@
   AssistantTestMixin* tester() { return &tester_; }
 
  private:
+  base::test::ScopedFeatureList feature_list_;
   base::test::ScopedRestoreICUDefaultLocale locale_{"en_US"};
   AssistantTestMixin tester_{&mixin_host_, this, embedded_test_server(), kMode,
                              kVersion};
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
index 2feee31..e5a727a 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
@@ -83,22 +83,7 @@
 
   // Returns the number of bytes received for the underlying `download_item_`.
   int64_t GetReceivedBytes() const {
-    int64_t received_bytes = download_item_->GetReceivedBytes();
-
-    if (IsInProgress(download_item_)) {
-      // If the underlying `download_item_` is still in-progress, ensure that
-      // `received_bytes` < `total_bytes`. This may not actually be the case if,
-      // for example, all bytes have been received but the `download_item_` is
-      // still in the process of completing. Failure to account for this
-      // scenario would cause the associated `holding_space_item_` to be marked
-      // complete prematurely and potentially be removed from the model due to
-      // failed backing file validity checks.
-      const absl::optional<int64_t> total_bytes = GetTotalBytes();
-      if (total_bytes.has_value())
-        received_bytes = std::min(received_bytes, total_bytes.value() - 1);
-    }
-
-    return received_bytes;
+    return download_item_->GetReceivedBytes();
   }
 
   // Returns the file path associated with the underlying `download_item_`.
@@ -118,7 +103,8 @@
   HoldingSpaceProgress GetProgress() const {
     if (IsComplete(download_item_))
       return HoldingSpaceProgress();
-    return HoldingSpaceProgress(GetReceivedBytes(), GetTotalBytes());
+    return HoldingSpaceProgress(GetReceivedBytes(), GetTotalBytes(),
+                                /*complete=*/false);
   }
 
   // Returns the number of total bytes for the underlying `download_item`.
diff --git a/chrome/browser/ui/ash/network/network_state_notifier.cc b/chrome/browser/ui/ash/network/network_state_notifier.cc
index 9d22361..6ffa118 100644
--- a/chrome/browser/ui/ash/network/network_state_notifier.cc
+++ b/chrome/browser/ui/ash/network/network_state_notifier.cc
@@ -197,6 +197,29 @@
   RemoveConnectNotification();
 }
 
+void NetworkStateNotifier::NetworkConnectionStateChanged(
+    const chromeos::NetworkState* network) {
+  if (!network->IsConnectedState() ||
+      connect_error_notification_network_guid_.empty() ||
+      connect_error_notification_network_guid_ != network->guid()) {
+    return;
+  }
+  RemoveConnectNotification();
+}
+
+void NetworkStateNotifier::NetworkIdentifierTransitioned(
+    const std::string& old_service_path,
+    const std::string& new_service_path,
+    const std::string& old_guid,
+    const std::string& new_guid) {
+  if (old_guid == new_guid ||
+      old_guid != connect_error_notification_network_guid_) {
+    return;
+  }
+
+  connect_error_notification_network_guid_ = new_guid;
+}
+
 void NetworkStateNotifier::ConnectSucceeded(const std::string& service_path) {
   RemoveConnectNotification();
 }
@@ -426,6 +449,7 @@
 
 void NetworkStateNotifier::RemoveConnectNotification() {
   SystemNotificationHelper::GetInstance()->Close(kNetworkConnectNotificationId);
+  connect_error_notification_network_guid_.clear();
 }
 
 void NetworkStateNotifier::OnConnectErrorGetProperties(
@@ -560,6 +584,7 @@
                                    weak_ptr_factory_.GetWeakPtr(), guid);
   }
 
+  connect_error_notification_network_guid_ = guid;
   ShowErrorNotification(
       NetworkPathId(service_path), kNetworkConnectNotificationId, network_type,
       l10n_util::GetStringUTF16(IDS_NETWORK_CONNECTION_ERROR_TITLE), error_msg,
diff --git a/chrome/browser/ui/ash/network/network_state_notifier.h b/chrome/browser/ui/ash/network/network_state_notifier.h
index 97273ad485..0db251bc 100644
--- a/chrome/browser/ui/ash/network/network_state_notifier.h
+++ b/chrome/browser/ui/ash/network/network_state_notifier.h
@@ -81,6 +81,12 @@
   void ActiveNetworksChanged(
       const std::vector<const NetworkState*>& active_networks) override;
   void NetworkPropertiesUpdated(const NetworkState* network) override;
+  void NetworkConnectionStateChanged(
+      const chromeos::NetworkState* network) override;
+  void NetworkIdentifierTransitioned(const std::string& old_service_path,
+                                     const std::string& new_service_path,
+                                     const std::string& old_guid,
+                                     const std::string& new_guid) override;
 
   void OnConnectErrorGetProperties(
       const std::string& error_name,
@@ -124,6 +130,10 @@
   // Set to the GUID of the active non VPN network if any, otherwise empty.
   std::string active_non_vpn_network_guid_;
 
+  // Set to the GUID of the current network which spawned a connection error
+  // notification if any, otherwise empty.
+  std::string connect_error_notification_network_guid_;
+
   // Tracks GUIDs of activating cellular networks for activation notification.
   std::set<std::string> cellular_activating_guids_;
 
diff --git a/chrome/browser/ui/ash/network/network_state_notifier_unittest.cc b/chrome/browser/ui/ash/network/network_state_notifier_unittest.cc
index 55132f6..dec35d8 100644
--- a/chrome/browser/ui/ash/network/network_state_notifier_unittest.cc
+++ b/chrome/browser/ui/ash/network/network_state_notifier_unittest.cc
@@ -43,6 +43,8 @@
 const char16_t kCellular1NetworkName16[] = u"cellular1";
 const char kTestEsimProfileName[] = "test_profile_name";
 const char16_t kTestEsimProfileName16[] = u"test_profile_name";
+const char kCellularEsimServicePath[] = "/service/cellular_esim1";
+const char kCellularDevicePath[] = "/device/cellular1";
 
 class NetworkConnectTestDelegate : public NetworkConnect::Delegate {
  public:
@@ -140,6 +142,20 @@
             kAddProfileWithService);
     base::RunLoop().RunUntilIdle();
   }
+
+  void SetCellularDeviceLocked(bool is_locked) {
+    ShillDeviceClient::TestInterface* device_test =
+        network_handler_test_helper_->device_test();
+
+    std::string lock_pin = is_locked ? shill::kSIMLockPin : "";
+    base::Value sim_lock_status(base::Value::Type::DICTIONARY);
+    sim_lock_status.SetKey(shill::kSIMLockTypeProperty, base::Value(lock_pin));
+    device_test->SetDeviceProperty(
+        kCellularDevicePath, shill::kSIMLockStatusProperty,
+        std::move(sim_lock_status), /*notify_changed=*/true);
+    base::RunLoop().RunUntilIdle();
+  }
+
   void SetupDefaultShillState() {
     ShillDeviceClient::TestInterface* device_test =
         network_handler_test_helper_->device_test();
@@ -164,7 +180,6 @@
         kWiFi1ServicePath, shill::kPassphraseProperty, base::Value("failure"));
 
     // Set up Cellular device, and add a single locked network.
-    const char kCellularDevicePath[] = "/device/cellular1";
     const char kCellular1ServicePath[] = "/service/cellular1";
     const char kCellular1Iccid[] = "iccid";
     device_test->AddDevice(kCellularDevicePath, shill::kTypeCellular,
@@ -264,6 +279,22 @@
       l10n_util::GetStringFUTF16(
           IDS_NETWORK_CONNECTION_ERROR_MESSAGE, kTestEsimProfileName16,
           l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SIM_CARD_LOCKED)));
+
+  ShillServiceClient::TestInterface* service_test =
+      ShillServiceClient::Get()->GetTestInterface();
+  service_test->SetServiceProperty(
+      kCellularEsimServicePath, shill::kConnectableProperty, base::Value(true));
+
+  // Set device locked status to false, this will allow for network connection
+  // to succeed.
+  SetCellularDeviceLocked(/*is_locked=*/false);
+  NetworkConnect::Get()->ConnectToNetworkId("esim_guidiccid");
+  base::RunLoop().RunUntilIdle();
+
+  // Notification is removed.
+  notification = tester.GetNotification(
+      NetworkStateNotifier::kNetworkConnectNotificationId);
+  EXPECT_FALSE(notification);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc b/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc
index efaf4a8..afbdef485 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc
@@ -92,15 +92,10 @@
  public:
   METADATA_HEADER(SharesheetImagePreview);
   explicit SharesheetImagePreview(size_t file_count) {
-    const int border_radius =
-        views::LayoutProvider::Get()->GetCornerRadiusMetric(
-            views::Emphasis::kMedium);
     SetBackground(views::CreateRoundedRectBackground(
-        kImagePreviewPlaceholderBackgroundColor, border_radius));
-    SetBorder(views::CreateRoundedRectBorder(
-        /* thickness */ 1, border_radius,
-        GetNativeTheme()->GetSystemColor(
-            ui::NativeTheme::kColorId_UnfocusedBorderColor)));
+        kImagePreviewPlaceholderBackgroundColor,
+        views::LayoutProvider::Get()->GetCornerRadiusMetric(
+            views::Emphasis::kMedium)));
     SetLayoutManager(std::make_unique<views::BoxLayout>(
         views::BoxLayout::Orientation::kVertical,
         /* inside_border_insets */ gfx::Insets(),
@@ -185,6 +180,16 @@
       was_pressed_ = true;
   }
 
+  void OnThemeChanged() override {
+    View::OnThemeChanged();
+    SetBorder(views::CreateRoundedRectBorder(
+        /*thickness=*/1,
+        views::LayoutProvider::Get()->GetCornerRadiusMetric(
+            views::Emphasis::kMedium),
+        GetNativeTheme()->GetSystemColor(
+            ui::NativeTheme::kColorId_UnfocusedBorderColor)));
+  }
+
   void AddRowToImageContainerView() {
     auto* row = AddChildView(std::make_unique<views::View>());
     row->SetLayoutManager(std::make_unique<views::BoxLayout>(
diff --git a/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.cc
index 6f31f5f4..f5fa5b9c 100644
--- a/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.cc
@@ -170,6 +170,28 @@
   observed_windows_.AddObservation(window);
   if (arc_tracker_)
     arc_tracker_->AddCandidateWindow(window);
+
+  // When the visibility of the window changes and if it is not already on a
+  // shelf then it is added to a shelf by `ASAWSC::OnWindowVisibilityChanged()`
+  // but when the window is created as a minimized window there is no change in
+  // visible state and it is not added to the shelf. Hence, when a widget has a
+  // `initial_show_state_` as ui::SHOW_STATE_MINIMIZED, it should add itself to
+  // a shelf during initialization. The below code is applicable only for Lacros
+  // browser app.
+  auto shelf_id = GetShelfId(window);
+  if (!shelf_id.IsNull() &&
+      GetAppType(shelf_id.app_id) == apps::mojom::AppType::kStandaloneBrowser &&
+      widget->IsMinimized()) {
+    // Update |state|. The app must be started, and running state. If visible,
+    // set it as |kVisible|, otherwise, clear the visible bit.
+    apps::InstanceState state =
+        app_service_instance_helper_->CalculateVisibilityState(
+            window, /*visible=*/false);
+    app_service_instance_helper_->OnInstances(GetAppId(shelf_id.app_id), window,
+                                              shelf_id.launch_id, state);
+
+    RegisterWindow(window, shelf_id);
+  }
 }
 
 void AppServiceAppWindowShelfController::OnWindowPropertyChanged(
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.h b/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
index 44b7f8e0..17669a84 100644
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/scoped_observation.h"
 #include "extensions/browser/api/automation_internal/automation_event_router.h"
 #include "ui/accessibility/ax_action_handler.h"
@@ -21,11 +22,6 @@
 #include "ui/views/accessibility/ax_event_observer.h"
 #include "ui/views/accessibility/ax_tree_source_views.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 namespace extensions {
 class AutomationEventRouterInterface;
 }  // namespace extensions
diff --git a/chrome/browser/ui/search/ntp_user_data_logger.cc b/chrome/browser/ui/search/ntp_user_data_logger.cc
index 72f8cda..80eb35c 100644
--- a/chrome/browser/ui/search/ntp_user_data_logger.cc
+++ b/chrome/browser/ui/search/ntp_user_data_logger.cc
@@ -461,16 +461,12 @@
     return;
   }
 
-  bool has_server_side_suggestions = false;
   int tiles_count = 0;
   for (const absl::optional<ntp_tiles::NTPTileImpression>& impression :
        logged_impressions_) {
     if (!impression.has_value()) {
       break;
     }
-    if (impression->source == ntp_tiles::TileSource::SUGGESTIONS_SERVICE) {
-      has_server_side_suggestions = true;
-    }
     ntp_tiles::metrics::RecordTileImpression(*impression);
     ++tiles_count;
   }
@@ -480,13 +476,7 @@
            << "number of tiles: " << tiles_count;
 
   UMA_HISTOGRAM_LOAD_TIME("NewTabPage.LoadTime", load_time);
-
-  // Split between ML (aka SuggestionsService) and MV (aka TopSites).
-  if (has_server_side_suggestions) {
-    UMA_HISTOGRAM_LOAD_TIME("NewTabPage.LoadTime.MostLikely", load_time);
-  } else {
-    UMA_HISTOGRAM_LOAD_TIME("NewTabPage.LoadTime.MostVisited", load_time);
-  }
+  UMA_HISTOGRAM_LOAD_TIME("NewTabPage.LoadTime.MostVisited", load_time);
 
   // Note: This could be inaccurate if the default search engine was changed
   // since the page load started. That's unlikely enough to not warrant special
diff --git a/chrome/browser/ui/search/ntp_user_data_logger_unittest.cc b/chrome/browser/ui/search/ntp_user_data_logger_unittest.cc
index b242a9d4..4e613e9 100644
--- a/chrome/browser/ui/search/ntp_user_data_logger_unittest.cc
+++ b/chrome/browser/ui/search/ntp_user_data_logger_unittest.cc
@@ -101,7 +101,7 @@
 
   for (int i = 0; i < ntp_tiles::kMaxNumTiles; ++i) {
     logger.LogMostVisitedImpression(MakeNTPTileImpression(
-        i, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::UNKNOWN,
+        i, TileSource::TOP_SITES, TileTitleSource::UNKNOWN,
         TileVisualType::ICON_REAL));
   }
   logger.LogMostVisitedLoaded(delta, /*using_most_visited=*/true,
@@ -121,9 +121,6 @@
 
   base::HistogramTester histogram_tester;
 
-  logger.LogMostVisitedImpression(MakeNTPTileImpression(
-      0, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::INFERRED,
-      TileVisualType::ICON_REAL));
   logger.LogMostVisitedImpression(
       MakeNTPTileImpression(1, TileSource::TOP_SITES, TileTitleSource::INFERRED,
                             TileVisualType::ICON_DEFAULT));
@@ -134,22 +131,15 @@
       histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression"),
       IsEmpty());
   EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.server"),
-      IsEmpty());
-  EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.client"),
       IsEmpty());
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType"), IsEmpty());
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.client"),
               IsEmpty());
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.server"),
-              IsEmpty());
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTitle"),
               IsEmpty());
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTitle.client"),
               IsEmpty());
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTitle.server"),
-              IsEmpty());
 }
 
 TEST_F(NTPUserDataLoggerTest, ShouldRecordImpressions) {
@@ -158,18 +148,18 @@
   base::HistogramTester histogram_tester;
 
   // Impressions increment the associated bins.
-  logger.LogMostVisitedImpression(MakeNTPTileImpression(
-      0, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::INFERRED,
-      TileVisualType::ICON_REAL));
-  logger.LogMostVisitedImpression(MakeNTPTileImpression(
-      1, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::INFERRED,
-      TileVisualType::ICON_DEFAULT));
-  logger.LogMostVisitedImpression(MakeNTPTileImpression(
-      2, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::INFERRED,
-      TileVisualType::ICON_REAL));
-  logger.LogMostVisitedImpression(MakeNTPTileImpression(
-      3, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::INFERRED,
-      TileVisualType::ICON_REAL));
+  logger.LogMostVisitedImpression(
+      MakeNTPTileImpression(0, TileSource::TOP_SITES, TileTitleSource::INFERRED,
+                            TileVisualType::ICON_REAL));
+  logger.LogMostVisitedImpression(
+      MakeNTPTileImpression(1, TileSource::TOP_SITES, TileTitleSource::INFERRED,
+                            TileVisualType::ICON_DEFAULT));
+  logger.LogMostVisitedImpression(
+      MakeNTPTileImpression(2, TileSource::TOP_SITES, TileTitleSource::INFERRED,
+                            TileVisualType::ICON_REAL));
+  logger.LogMostVisitedImpression(
+      MakeNTPTileImpression(3, TileSource::TOP_SITES, TileTitleSource::INFERRED,
+                            TileVisualType::ICON_REAL));
   logger.LogMostVisitedImpression(MakeNTPTileImpression(
       4, TileSource::TOP_SITES, TileTitleSource::TITLE_TAG,
       TileVisualType::ICON_REAL));
@@ -192,11 +182,9 @@
       ElementsAre(Bucket(0, 1), Bucket(1, 1), Bucket(2, 1), Bucket(3, 1),
                   Bucket(4, 1), Bucket(5, 1), Bucket(6, 1), Bucket(7, 1)));
   EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.server"),
-      ElementsAre(Bucket(0, 1), Bucket(1, 1), Bucket(2, 1), Bucket(3, 1)));
-  EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.client"),
-      ElementsAre(Bucket(4, 1), Bucket(5, 1)));
+      ElementsAre(Bucket(0, 1), Bucket(1, 1), Bucket(2, 1), Bucket(3, 1),
+                  Bucket(4, 1), Bucket(5, 1)));
   EXPECT_THAT(histogram_tester.GetAllSamples(
                   "NewTabPage.SuggestionsImpression.popular_fetched"),
               ElementsAre(Bucket(6, 1)));
@@ -206,11 +194,9 @@
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType"),
               ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 7),
                           Bucket(ntp_tiles::TileVisualType::ICON_DEFAULT, 1)));
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.server"),
-              ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 3),
-                          Bucket(ntp_tiles::TileVisualType::ICON_DEFAULT, 1)));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.client"),
-              ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 2)));
+              ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 5),
+                          Bucket(ntp_tiles::TileVisualType::ICON_DEFAULT, 1)));
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.TileType.popular_fetched"),
       ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 1)));
@@ -222,11 +208,10 @@
                           Bucket(kMetaTagTitleSource, 1),
                           Bucket(kTitleTagTitleSource, 2),
                           Bucket(kInferredTitleSource, 4)));
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTitle.server"),
-              ElementsAre(Bucket(kInferredTitleSource, 4)));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTitle.client"),
               ElementsAre(Bucket(kManifestTitleSource, 1),
-                          Bucket(kTitleTagTitleSource, 1)));
+                          Bucket(kTitleTagTitleSource, 1),
+                          Bucket(kInferredTitleSource, 4)));
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.TileTitle.popular_fetched"),
       ElementsAre(Bucket(kTitleTagTitleSource, 1)));
@@ -241,29 +226,29 @@
   base::HistogramTester histogram_tester;
 
   // Impressions increment the associated bins.
-  logger.LogMostVisitedImpression(MakeNTPTileImpression(
-      0, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::INFERRED,
-      TileVisualType::ICON_REAL));
-  logger.LogMostVisitedImpression(MakeNTPTileImpression(
-      1, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::INFERRED,
-      TileVisualType::ICON_DEFAULT));
-  logger.LogMostVisitedImpression(MakeNTPTileImpression(
-      2, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::INFERRED,
-      TileVisualType::ICON_REAL));
-  logger.LogMostVisitedImpression(MakeNTPTileImpression(
-      3, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::INFERRED,
-      TileVisualType::ICON_REAL));
+  logger.LogMostVisitedImpression(
+      MakeNTPTileImpression(0, TileSource::TOP_SITES, TileTitleSource::INFERRED,
+                            TileVisualType::ICON_REAL));
+  logger.LogMostVisitedImpression(
+      MakeNTPTileImpression(1, TileSource::TOP_SITES, TileTitleSource::INFERRED,
+                            TileVisualType::ICON_DEFAULT));
+  logger.LogMostVisitedImpression(
+      MakeNTPTileImpression(2, TileSource::TOP_SITES, TileTitleSource::INFERRED,
+                            TileVisualType::ICON_REAL));
+  logger.LogMostVisitedImpression(
+      MakeNTPTileImpression(3, TileSource::TOP_SITES, TileTitleSource::INFERRED,
+                            TileVisualType::ICON_REAL));
 
   // Repeated impressions for the same bins are ignored.
-  logger.LogMostVisitedImpression(MakeNTPTileImpression(
-      0, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::UNKNOWN,
-      TileVisualType::ICON_DEFAULT));
+  logger.LogMostVisitedImpression(
+      MakeNTPTileImpression(0, TileSource::TOP_SITES, TileTitleSource::UNKNOWN,
+                            TileVisualType::ICON_DEFAULT));
   logger.LogMostVisitedImpression(
       MakeNTPTileImpression(1, TileSource::TOP_SITES, TileTitleSource::UNKNOWN,
                             TileVisualType::ICON_DEFAULT));
-  logger.LogMostVisitedImpression(MakeNTPTileImpression(
-      2, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::UNKNOWN,
-      TileVisualType::ICON_REAL));
+  logger.LogMostVisitedImpression(
+      MakeNTPTileImpression(2, TileSource::TOP_SITES, TileTitleSource::UNKNOWN,
+                            TileVisualType::ICON_REAL));
   logger.LogMostVisitedImpression(
       MakeNTPTileImpression(3, TileSource::TOP_SITES, TileTitleSource::UNKNOWN,
                             TileVisualType::ICON_REAL));
@@ -276,25 +261,18 @@
       histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression"),
       ElementsAre(Bucket(0, 1), Bucket(1, 1), Bucket(2, 1), Bucket(3, 1)));
   EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.server"),
-      ElementsAre(Bucket(0, 1), Bucket(1, 1), Bucket(2, 1), Bucket(3, 1)));
-  EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.client"),
-      IsEmpty());
+      ElementsAre(Bucket(0, 1), Bucket(1, 1), Bucket(2, 1), Bucket(3, 1)));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType"),
               ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 3),
                           Bucket(ntp_tiles::TileVisualType::ICON_DEFAULT, 1)));
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.server"),
+  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.client"),
               ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 3),
                           Bucket(ntp_tiles::TileVisualType::ICON_DEFAULT, 1)));
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.client"),
-              IsEmpty());
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTitle"),
               ElementsAre(Bucket(kInferredTitleSource, 4)));
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTitle.server"),
-              ElementsAre(Bucket(kInferredTitleSource, 4)));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTitle.client"),
-              IsEmpty());
+              ElementsAre(Bucket(kInferredTitleSource, 4)));
 }
 
 TEST_F(NTPUserDataLoggerTest, ShouldNotRecordImpressionsForBinsBeyondMax) {
@@ -305,7 +283,7 @@
   // Impressions increment the associated bins.
   for (int bin = 0; bin < ntp_tiles::kMaxNumTiles; bin++) {
     logger.LogMostVisitedImpression(MakeNTPTileImpression(
-        bin, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::INFERRED,
+        bin, TileSource::TOP_SITES, TileTitleSource::INFERRED,
         TileVisualType::ICON_REAL));
   }
 
@@ -327,27 +305,20 @@
       histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression"),
       ContainerEq(expectedImpressions));
   EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.server"),
-      ContainerEq(expectedImpressions));
-  EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.client"),
-      IsEmpty());
+      ContainerEq(expectedImpressions));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType"),
               ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL,
                                  ntp_tiles::kMaxNumTiles)));
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.server"),
+  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.client"),
               ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL,
                                  ntp_tiles::kMaxNumTiles)));
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.client"),
-              IsEmpty());
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.TileTitle"),
       ElementsAre(Bucket(kInferredTitleSource, ntp_tiles::kMaxNumTiles)));
   EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.TileTitle.server"),
+      histogram_tester.GetAllSamples("NewTabPage.TileTitle.client"),
       ElementsAre(Bucket(kInferredTitleSource, ntp_tiles::kMaxNumTiles)));
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTitle.client"),
-              IsEmpty());
 }
 
 TEST_F(NTPUserDataLoggerTest, ShouldRecordNavigations) {
@@ -357,49 +328,39 @@
     base::HistogramTester histogram_tester;
 
     logger.LogMostVisitedNavigation(MakeNTPTileImpression(
-        0, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::UNKNOWN,
+        0, TileSource::TOP_SITES, TileTitleSource::UNKNOWN,
         TileVisualType::ICON_REAL));
 
     EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited"),
                 ElementsAre(Bucket(0, 1)));
-    EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.server"),
-                ElementsAre(Bucket(0, 1)));
     EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.client"),
-                IsEmpty());
+                ElementsAre(Bucket(0, 1)));
 
     EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked"),
                 ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 1)));
     EXPECT_THAT(
-        histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked.server"),
-        ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 1)));
-    EXPECT_THAT(
         histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked.client"),
-        IsEmpty());
+        ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 1)));
   }
 
   {
     base::HistogramTester histogram_tester;
 
     logger.LogMostVisitedNavigation(MakeNTPTileImpression(
-        1, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::UNKNOWN,
+        1, TileSource::TOP_SITES, TileTitleSource::UNKNOWN,
         TileVisualType::ICON_DEFAULT));
 
     EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited"),
                 ElementsAre(Bucket(1, 1)));
-    EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.server"),
-                ElementsAre(Bucket(1, 1)));
     EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.client"),
-                IsEmpty());
+                ElementsAre(Bucket(1, 1)));
 
     EXPECT_THAT(
         histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked"),
         ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_DEFAULT, 1)));
     EXPECT_THAT(
-        histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked.server"),
-        ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_DEFAULT, 1)));
-    EXPECT_THAT(
         histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked.client"),
-        IsEmpty());
+        ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_DEFAULT, 1)));
   }
 
   {
@@ -411,8 +372,6 @@
 
     EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited"),
                 ElementsAre(Bucket(2, 1)));
-    EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.server"),
-                IsEmpty());
     EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.client"),
                 ElementsAre(Bucket(2, 1)));
 
@@ -420,9 +379,6 @@
         histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked"),
         ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_DEFAULT, 1)));
     EXPECT_THAT(
-        histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked.server"),
-        IsEmpty());
-    EXPECT_THAT(
         histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked.client"),
         ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_DEFAULT, 1)));
   }
@@ -436,8 +392,6 @@
 
     EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited"),
                 ElementsAre(Bucket(3, 1)));
-    EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.server"),
-                IsEmpty());
     EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.client"),
                 IsEmpty());
     EXPECT_THAT(histogram_tester.GetAllSamples(
@@ -447,9 +401,6 @@
     EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked"),
                 ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 1)));
     EXPECT_THAT(
-        histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked.server"),
-        IsEmpty());
-    EXPECT_THAT(
         histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked.client"),
         IsEmpty());
     EXPECT_THAT(histogram_tester.GetAllSamples(
@@ -459,9 +410,6 @@
     EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTitleClicked"),
                 ElementsAre(Bucket(kMetaTagTitleSource, 1)));
     EXPECT_THAT(
-        histogram_tester.GetAllSamples("NewTabPage.TileTitleClicked.server"),
-        IsEmpty());
-    EXPECT_THAT(
         histogram_tester.GetAllSamples("NewTabPage.TileTitleClicked.client"),
         IsEmpty());
     EXPECT_THAT(histogram_tester.GetAllSamples(
@@ -474,13 +422,13 @@
 
     // Navigations always increase.
     logger.LogMostVisitedNavigation(MakeNTPTileImpression(
-        0, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::UNKNOWN,
+        0, TileSource::TOP_SITES, TileTitleSource::UNKNOWN,
         TileVisualType::ICON_REAL));
     logger.LogMostVisitedNavigation(MakeNTPTileImpression(
         1, TileSource::TOP_SITES, TileTitleSource::TITLE_TAG,
         TileVisualType::ICON_REAL));
     logger.LogMostVisitedNavigation(MakeNTPTileImpression(
-        2, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::UNKNOWN,
+        2, TileSource::TOP_SITES, TileTitleSource::UNKNOWN,
         TileVisualType::ICON_REAL));
     logger.LogMostVisitedNavigation(
         MakeNTPTileImpression(3, TileSource::POPULAR, TileTitleSource::MANIFEST,
@@ -489,10 +437,8 @@
     EXPECT_THAT(
         histogram_tester.GetAllSamples("NewTabPage.MostVisited"),
         ElementsAre(Bucket(0, 1), Bucket(1, 1), Bucket(2, 1), Bucket(3, 1)));
-    EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.server"),
-                ElementsAre(Bucket(0, 1), Bucket(2, 1)));
     EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.client"),
-                ElementsAre(Bucket(1, 1)));
+                ElementsAre(Bucket(0, 1), Bucket(1, 1), Bucket(2, 1)));
     EXPECT_THAT(histogram_tester.GetAllSamples(
                     "NewTabPage.MostVisited.popular_fetched"),
                 ElementsAre(Bucket(3, 1)));
@@ -500,11 +446,8 @@
     EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked"),
                 ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 4)));
     EXPECT_THAT(
-        histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked.server"),
-        ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 2)));
-    EXPECT_THAT(
         histogram_tester.GetAllSamples("NewTabPage.TileTypeClicked.client"),
-        ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 1)));
+        ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 3)));
     EXPECT_THAT(histogram_tester.GetAllSamples(
                     "NewTabPage.TileTypeClicked.popular_fetched"),
                 ElementsAre(Bucket(ntp_tiles::TileVisualType::ICON_REAL, 1)));
@@ -514,11 +457,9 @@
                             Bucket(kManifestTitleSource, 1),
                             Bucket(kTitleTagTitleSource, 1)));
     EXPECT_THAT(
-        histogram_tester.GetAllSamples("NewTabPage.TileTitleClicked.server"),
-        ElementsAre(Bucket(kUnknownTitleSource, 2)));
-    EXPECT_THAT(
         histogram_tester.GetAllSamples("NewTabPage.TileTitleClicked.client"),
-        ElementsAre(Bucket(kTitleTagTitleSource, 1)));
+        ElementsAre(Bucket(kUnknownTitleSource, 2),
+                    Bucket(kTitleTagTitleSource, 1)));
     EXPECT_THAT(histogram_tester.GetAllSamples(
                     "NewTabPage.TileTitleClicked.popular_fetched"),
                 ElementsAre(Bucket(kManifestTitleSource, 1)));
@@ -560,33 +501,6 @@
                                          delta_tiles_loaded, 1);
 }
 
-TEST_F(NTPUserDataLoggerTest, ShouldRecordMostLikelyLoadTime) {
-  base::HistogramTester histogram_tester;
-
-  TestNTPUserDataLogger logger(GURL("chrome://newtab/"));
-
-  // Log a SUGGESTIONS_SERVICE impression, so the times will end up in
-  // .MostLikely.
-  logger.LogMostVisitedImpression(MakeNTPTileImpression(
-      0, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::UNKNOWN,
-      TileVisualType::ICON_REAL));
-
-  base::TimeDelta delta_tiles_loaded = base::TimeDelta::FromMilliseconds(500);
-  logger.LogMostVisitedLoaded(delta_tiles_loaded, /*using_most_visited=*/true,
-                              /*is_visible=*/true);
-
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.LoadTime"), SizeIs(1));
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.LoadTime.MostVisited"),
-              IsEmpty());
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.LoadTime.MostLikely"),
-              SizeIs(1));
-
-  histogram_tester.ExpectTimeBucketCount("NewTabPage.LoadTime",
-                                         delta_tiles_loaded, 1);
-  histogram_tester.ExpectTimeBucketCount("NewTabPage.LoadTime.MostLikely",
-                                         delta_tiles_loaded, 1);
-}
-
 TEST_F(NTPUserDataLoggerTest, ShouldRecordLoadTimeRemoteNTPOther) {
   base::HistogramTester histogram_tester;
 
@@ -628,7 +542,7 @@
   constexpr base::TimeDelta kBucketTolerance = base::TimeDelta::FromSeconds(20);
 
   logger.LogMostVisitedImpression(ntp_tiles::NTPTileImpression(
-      0, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::UNKNOWN,
+      0, TileSource::TOP_SITES, TileTitleSource::UNKNOWN,
       TileVisualType::ICON_REAL, favicon_base::IconType::kInvalid,
       base::Time::Now() - kSuggestionAge, GURL()));
 
@@ -636,7 +550,7 @@
                               /*is_visible=*/true);
 
   EXPECT_THAT(histogram_tester.GetAllSamples(
-                  "NewTabPage.SuggestionsImpressionAge.server"),
+                  "NewTabPage.SuggestionsImpressionAge.client"),
               ElementsAre(IsBucketBetween(
                   (kSuggestionAge - kBucketTolerance).InSeconds(),
                   (kSuggestionAge + kBucketTolerance).InSeconds(),
diff --git a/chrome/browser/ui/search/search_ipc_router_unittest.cc b/chrome/browser/ui/search/search_ipc_router_unittest.cc
index 19718a2a..f8f1428 100644
--- a/chrome/browser/ui/search/search_ipc_router_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_unittest.cc
@@ -347,9 +347,9 @@
 
 TEST_F(SearchIPCRouterTest, ProcessLogMostVisitedImpressionMsg) {
   const ntp_tiles::NTPTileImpression impression(
-      3, ntp_tiles::TileSource::SUGGESTIONS_SERVICE,
-      ntp_tiles::TileTitleSource::UNKNOWN, ntp_tiles::TileVisualType::ICON_REAL,
-      favicon_base::IconType::kInvalid, base::Time(), GURL());
+      3, ntp_tiles::TileSource::TOP_SITES, ntp_tiles::TileTitleSource::UNKNOWN,
+      ntp_tiles::TileVisualType::ICON_REAL, favicon_base::IconType::kInvalid,
+      base::Time(), GURL());
   NavigateAndCommitActiveTab(GURL("chrome-search://foo/baz"));
   SetupMockDelegateAndPolicy();
   MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
@@ -364,9 +364,9 @@
 
 TEST_F(SearchIPCRouterTest, ProcessLogMostVisitedNavigationMsg) {
   const ntp_tiles::NTPTileImpression impression(
-      3, ntp_tiles::TileSource::SUGGESTIONS_SERVICE,
-      ntp_tiles::TileTitleSource::UNKNOWN, ntp_tiles::TileVisualType::ICON_REAL,
-      favicon_base::IconType::kInvalid, base::Time(), GURL());
+      3, ntp_tiles::TileSource::TOP_SITES, ntp_tiles::TileTitleSource::UNKNOWN,
+      ntp_tiles::TileVisualType::ICON_REAL, favicon_base::IconType::kInvalid,
+      base::Time(), GURL());
   NavigateAndCommitActiveTab(GURL("chrome-search://foo/baz"));
   SetupMockDelegateAndPolicy();
   MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
diff --git a/chrome/browser/ui/tab_contents/tab_contents_iterator.cc b/chrome/browser/ui/tab_contents/tab_contents_iterator.cc
index 6132096..d1857216 100644
--- a/chrome/browser/ui/tab_contents/tab_contents_iterator.cc
+++ b/chrome/browser/ui/tab_contents/tab_contents_iterator.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
 
 #include "base/check.h"
-#include "base/no_destructor.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -54,6 +53,6 @@
 }
 
 const AllTabContentsesList& AllTabContentses() {
-  static const base::NoDestructor<AllTabContentsesList> all_tabs;
-  return *all_tabs;
+  static const AllTabContentsesList all_tabs;
+  return all_tabs;
 }
diff --git a/chrome/browser/ui/thumbnails/thumbnail_stats_tracker.h b/chrome/browser/ui/thumbnails/thumbnail_stats_tracker.h
index 044a3b0..8ec6cce 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_stats_tracker.h
+++ b/chrome/browser/ui/thumbnails/thumbnail_stats_tracker.h
@@ -7,14 +7,10 @@
 
 #include <set>
 
+#include "base/no_destructor.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 class ThumbnailImage;
 
 // Records memory metrics across all thumbnails in a browser process.
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 0b4ea70..0bbc1e82 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -128,6 +128,11 @@
 const base::Feature kTabGroupsNewBadgePromo{"TabGroupsNewBadgePromo",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables users to explicitly save and recall tab groups.
+// https://crbug.com/1223929
+const base::Feature kTabGroupsSave{"TabGroupsSave",
+                                   base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables preview images in tab-hover cards.
 // https://crbug.com/928954
 const base::Feature kTabHoverCardImages{"TabHoverCardImages",
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index ec5881f7..a5562fc 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -82,6 +82,8 @@
 
 extern const base::Feature kTabGroupsNewBadgePromo;
 
+extern const base::Feature kTabGroupsSave;
+
 extern const base::Feature kTabHoverCardImages;
 extern const char kTabHoverCardImagesNotReadyDelayParameterName[];
 extern const char kTabHoverCardImagesLoadingDelayParameterName[];
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc
index 83255c7..46a0c2e5 100644
--- a/chrome/browser/ui/views/download/download_item_view.cc
+++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -139,6 +139,9 @@
 // The analysis service tag for data loss prevention.
 const char kDlpTag[] = "dlp";
 
+// The analysis service tag for malware.
+const char kMalwareTag[] = "malware";
+
 // A stub subclass of Button that has no visuals.
 class TransparentButton : public views::Button {
  public:
@@ -820,16 +823,26 @@
     prompt_to_scan =
         danger_type == download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING;
 
-    prompt_to_review =
-        (danger_type ==
-             download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING ||
-         danger_type ==
-             download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK) &&
-        enterprise_connectors::ConnectorsServiceFactory::GetForBrowserContext(
-            model_->profile())
-            ->HasCustomInfoToDisplay(
-                enterprise_connectors::AnalysisConnector::FILE_DOWNLOADED,
-                kDlpTag);
+    if (danger_type ==
+            download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING ||
+        danger_type == download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK) {
+      prompt_to_review =
+          enterprise_connectors::ConnectorsServiceFactory::GetForBrowserContext(
+              model_->profile())
+              ->HasCustomInfoToDisplay(
+                  enterprise_connectors::AnalysisConnector::FILE_DOWNLOADED,
+                  kDlpTag);
+    } else if (danger_type == download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
+               danger_type == download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
+               danger_type ==
+                   download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT) {
+      prompt_to_review =
+          enterprise_connectors::ConnectorsServiceFactory::GetForBrowserContext(
+              model_->profile())
+              ->HasCustomInfoToDisplay(
+                  enterprise_connectors::AnalysisConnector::FILE_DOWNLOADED,
+                  kMalwareTag);
+    }
 
     prompt_to_discard =
         !prompt_to_review && !prompt_to_scan &&
@@ -1243,6 +1256,14 @@
         WARNING;
   }
 
+  const char* tag =
+      (danger_type ==
+                   download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING ||
+               danger_type ==
+                   download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK
+           ? kDlpTag
+           : kMalwareTag);
+
   auto* connectors_service =
       enterprise_connectors::ConnectorsServiceFactory::GetForBrowserContext(
           model_->profile());
@@ -1251,14 +1272,12 @@
   std::u16string custom_message =
       connectors_service
           ->GetCustomMessage(
-              enterprise_connectors::AnalysisConnector::FILE_DOWNLOADED,
-              kDlpTag)
+              enterprise_connectors::AnalysisConnector::FILE_DOWNLOADED, tag)
           .value_or(u"");
   GURL learn_more_url =
       connectors_service
           ->GetLearnMoreUrl(
-              enterprise_connectors::AnalysisConnector::FILE_DOWNLOADED,
-              kDlpTag)
+              enterprise_connectors::AnalysisConnector::FILE_DOWNLOADED, tag)
           .value_or(GURL());
 
   // This dialog opens itself, and is thereafter owned by constrained window
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc
index f82e4e3..18897624 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc
@@ -137,11 +137,13 @@
 }
 
 void StartOverview() {
-  ash::Shell::Get()->overview_controller()->StartOverview();
+  ash::Shell::Get()->overview_controller()->StartOverview(
+      ash::OverviewStartAction::kTests);
 }
 
 void EndOverview() {
-  ash::Shell::Get()->overview_controller()->EndOverview();
+  ash::Shell::Get()->overview_controller()->EndOverview(
+      ash::OverviewEndAction::kTests);
 }
 
 bool IsShelfVisible() {
diff --git a/chrome/browser/ui/views/frame/webui_tab_strip_field_trial.cc b/chrome/browser/ui/views/frame/webui_tab_strip_field_trial.cc
index d9143d9..a6045dc 100644
--- a/chrome/browser/ui/views/frame/webui_tab_strip_field_trial.cc
+++ b/chrome/browser/ui/views/frame/webui_tab_strip_field_trial.cc
@@ -6,7 +6,6 @@
 
 #include "base/feature_list.h"
 #include "base/logging.h"
-#include "base/no_destructor.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
@@ -51,7 +50,7 @@
 
 // static
 void WebUITabStripFieldTrial::RegisterFieldTrialIfNecessary() {
-  static base::NoDestructor<WebUITabStripFieldTrial> instance;
+  static WebUITabStripFieldTrial instance;
 }
 
 WebUITabStripFieldTrial::WebUITabStripFieldTrial() {
diff --git a/chrome/browser/ui/views/frame/webui_tab_strip_field_trial.h b/chrome/browser/ui/views/frame/webui_tab_strip_field_trial.h
index 7342d59..1884c953 100644
--- a/chrome/browser/ui/views/frame/webui_tab_strip_field_trial.h
+++ b/chrome/browser/ui/views/frame/webui_tab_strip_field_trial.h
@@ -5,10 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_FRAME_WEBUI_TAB_STRIP_FIELD_TRIAL_H_
 #define CHROME_BROWSER_UI_VIEWS_FRAME_WEBUI_TAB_STRIP_FIELD_TRIAL_H_
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}
+#include "base/no_destructor.h"
 
 // Manages a synthetic field trial for the WebUI tab strip. The feature
 // flag itself is controlled by an external field trial. This synthetic
diff --git a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
index 857126bd..f21ea451 100644
--- a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
+++ b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
@@ -263,7 +263,6 @@
     // "chrome://signin-dice-web-intercept",
     "chrome://signin-internals",
     "chrome://site-engagement",
-    "chrome://suggestions",
     // TODO(crbug.com/1099564): Navigating to chrome://sync-confirmation and
     // quickly navigating away cause DCHECK failure.
     // "chrome://sync-confirmation",
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 c47da6f6..c466e88 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/media/media_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/search/suggestions/suggestions_ui.h"
 #include "chrome/browser/ui/chrome_select_file_policy.h"
 #include "chrome/browser/ui/webui/about_ui.h"
 #include "chrome/browser/ui/webui/autofill_and_password_manager_internals/autofill_internals_ui.h"
@@ -630,8 +629,6 @@
     return &NewWebUI<safe_browsing::SafeBrowsingUI>;
   if (url.host_piece() == chrome::kChromeUISignInInternalsHost)
     return &NewWebUI<SignInInternalsUI>;
-  if (url.host_piece() == chrome::kChromeUISuggestionsHost)
-    return &NewWebUI<suggestions::SuggestionsUI>;
   if (url.host_piece() == chrome::kChromeUISupervisedUserPassphrasePageHost)
     return &NewWebUI<ConstrainedWebDialogUI>;
   if (url.host_piece() == chrome::kChromeUISyncInternalsHost)
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_metrics_recorder.cc b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_metrics_recorder.cc
index d9412ba..13c687f 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_metrics_recorder.cc
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_metrics_recorder.cc
@@ -12,8 +12,8 @@
 
 // static
 AddSupervisionMetricsRecorder* AddSupervisionMetricsRecorder::GetInstance() {
-  static base::NoDestructor<AddSupervisionMetricsRecorder> instance_;
-  return instance_.get();
+  static AddSupervisionMetricsRecorder instance_;
+  return &instance_;
 }
 
 void AddSupervisionMetricsRecorder::RecordAddSupervisionEnrollment(
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_metrics_recorder.h b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_metrics_recorder.h
index 540c314..1e20c1e 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_metrics_recorder.h
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_metrics_recorder.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_UI_WEBUI_CHROMEOS_ADD_SUPERVISION_ADD_SUPERVISION_METRICS_RECORDER_H_
 
 #include "base/macros.h"
-#include "base/no_destructor.h"
 #include "base/time/time.h"
 
 namespace base {
@@ -47,8 +46,6 @@
   void SetClockForTesting(const base::TickClock* tick_clock);
 
  private:
-  friend class base::NoDestructor<AddSupervisionMetricsRecorder>;
-
   AddSupervisionMetricsRecorder();
 
   // Records UMA metric of how long the user spends in the Add Supervision
diff --git a/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc b/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
index d23b3545..1ad3803 100644
--- a/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
+++ b/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/history/top_sites_factory.h"
 #include "chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search/suggestions/suggestions_service_factory.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/common/url_constants.h"
 #include "components/grit/dev_ui_components_resources.h"
@@ -79,7 +78,6 @@
     ntp_tiles::TileSource source) {
   switch (source) {
     case ntp_tiles::TileSource::TOP_SITES:
-    case ntp_tiles::TileSource::SUGGESTIONS_SERVICE:
     case ntp_tiles::TileSource::ALLOWLIST:
     case ntp_tiles::TileSource::HOMEPAGE:
       return true;
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
index 8cae117..b77f5ff 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
@@ -245,7 +245,7 @@
                        std::unique_ptr<base::ListValue>* printers_out,
                        const base::ListValue& printers) {
   ++(*call_count);
-  printers_out->reset(printers.DeepCopy());
+  *printers_out = printers.CreateDeepCopy();
 }
 
 // Used as a callback to StartGetPrinters in tests.
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos_unittest.cc b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos_unittest.cc
index 1dd8c674..dfdd73a 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos_unittest.cc
@@ -32,7 +32,7 @@
                        std::unique_ptr<base::ListValue>& printers_out,
                        const base::ListValue& printers) {
   ++call_count;
-  printers_out.reset(printers.DeepCopy());
+  printers_out = printers.CreateDeepCopy();
 }
 
 // Used as a callback to `StartGetPrinters` in tests.
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc b/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc
index f7f5904..ce2753e 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc
@@ -43,7 +43,7 @@
                        std::unique_ptr<base::ListValue>& printers_out,
                        const base::ListValue& printers) {
   ++call_count;
-  printers_out.reset(printers.DeepCopy());
+  printers_out = printers.CreateDeepCopy();
 }
 
 // Used as a callback to `StartGetPrinters` in tests.
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h
index 99062fa..b4cc9e73 100644
--- a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h
+++ b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h
@@ -7,6 +7,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/upgrade_detector/build_state_observer.h"
@@ -17,8 +18,6 @@
 class PrefRegistrySimple;
 namespace base {
 class Clock;
-template <typename T>
-class NoDestructor;
 class TickClock;
 }  // namespace base
 
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_impl.h b/chrome/browser/upgrade_detector/upgrade_detector_impl.h
index 25ad785..73a7934 100644
--- a/chrome/browser/upgrade_detector/upgrade_detector_impl.h
+++ b/chrome/browser/upgrade_detector/upgrade_detector_impl.h
@@ -8,6 +8,7 @@
 #include <array>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/sequence_checker.h"
 #include "base/timer/timer.h"
 #include "base/version.h"
@@ -19,8 +20,6 @@
 
 namespace base {
 class Clock;
-template <typename T>
-class NoDestructor;
 class TickClock;
 }  // namespace base
 
diff --git a/chrome/browser/web_applications/components/install_bounce_metric.cc b/chrome/browser/web_applications/components/install_bounce_metric.cc
index fa0a73d..20b47f6 100644
--- a/chrome/browser/web_applications/components/install_bounce_metric.cc
+++ b/chrome/browser/web_applications/components/install_bounce_metric.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/web_applications/components/install_bounce_metric.h"
 
 #include "base/metrics/histogram_macros.h"
-#include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -16,8 +15,8 @@
 namespace {
 
 absl::optional<base::Time>& GetTimeOverride() {
-  static base::NoDestructor<absl::optional<base::Time>> g_time_override;
-  return *g_time_override;
+  static absl::optional<base::Time> time_override;
+  return time_override;
 }
 
 base::Time GetTime() {
diff --git a/chrome/common/channel_info_mac.mm b/chrome/common/channel_info_mac.mm
index cef77b676..6696a10f 100644
--- a/chrome/common/channel_info_mac.mm
+++ b/chrome/common/channel_info_mac.mm
@@ -95,7 +95,7 @@
 
 bool SideBySideCapable() {
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  static const base::NoDestructor<bool> capable([] {
+  static const bool capable = [] {
     // Use the main Chrome application bundle and not the framework bundle.
     // Keystone keys don't live in the framework.
     NSBundle* bundle = base::mac::OuterBundle();
@@ -117,8 +117,8 @@
     // beta/dev/canary Chrome is separate, and it can run side-by-side to the
     // stable Chrome.
     return [bundle objectForInfoDictionaryKey:@"CrProductDirName"] != nil;
-  }());
-  return *capable;
+  }();
+  return capable;
 #else
   return true;
 #endif
diff --git a/chrome/common/privacy_budget/BUILD.gn b/chrome/common/privacy_budget/BUILD.gn
index 8190119..0d22dbf 100644
--- a/chrome/common/privacy_budget/BUILD.gn
+++ b/chrome/common/privacy_budget/BUILD.gn
@@ -11,6 +11,7 @@
     "privacy_budget_features.h",
     "privacy_budget_settings_provider.cc",
     "privacy_budget_settings_provider.h",
+    "types.h",
   ]
 
   public_deps = [
diff --git a/chrome/common/privacy_budget/container_ops.h b/chrome/common/privacy_budget/container_ops.h
index 29ac502..6449586 100644
--- a/chrome/common/privacy_budget/container_ops.h
+++ b/chrome/common/privacy_budget/container_ops.h
@@ -15,8 +15,8 @@
 #include "base/strings/string_piece.h"
 
 namespace internal {
-// Note that these implementations aren't meant for to be used outside of the
-// narrow usage within this directory.
+// Note that these implementations aren't meant to be used outside of the narrow
+// usage within this directory.
 
 // Moves a random subset of |num| elements from |from| to |to|. The type |T|
 // must satisfy |UnorderedAssociativeContainer|.
@@ -47,7 +47,7 @@
   to_be_moved.reserve(from->size());
   std::copy_if(from->begin(), from->end(),
                std::inserter(to_be_moved, to_be_moved.end()), predicate);
-  for (const auto& v : to_be_moved)
+  for (const auto v : to_be_moved)
     from->erase(v);
   to->insert(to_be_moved.begin(), to_be_moved.end());
   return !to_be_moved.empty();
diff --git a/chrome/common/privacy_budget/container_ops_unittest.cc b/chrome/common/privacy_budget/container_ops_unittest.cc
index f524505..4d43c12 100644
--- a/chrome/common/privacy_budget/container_ops_unittest.cc
+++ b/chrome/common/privacy_budget/container_ops_unittest.cc
@@ -2,17 +2,32 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <initializer_list>
+#include <iterator>
 #include <set>
 
 #include "base/containers/cxx20_erase.h"
+#include "base/containers/flat_set.h"
+#include "base/notreached.h"
+#include "base/ranges/algorithm.h"
 #include "chrome/common/privacy_budget/container_ops.h"
+#include "chrome/common/privacy_budget/types.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
 
 namespace internal {
 
 namespace {
 
-typedef ::testing::Types<std::unordered_set<int>, std::set<int>> ContainerTypes;
+typedef ::testing::Types<IdentifiableSurfaceSet> ContainerTypes;
+
+IdentifiableSurfaceSet TestCaseFrom(std::initializer_list<int> ints) {
+  IdentifiableSurfaceSet::container_type surfaces;
+  base::ranges::transform(ints, std::back_inserter(surfaces), [](const auto v) {
+    return blink::IdentifiableSurface::FromMetricHash(v);
+  });
+  return IdentifiableSurfaceSet(surfaces);
+}
 
 }  // namespace
 
@@ -22,8 +37,8 @@
 TYPED_TEST_SUITE_P(ContainerOpsTest);
 
 TYPED_TEST_P(ContainerOpsTest, TestExtractRandomSubset) {
-  TypeParam from = {1, 2, 3, 4};
-  TypeParam to = {5};
+  TypeParam from = TestCaseFrom({1, 2, 3, 4});
+  TypeParam to = TestCaseFrom({5});
 
   EXPECT_TRUE(ExtractRandomSubset(&from, &to, 1));
   EXPECT_EQ(3u, from.size());
@@ -31,15 +46,15 @@
 }
 
 TYPED_TEST_P(ContainerOpsTest, TestExtractRandomSubset_Zero) {
-  TypeParam from = {1, 2, 3, 4};
-  TypeParam to = {5};
+  TypeParam from = TestCaseFrom({1, 2, 3, 4});
+  TypeParam to = TestCaseFrom({5});
 
   EXPECT_FALSE(ExtractRandomSubset(&from, &to, 0));
-  EXPECT_EQ(from, (TypeParam{1, 2, 3, 4}));
+  EXPECT_EQ(from, TestCaseFrom({1, 2, 3, 4}));
 }
 
 TYPED_TEST_P(ContainerOpsTest, TestExtractRandomSubset_Empty) {
-  TypeParam to = {5};
+  TypeParam to = TestCaseFrom({5});
 
   // Moving from an empty set.
   TypeParam from_two;
@@ -47,8 +62,9 @@
 }
 
 TYPED_TEST_P(ContainerOpsTest, TestExtractRandomSubset_Overflow) {
-  TypeParam from = {1, 2, 3, 4};
-  TypeParam to = {5};
+  TypeParam from = TestCaseFrom({1, 2, 3, 4});
+  TypeParam to = TestCaseFrom({5});
+
   // Overflow.
   EXPECT_TRUE(ExtractRandomSubset(&from, &to, 100));
   EXPECT_EQ(0u, from.size());
@@ -56,8 +72,9 @@
 }
 
 TYPED_TEST_P(ContainerOpsTest, TestExtractRandomSubset_Merge) {
-  TypeParam from = {1, 2, 3, 4};
-  TypeParam to = {1, 2, 3, 4, 5};
+  TypeParam from = TestCaseFrom({1, 2, 3, 4});
+  TypeParam to = TestCaseFrom({1, 2, 3, 4, 5});
+
   // Moved element disappears because |from| is a subset of |to|. Assuming the
   // underlying set implementation is correct (we'd be in pretty bad shape if
   // that were not true) verifies that the element being moved is one we expect.
@@ -67,9 +84,9 @@
 }
 
 TYPED_TEST_P(ContainerOpsTest, TestIntersects) {
-  TypeParam a = {1, 2, 3, 4};
-  TypeParam b = {4, 5, 6, 7};
-  TypeParam c = {10, 11};
+  TypeParam a = TestCaseFrom({1, 2, 3, 4});
+  TypeParam b = TestCaseFrom({4, 5, 6, 7});
+  TypeParam c = TestCaseFrom({10, 11});
   TypeParam d;
 
   EXPECT_TRUE(Intersects(a, b));
@@ -79,27 +96,28 @@
 }
 
 TYPED_TEST_P(ContainerOpsTest, TestExtractIf_Basic) {
-  TypeParam from = {1, 2, 3, 4, 5, 6};
+  TypeParam from = TestCaseFrom({1, 2, 3, 4, 5, 6});
   TypeParam to;
 
   // Extracts only when predicate is true.
-  EXPECT_TRUE(ExtractIf(&from, &to, [](const auto& v) { return v % 2 == 0; }));
-  EXPECT_EQ(from, (TypeParam{1, 3, 5}));
-  EXPECT_EQ(to, (TypeParam{2, 4, 6}));
+  EXPECT_TRUE(ExtractIf(
+      &from, &to, [](const auto& v) { return v.ToUkmMetricHash() % 2 == 0; }));
+  EXPECT_EQ(from, TestCaseFrom({1, 3, 5}));
+  EXPECT_EQ(to, TestCaseFrom({2, 4, 6}));
 }
 
 TYPED_TEST_P(ContainerOpsTest, TestExtractIf_AlwaysFalse) {
-  TypeParam from = {1, 3, 5};
+  TypeParam from = TestCaseFrom({1, 3, 5});
   TypeParam to;
 
   // Noop if predicate is always false.
   EXPECT_FALSE(ExtractIf(&from, &to, [](const auto& v) { return false; }));
-  EXPECT_EQ(from, (TypeParam{1, 3, 5}));
+  EXPECT_EQ(from, TestCaseFrom({1, 3, 5}));
   EXPECT_TRUE(to.empty());
 }
 
 TYPED_TEST_P(ContainerOpsTest, TestExtractIf_AlwaysTrue) {
-  TypeParam from = {1, 2, 3, 4, 5, 6};
+  TypeParam from = TestCaseFrom({1, 2, 3, 4, 5, 6});
   TypeParam to;
 
   // Just because. Isn't testing anything new.
@@ -110,7 +128,7 @@
 
 TYPED_TEST_P(ContainerOpsTest, TestExtractIf_EmptySource) {
   TypeParam from;
-  TypeParam to = {1, 2, 3, 4, 5, 6};
+  TypeParam to = TestCaseFrom({1, 2, 3, 4, 5, 6});
 
   // Noop if |from| is empty.
   EXPECT_FALSE(ExtractIf(&from, &to, [](const auto& v) { return true; }));
@@ -119,29 +137,29 @@
 }
 
 TYPED_TEST_P(ContainerOpsTest, TestSubtractLeftFromRight_WithIntersection) {
-  TypeParam a = {1, 2, 3, 4, 5, 6, 7};
-  TypeParam b = {2, 3, 4};
+  TypeParam a = TestCaseFrom({1, 2, 3, 4, 5, 6, 7});
+  TypeParam b = TestCaseFrom({2, 3, 4});
 
   EXPECT_TRUE(SubtractLeftFromRight(b, &a));
-  EXPECT_EQ(a, (TypeParam{1, 5, 6, 7}));
+  EXPECT_EQ(a, (TestCaseFrom({1, 5, 6, 7})));
 }
 
 TYPED_TEST_P(ContainerOpsTest, TestSubtractLeftFromRight_NoIntersection) {
-  TypeParam a = {1, 5, 6, 7};
-  TypeParam c = {10};
+  TypeParam a = TestCaseFrom({1, 5, 6, 7});
+  TypeParam c = TestCaseFrom({10});
 
   // Nothing happens if there's no intersection.
   EXPECT_FALSE(SubtractLeftFromRight(c, &a));
-  EXPECT_EQ(a, (TypeParam{1, 5, 6, 7}));
+  EXPECT_EQ(a, TestCaseFrom({1, 5, 6, 7}));
 }
 
 TYPED_TEST_P(ContainerOpsTest, TestSubtractLeftFromRight_LeftEmpty) {
-  TypeParam a = {1, 5, 6, 7};
+  TypeParam a = TestCaseFrom({1, 5, 6, 7});
   TypeParam d;
 
   // Nothing happens when the left side is empty.
   EXPECT_FALSE(SubtractLeftFromRight(d, &a));
-  EXPECT_EQ(a, (TypeParam{1, 5, 6, 7}));
+  EXPECT_EQ(a, TestCaseFrom({1, 5, 6, 7}));
 }
 
 REGISTER_TYPED_TEST_SUITE_P(ContainerOpsTest,
diff --git a/chrome/common/privacy_budget/field_trial_param_conversions.cc b/chrome/common/privacy_budget/field_trial_param_conversions.cc
index 80859cbe..bc0143d 100644
--- a/chrome/common/privacy_budget/field_trial_param_conversions.cc
+++ b/chrome/common/privacy_budget/field_trial_param_conversions.cc
@@ -4,7 +4,15 @@
 
 #include "chrome/common/privacy_budget/field_trial_param_conversions.h"
 
+#include <algorithm>
 #include <utility>
+#include "base/ranges/algorithm.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
+
+namespace privacy_budget_internal {
 
 bool DecodeIdentifiabilityType(const base::StringPiece s,
                                blink::IdentifiableSurface* out) {
@@ -26,6 +34,25 @@
   return true;
 }
 
+bool DecodeIdentifiabilityType(const base::StringPiece s, int* out) {
+  return base::StringToInt(s, out);
+}
+
+bool DecodeIdentifiabilityType(const base::StringPiece s, unsigned int* out) {
+  return base::StringToUint(s, out);
+}
+
+bool DecodeIdentifiabilityType(const base::StringPiece s, double* out) {
+  return base::StringToDouble(s, out);
+}
+
+bool DecodeIdentifiabilityType(const base::StringPiece s,
+                               std::vector<blink::IdentifiableSurface>* out) {
+  *out = DecodeIdentifiabilityFieldTrialParam<
+      std::vector<blink::IdentifiableSurface>, ';'>(s);
+  return !out->empty();
+}
+
 std::string EncodeIdentifiabilityType(const blink::IdentifiableSurface& s) {
   return base::NumberToString(s.ToUkmMetricHash());
 }
@@ -37,12 +64,23 @@
 
 std::string EncodeIdentifiabilityType(
     const std::pair<const blink::IdentifiableSurface, int>& v) {
-  return EncodeIdentifiabilityType(v.first) + ";" +
-         base::NumberToString(v.second);
+  return base::StrCat({EncodeIdentifiabilityType(v.first), ";",
+                       base::NumberToString(v.second)});
+}
+
+std::string EncodeIdentifiabilityType(const unsigned int& v) {
+  return base::NumberToString(v);
 }
 
 std::string EncodeIdentifiabilityType(
-    const std::pair<const blink::IdentifiableSurface::Type, int>& v) {
-  return EncodeIdentifiabilityType(v.first) + ";" +
-         base::NumberToString(v.second);
+    const std::vector<blink::IdentifiableSurface>& value) {
+  std::vector<std::string> parts;
+  base::ranges::transform(
+      value, std::back_inserter(parts),
+      [](const blink::IdentifiableSurface v) -> std::string {
+        return EncodeIdentifiabilityType(v);
+      });
+  return base::JoinString(parts, ";");
 }
+
+}  // namespace privacy_budget_internal
diff --git a/chrome/common/privacy_budget/field_trial_param_conversions.h b/chrome/common/privacy_budget/field_trial_param_conversions.h
index 3c99297..f967aa34 100644
--- a/chrome/common/privacy_budget/field_trial_param_conversions.h
+++ b/chrome/common/privacy_budget/field_trial_param_conversions.h
@@ -5,101 +5,107 @@
 #ifndef CHROME_COMMON_PRIVACY_BUDGET_FIELD_TRIAL_PARAM_CONVERSIONS_H_
 #define CHROME_COMMON_PRIVACY_BUDGET_FIELD_TRIAL_PARAM_CONVERSIONS_H_
 
+#include <iterator>
 #include <type_traits>
+#include <vector>
 
-#include "base/notreached.h"
+#include "base/ranges/algorithm.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
-#include "chrome/common/privacy_budget/privacy_budget_features.h"
 #include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
 
 // The Encode/Decode families of functions are meant to be used to encode
 // various types so that they can be specified via field trial configurations
 // and Prefs.
 
+namespace privacy_budget_internal {
+
+// DecodeIdentifiabilityType() overloads should not be called directly. Instead
+// use either DecodeIdentifiabilityFieldTrialParam() or
+// EncodeIdentifiabilityFieldTrialParam().
+//
+// DecodeIdentifiabilityType(StringPiece s,V* v) decodes a element of type
+// V serialized as a string and referred to via StringPiece s and stores it in
+// *v.
 bool DecodeIdentifiabilityType(const base::StringPiece,
                                blink::IdentifiableSurface*);
-
 bool DecodeIdentifiabilityType(const base::StringPiece,
                                blink::IdentifiableSurface::Type*);
+bool DecodeIdentifiabilityType(const base::StringPiece, int*);
+bool DecodeIdentifiabilityType(const base::StringPiece, unsigned int*);
+bool DecodeIdentifiabilityType(const base::StringPiece, double*);
+bool DecodeIdentifiabilityType(const base::StringPiece,
+                               std::vector<blink::IdentifiableSurface>*);
 
-// This explosion of DecodeIdentifiabilityTypePair specializations is a great
-// example of why you don't want to go down the path of templates if you could
-// ever avoid it.
-//
-// The variability here is that unordered_map uses pair<K,V> as its value_type
-// while map uses pair<const K, V>. Per spec, unordered_map should also use the
-// latter, but alas, that is not the case.
-//
-// Adding to the confusion, pair<const K, V> is non-movable and non-copyable. So
-// it can't be returned via a trivial *out parameter. Amazing.
-template <typename U,
-          typename V = typename U::value_type,
-          typename P = typename std::remove_const<typename V::first_type>::type>
-std::pair<V, bool> DecodeIdentifiabilityTypePair(base::StringPiece s) {
+// V is a std::pair<P,R> where P and R are types known to
+// DecodeIdentifiabilityType().
+template <
+    typename V,
+    typename P = typename std::remove_const<typename V::first_type>::type,
+    typename R = typename std::remove_const<typename V::second_type>::type>
+bool DecodeIdentifiabilityType(base::StringPiece s, V* result) {
   auto pieces =
       base::SplitStringPiece(s, ";", base::WhitespaceHandling::TRIM_WHITESPACE,
                              base::SplitResult::SPLIT_WANT_NONEMPTY);
   if (pieces.size() != 2)
-    return {V(), false};
-  P type_id;
-  int rate;
-  if (!DecodeIdentifiabilityType(pieces[0], &type_id) ||
-      !base::StringToInt(pieces[1], &rate))
-    return {V(), false};
-  if (rate < 0 || rate > features::kMaxIdentifiabilityStudySurfaceSelectionRate)
-    return {V(), false};
-  return {V(type_id, rate), true};
-}
-
-template <
-    typename U,
-    typename S = std::enable_if_t<
-        std::is_same<typename U::key_type, typename U::value_type>::value>>
-std::pair<typename U::value_type, bool> DecodeIdentifiabilityTypePair(
-    base::StringPiece s) {
-  using P = typename U::value_type;
-  P value;
-  if (!DecodeIdentifiabilityType(s, &value))
-    return {P(), false};
-  return {value, true};
+    return false;
+  P first;
+  R second;
+  if (!DecodeIdentifiabilityType(pieces[0], &first) ||
+      !DecodeIdentifiabilityType(pieces[1], &second))
+    return false;
+  ::new (result) V(std::move(first), std::move(second));
+  return true;
 }
 
 std::string EncodeIdentifiabilityType(const blink::IdentifiableSurface&);
-
 std::string EncodeIdentifiabilityType(const blink::IdentifiableSurface::Type&);
-
+std::string EncodeIdentifiabilityType(const unsigned int&);
+template <typename T, typename U>
+std::string EncodeIdentifiabilityType(const std::pair<T, U>& v) {
+  return base::StrCat({EncodeIdentifiabilityType(v.first), ";",
+                       base::NumberToString(v.second)});
+}
 std::string EncodeIdentifiabilityType(
-    const std::pair<const blink::IdentifiableSurface, int>&);
+    const std::vector<blink::IdentifiableSurface>& value);
 
-std::string EncodeIdentifiabilityType(
-    const std::pair<const blink::IdentifiableSurface::Type, int>&);
+template <typename T>
+struct NoOpFilter {
+  bool operator()(T t) { return true; }
+};
 
-std::string EncodeIdentifiabilityType(
-    const std::pair<blink::IdentifiableSurface, int>&);
+// Instantiate with a type and inherit from std::true_type in order to sort the
+// encoded elements of a container. The ordering is undefined but stable across
+// versions of Chrome.
+template <typename T>
+struct SortWhenSerializing : std::false_type {};
 
-std::string EncodeIdentifiabilityType(
-    const std::pair<blink::IdentifiableSurface::Type, int>&);
+}  // namespace privacy_budget_internal
 
 // Decodes a field trial parameter containing a list of values. The result is
 // returned in the form of a container type that must be specified at
 // instantiation. There should be a valid DecodeIdentifiabilityType
 // specialization for the container's value type.
 template <typename T,
-          std::pair<typename T::value_type, bool> Decoder(
-              const base::StringPiece) = DecodeIdentifiabilityTypePair<T>>
+          char Separator = ',',
+          typename V = typename T::value_type,
+          bool ElementDecoder(const base::StringPiece, V*) =
+              &privacy_budget_internal::DecodeIdentifiabilityType>
 T DecodeIdentifiabilityFieldTrialParam(base::StringPiece encoded_value) {
   T result;
-  auto hashes = base::SplitStringPiece(
-      encoded_value, ",", base::WhitespaceHandling::TRIM_WHITESPACE,
-      base::SplitResult::SPLIT_WANT_NONEMPTY);
-  for (const auto& hash : hashes) {
-    auto decoded = Decoder(hash);
-    if (!decoded.second)
+  auto pieces =
+      base::SplitStringPiece(encoded_value, std::string(1, Separator),
+                             base::WhitespaceHandling::TRIM_WHITESPACE,
+                             base::SplitResult::SPLIT_WANT_NONEMPTY);
+  auto inserter = std::inserter(result, result.end());
+  for (const auto& piece : pieces) {
+    V v;
+    if (!ElementDecoder(piece, &v))
       continue;
-    result.insert(decoded.first);
+    inserter = v;
   }
   return result;
 }
@@ -109,13 +115,18 @@
 // value_type must have a corresponding EncodeIdentifiabilityType
 // specialization.
 template <typename T,
-          std::string Encoder(const typename T::value_type&) =
-              EncodeIdentifiabilityType>
+          std::string ElementEncoder(const typename T::value_type&) =
+              privacy_budget_internal::EncodeIdentifiabilityType>
 std::string EncodeIdentifiabilityFieldTrialParam(const T& source) {
-  std::vector<std::string> result;
-  std::transform(source.begin(), source.end(), std::back_inserter(result),
-                 [](auto& v) { return Encoder(v); });
-  return base::JoinString(result, ",");
+  std::vector<std::string> encoded_elements;
+  std::transform(source.begin(), source.end(),
+                 std::back_inserter(encoded_elements),
+                 [](auto& v) { return ElementEncoder(v); });
+  if (privacy_budget_internal::SortWhenSerializing<
+          typename std::remove_cv<T>::type>::value) {
+    base::ranges::sort(encoded_elements);
+  }
+  return base::JoinString(encoded_elements, ",");
 }
 
 #endif  // CHROME_COMMON_PRIVACY_BUDGET_FIELD_TRIAL_PARAM_CONVERSIONS_H_
diff --git a/chrome/common/privacy_budget/field_trial_param_conversions_unittest.cc b/chrome/common/privacy_budget/field_trial_param_conversions_unittest.cc
index 88a15f5..0d7e9f59 100644
--- a/chrome/common/privacy_budget/field_trial_param_conversions_unittest.cc
+++ b/chrome/common/privacy_budget/field_trial_param_conversions_unittest.cc
@@ -4,60 +4,224 @@
 
 #include "chrome/common/privacy_budget/field_trial_param_conversions.h"
 
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "chrome/common/privacy_budget/types.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
 
-TEST(FieldTrialParamConversionsTest, Surface) {
-  auto original_surface = blink::IdentifiableSurface::FromMetricHash(100);
-  EXPECT_EQ(std::string("100"), EncodeIdentifiabilityType(original_surface));
+namespace privacy_budget_internal {
 
-  original_surface = blink::IdentifiableSurface::FromTypeAndToken(
-      blink::IdentifiableSurface::Type::kWebFeature, 1);
-  std::string encoded = EncodeIdentifiabilityType(original_surface);
-  EXPECT_EQ(std::string("257"), encoded);
+namespace {
+
+// 100
+constexpr auto kSurface1 = blink::IdentifiableSurface::FromMetricHash(100);
+
+// 257
+constexpr auto kSurface2 = blink::IdentifiableSurface::FromTypeAndToken(
+    blink::IdentifiableSurface::Type::kWebFeature,
+    1);
+
+// 36028797018963968
+constexpr auto kSurface3 =
+    blink::IdentifiableSurface::FromMetricHash(UINT64_C(1) << 55);
+
+// 1
+constexpr auto kType1 = blink::IdentifiableSurface::Type::kWebFeature;
+
+// 9
+constexpr auto kType2 =
+    blink::IdentifiableSurface::Type::kMediaRecorder_IsTypeSupported;
+
+}  // namespace
+
+TEST(FieldTrialParamConversionsTest, EncodeDecodeSingleSurface) {
+  EXPECT_EQ(std::string("100"), EncodeIdentifiabilityType(kSurface1));
 
   auto decoded_surface = blink::IdentifiableSurface();
-  EXPECT_TRUE(DecodeIdentifiabilityType(encoded, &decoded_surface));
-  EXPECT_EQ(original_surface, decoded_surface);
+  EXPECT_TRUE(DecodeIdentifiabilityType("257", &decoded_surface));
+  EXPECT_EQ(kSurface2, decoded_surface);
+
+  EXPECT_FALSE(DecodeIdentifiabilityType("", &decoded_surface));
 }
 
-TEST(FieldTrialParamConversionsTest, Type) {
-  auto original_type = blink::IdentifiableSurface::Type::kWebFeature;
-  std::string encoded = EncodeIdentifiabilityType(original_type);
-  EXPECT_EQ(std::string("1"), encoded);
+TEST(FieldTrialParamConversionsTest, EncodeDecodeSingleType) {
+  EXPECT_EQ(std::string("1"), EncodeIdentifiabilityType(kType1));
 
   auto foo = blink::IdentifiableSurface::Type::kReservedInternal;
-  EXPECT_TRUE(DecodeIdentifiabilityType(encoded, &foo));
-  EXPECT_EQ(original_type, foo);
+  EXPECT_TRUE(DecodeIdentifiabilityType("1", &foo));
+  EXPECT_EQ(kType1, foo);
+
+  EXPECT_FALSE(DecodeIdentifiabilityType("", &foo));
 }
 
-TEST(FieldTrialParamConversionsTest, FieldTrialParam_Surface) {
-  std::map<blink::IdentifiableSurface, int> original_map;
-  original_map[blink::IdentifiableSurface::FromMetricHash(100)] = 5;
-  original_map[blink::IdentifiableSurface::FromMetricHash(UINT64_C(1) << 55)] =
-      5;
+TEST(FieldTrialParamConversionsTest, SurfaceToIntMap) {
+  const std::map<blink::IdentifiableSurface, unsigned int> original_map = {
+      {kSurface1, 5}, {kSurface3, 5}};
 
   auto encoded = EncodeIdentifiabilityFieldTrialParam(original_map);
   EXPECT_EQ(std::string("100;5,36028797018963968;5"), encoded);
 
   auto decoded_map = DecodeIdentifiabilityFieldTrialParam<
-      std::map<blink::IdentifiableSurface, int>>(encoded);
+      std::map<blink::IdentifiableSurface, unsigned int>>(encoded);
   EXPECT_EQ(original_map, decoded_map);
 }
 
-TEST(FieldTrialParamConversionsTest, FieldTrialParam_Type) {
-  std::map<blink::IdentifiableSurface::Type, int> original_map;
-  original_map[blink::IdentifiableSurface::Type::kReservedInternal] = 6;
-  original_map[blink::IdentifiableSurface::Type::kWebFeature] = 5;
+TEST(FieldTrialParamConversionsTest, IdentifiableSurfaceList) {
+  const IdentifiableSurfaceList kSurfaceList = {kSurface1, kSurface2,
+                                                kSurface3};
+
+  auto encoded_surface_list =
+      EncodeIdentifiabilityFieldTrialParam(kSurfaceList);
+  EXPECT_EQ(std::string("100,257,36028797018963968"), encoded_surface_list);
+
+  auto decoded_surface_list =
+      DecodeIdentifiabilityFieldTrialParam<IdentifiableSurfaceList>(
+          encoded_surface_list);
+  EXPECT_EQ(kSurfaceList, decoded_surface_list);
+}
+
+TEST(FieldTrialParamConversionsTest, IdentifiableSurfaceTypeList) {
+  const IdentifiableSurfaceTypeList kTypeList = {kType1, kType2};
+
+  auto encoded_type_list = EncodeIdentifiabilityFieldTrialParam(kTypeList);
+  EXPECT_EQ(std::string("1,9"), encoded_type_list);
+
+  auto decoded_type_list =
+      DecodeIdentifiabilityFieldTrialParam<IdentifiableSurfaceTypeList>(
+          encoded_type_list);
+  EXPECT_EQ(kTypeList, decoded_type_list);
+}
+
+TEST(FieldTrialParamConversionsTest, IdentifiableSurfaceSet) {
+  const IdentifiableSurfaceSet kSurfaceSet = {kSurface1, kSurface2, kSurface3};
+
+  auto encoded_surface_set = EncodeIdentifiabilityFieldTrialParam(kSurfaceSet);
+  EXPECT_EQ(std::string("100,257,36028797018963968"), encoded_surface_set);
+
+  auto decoded_surface_set =
+      DecodeIdentifiabilityFieldTrialParam<IdentifiableSurfaceSet>(
+          encoded_surface_set);
+  EXPECT_EQ(kSurfaceSet, decoded_surface_set);
+}
+
+TEST(FieldTrialParamConversionsTest, IdentifiableSurfaceTypeSet) {
+  const IdentifiableSurfaceTypeSet kTypeSet = {kType1, kType2};
+
+  auto encoded_type_set = EncodeIdentifiabilityFieldTrialParam(kTypeSet);
+  EXPECT_EQ(std::string("1,9"), encoded_type_set);
+
+  auto decoded_type_set =
+      DecodeIdentifiabilityFieldTrialParam<IdentifiableSurfaceTypeSet>(
+          encoded_type_set);
+  EXPECT_EQ(kTypeSet, decoded_type_set);
+}
+
+TEST(FieldTrialParamConversionsTest, IdentifiableSurfaceSampleRateMap) {
+  IdentifiableSurfaceSampleRateMap original_map = {{kSurface1, 5},
+                                                   {kSurface2, 6}};
 
   auto encoded = EncodeIdentifiabilityFieldTrialParam(original_map);
-  EXPECT_EQ(std::string("0;6,1;5"), encoded);
+  EXPECT_EQ(std::string("100;5,257;6"), encoded);
 
-  auto decoded_map = DecodeIdentifiabilityFieldTrialParam<
-      std::map<blink::IdentifiableSurface::Type, int>>(encoded);
+  auto decoded_map =
+      DecodeIdentifiabilityFieldTrialParam<IdentifiableSurfaceSampleRateMap>(
+          encoded);
   EXPECT_EQ(original_map, decoded_map);
 }
 
-TEST(FieldTrialParamConversionsTest, DecodeBadSurface) {
+TEST(FieldTrialParamConversionsTest, IdentifiableSurfaceTypeSampleRateMap) {
+  const IdentifiableSurfaceTypeSampleRateMap original_map = {{kType1, 6},
+                                                             {kType2, 7}};
+
+  auto encoded = EncodeIdentifiabilityFieldTrialParam(original_map);
+  EXPECT_EQ(std::string("1;6,9;7"), encoded);
+
+  auto decoded_map = DecodeIdentifiabilityFieldTrialParam<
+      IdentifiableSurfaceTypeSampleRateMap>(encoded);
+  EXPECT_EQ(original_map, decoded_map);
+
+  // Extraneous bad values should be silently skipped.
+  auto decoded_with_noise = DecodeIdentifiabilityFieldTrialParam<
+      IdentifiableSurfaceTypeSampleRateMap>("1;6,2;3;4,9;7,10");
+  EXPECT_EQ(original_map, decoded_with_noise);
+}
+
+TEST(FieldTrialParamConversionsTest, IdentifiableSurfaceCostMap) {
+  const IdentifiableSurfaceCostMap original_map = {
+      {kSurface1, 0.5}, {kSurface2, 0.25}, {kSurface3, 0.4}};
+
+  auto encoded =
+      EncodeIdentifiabilityFieldTrialParam<IdentifiableSurfaceCostMap>(
+          original_map);
+  EXPECT_EQ(std::string("100;0.5,257;0.25,36028797018963968;0.4"), encoded);
+
+  auto decoded =
+      DecodeIdentifiabilityFieldTrialParam<IdentifiableSurfaceCostMap>(
+          "100;0.5,257;0.25,36028797018963968;0.4");
+  EXPECT_EQ(original_map, decoded);
+}
+
+TEST(FieldTrialParamConversionsTest, IdentifiableSurfaceTypeCostMap) {
+  const IdentifiableSurfaceTypeCostMap original_map = {{kType1, 0.5},
+                                                       {kType2, 0.25}};
+
+  auto encoded =
+      EncodeIdentifiabilityFieldTrialParam<IdentifiableSurfaceTypeCostMap>(
+          original_map);
+  EXPECT_EQ(std::string("1;0.5,9;0.25"), encoded);
+
+  auto decoded =
+      DecodeIdentifiabilityFieldTrialParam<IdentifiableSurfaceTypeCostMap>(
+          "1;0.5,9;0.25");
+  EXPECT_EQ(original_map, decoded);
+}
+
+TEST(FieldTrialParamConversionsTest, SurfaceSetEquivalentClassesList) {
+  const SurfaceSetEquivalentClassesList original_classes = {
+      {kSurface1, kSurface2, kSurface3}, {kSurface3, kSurface2, kSurface1}};
+  auto encoded =
+      EncodeIdentifiabilityFieldTrialParam<SurfaceSetEquivalentClassesList>(
+          original_classes);
+  EXPECT_EQ(std::string("100;257;36028797018963968,36028797018963968;257;100"),
+            encoded);
+  auto decoded =
+      DecodeIdentifiabilityFieldTrialParam<SurfaceSetEquivalentClassesList>(
+          encoded);
+  EXPECT_EQ(original_classes, decoded);
+}
+
+TEST(FieldTrialParamConversionsTest, IdentifiableSurfaceGroupList) {
+  const IdentifiableSurfaceGroupList original_classes = {
+      {kSurface1, kSurface2, kSurface3}, {kSurface3, kSurface2, kSurface1}};
+  auto encoded =
+      EncodeIdentifiabilityFieldTrialParam<IdentifiableSurfaceGroupList>(
+          original_classes);
+  EXPECT_EQ(std::string("100;257;36028797018963968,36028797018963968;257;100"),
+            encoded);
+  auto decoded =
+      DecodeIdentifiabilityFieldTrialParam<IdentifiableSurfaceGroupList>(
+          encoded);
+  EXPECT_EQ(original_classes, decoded);
+}
+
+TEST(FieldTrialParamConversionsTest, VectorOfSizeT) {
+  const std::vector<unsigned int> kNumbers = {1, 3, 1000,
+                                              std::numeric_limits<int>::max()};
+  auto encoded = EncodeIdentifiabilityFieldTrialParam(kNumbers);
+  EXPECT_EQ(std::string("1,3,1000,2147483647"), encoded);
+
+  auto decoded =
+      DecodeIdentifiabilityFieldTrialParam<std::vector<unsigned int>>(
+          "4,  1000,  23");
+  EXPECT_EQ(std::vector<unsigned int>({4, 1000, 23}), decoded);
+}
+
+TEST(FieldTrialParamConversionsTest, DecodeBadValues) {
   auto decoded_surface = blink::IdentifiableSurface();
   EXPECT_FALSE(DecodeIdentifiabilityType("foo", &decoded_surface));
   EXPECT_FALSE(DecodeIdentifiabilityType("-100", &decoded_surface));
@@ -65,9 +229,11 @@
                                          &decoded_surface));
 }
 
-TEST(FieldTrialParamConversionsTest, DecodeBadType) {
+TEST(FieldTrialParamConversionsTest, DecodeBadTypes) {
   auto decoded_type = blink::IdentifiableSurface::Type::kReservedInternal;
   EXPECT_FALSE(DecodeIdentifiabilityType("foo", &decoded_type));
   EXPECT_FALSE(DecodeIdentifiabilityType("-100", &decoded_type));
   EXPECT_FALSE(DecodeIdentifiabilityType("256", &decoded_type));
 }
+
+}  // namespace privacy_budget_internal
diff --git a/chrome/common/privacy_budget/privacy_budget_settings_provider.h b/chrome/common/privacy_budget/privacy_budget_settings_provider.h
index 81d597d..375cfae 100644
--- a/chrome/common/privacy_budget/privacy_budget_settings_provider.h
+++ b/chrome/common/privacy_budget/privacy_budget_settings_provider.h
@@ -5,10 +5,8 @@
 #ifndef CHROME_COMMON_PRIVACY_BUDGET_PRIVACY_BUDGET_SETTINGS_PROVIDER_H_
 #define CHROME_COMMON_PRIVACY_BUDGET_PRIVACY_BUDGET_SETTINGS_PROVIDER_H_
 
-#include <memory>
-#include <unordered_set>
-
 #include "base/containers/flat_map.h"
+#include "chrome/common/privacy_budget/types.h"
 #include "third_party/blink/public/common/privacy_budget/identifiability_study_settings_provider.h"
 #include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
 
@@ -34,12 +32,6 @@
 class PrivacyBudgetSettingsProvider final
     : public blink::IdentifiabilityStudySettingsProvider {
  public:
-  using IdentifiableSurfaceSet =
-      std::unordered_set<blink::IdentifiableSurface,
-                         blink::IdentifiableSurfaceHash>;
-  using IdentifiableSurfaceTypeSet =
-      std::unordered_set<blink::IdentifiableSurface::Type>;
-
   PrivacyBudgetSettingsProvider();
   PrivacyBudgetSettingsProvider(const PrivacyBudgetSettingsProvider&);
   PrivacyBudgetSettingsProvider(PrivacyBudgetSettingsProvider&&);
diff --git a/chrome/common/privacy_budget/types.h b/chrome/common/privacy_budget/types.h
new file mode 100644
index 0000000..c89732d
--- /dev/null
+++ b/chrome/common/privacy_budget/types.h
@@ -0,0 +1,98 @@
+// 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_COMMON_PRIVACY_BUDGET_TYPES_H_
+#define CHROME_COMMON_PRIVACY_BUDGET_TYPES_H_
+
+#include <type_traits>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "chrome/common/privacy_budget/field_trial_param_conversions.h"
+#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
+
+// Common container and map types. In order to verify successful encoding and
+// decoding, each of these must be tested in
+// field_trial_param_conversions_unittest.cc.
+//
+// In all cases, the choice of container assumes that:
+//   1. Size is relatively low: Use of a contiguous container helps with data
+//      locality.
+//   2. Mutations are uncommon: A contiguous container is usually expensive to
+//      mutate, but fast lookups and locality make up for it.
+//
+// If other characteristics are desired, then we should consider other container
+// types. Please test encoding/decoding when using new container types.
+
+using IdentifiableSurfaceSet = base::flat_set<blink::IdentifiableSurface>;
+
+using IdentifiableSurfaceTypeSet =
+    base::flat_set<blink::IdentifiableSurface::Type>;
+
+using IdentifiableSurfaceList = std::vector<blink::IdentifiableSurface>;
+
+using IdentifiableSurfaceTypeList =
+    std::vector<blink::IdentifiableSurface::Type>;
+
+// Sampling rates are represented as the denominator of a quotient 1/R. I.e.
+// A sampling rate of 1 in 100 is represented using the integer 100.
+using IdentifiableSurfaceSampleRateMap =
+    base::flat_map<blink::IdentifiableSurface, unsigned int>;
+
+// Sampling rates are represented as the denominator of a quotient 1/R. I.e.
+// A sampling rate of 1 in 100 is represented using the integer 100.
+using IdentifiableSurfaceTypeSampleRateMap =
+    base::flat_map<blink::IdentifiableSurface::Type, unsigned int>;
+
+// Costs are represented as a ratio relative to the "median" identifiability of
+// a single API. This odd choice is due to backwards compatibility where prior
+// versions of the study controlled client exposure via placing a limit on the
+// _number_ of surfaces sampled.
+//
+// Relative costs are in the logarithmic domain. Explained below.
+//
+// Let's say there's a surface 𝐀 whose value can be used to uniformly divide the
+// audience in to four segments. In terms of Shannon entropy one might say that
+// the information content of surface 𝐀 is log₂4 bits. Now if there's another
+// surface 𝐁 whose relative cost is 0.5, then 𝐁 would have a Shannon entropy of
+// 0.5×log₂4 bits. In other words 𝐁 has the information content equivalent to
+// what's needed to uniformly divide an audience into √4=2 equal parts.
+//
+// In general, if the median identifiability is 𝐦, and the relative
+// identifiability of a surface is 𝐫, then the identifiability of that surface
+// is mʳ.
+using PrivacyBudgetCost = double;
+
+using IdentifiableSurfaceCostMap =
+    base::flat_map<blink::IdentifiableSurface, PrivacyBudgetCost>;
+
+using IdentifiableSurfaceTypeCostMap =
+    base::flat_map<blink::IdentifiableSurface::Type, PrivacyBudgetCost>;
+
+// See SurfaceSetEquivalence for details on how equivalence classes work.
+// SurfaceSetEquivalentClassesList contains a list of equivalence classes. Each
+// class is encoded as a list of surfaces.
+//
+// **The first element in the list is considered to be the representative
+// surface for that class.
+//
+// Obv an equivalence set which contains just zero or one members is
+// nonsensical. For the purpose of ecoding/decoding such instances are ignored.
+using SurfaceSetEquivalentClassesList = std::vector<IdentifiableSurfaceList>;
+
+// Similar to the SurfaceSetEquivalentClassesList, but is semantically different
+// in that the ordering doesn't matter. There's no assumption that the first
+// element of each list is special in any meaningful way.
+using IdentifiableSurfaceGroupList = std::vector<IdentifiableSurfaceList>;
+
+namespace privacy_budget_internal {
+
+template <>
+struct SortWhenSerializing<IdentifiableSurfaceSet> : std::true_type {};
+template <>
+struct SortWhenSerializing<IdentifiableSurfaceTypeSet> : std::true_type {};
+
+}  // namespace privacy_budget_internal
+#endif  // CHROME_COMMON_PRIVACY_BUDGET_TYPES_H_
diff --git a/chrome/common/profiler/thread_profiler.cc b/chrome/common/profiler/thread_profiler.cc
index ca98c2a..05b83820 100644
--- a/chrome/common/profiler/thread_profiler.cc
+++ b/chrome/common/profiler/thread_profiler.cc
@@ -282,8 +282,7 @@
 void ThreadProfiler::StartOnChildThread(CallStackProfileParams::Thread thread) {
   // The profiler object is stored in a SequenceLocalStorageSlot on child
   // threads to give it the same lifetime as the threads.
-  static base::NoDestructor<
-      base::SequenceLocalStorageSlot<std::unique_ptr<ThreadProfiler>>>
+  static base::SequenceLocalStorageSlot<std::unique_ptr<ThreadProfiler>>
       child_thread_profiler_sequence_local_storage;
 
   if (!ThreadProfilerConfiguration::Get()
@@ -291,7 +290,7 @@
     return;
   }
 
-  child_thread_profiler_sequence_local_storage->emplace(
+  child_thread_profiler_sequence_local_storage.emplace(
       new ThreadProfiler(thread, base::ThreadTaskRunnerHandle::Get()));
 }
 
diff --git a/chrome/common/profiler/thread_profiler_configuration.h b/chrome/common/profiler/thread_profiler_configuration.h
index 27f56c5..e67d413 100644
--- a/chrome/common/profiler/thread_profiler_configuration.h
+++ b/chrome/common/profiler/thread_profiler_configuration.h
@@ -8,6 +8,7 @@
 #include <initializer_list>
 #include <string>
 
+#include "base/no_destructor.h"
 #include "base/profiler/stack_sampling_profiler.h"
 #include "components/metrics/call_stack_profile_params.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -15,8 +16,6 @@
 
 namespace base {
 class CommandLine;
-template <typename>
-class NoDestructor;
 }  // namespace base
 
 class ThreadProfilerPlatformConfiguration;
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 95b6bb7..31d58aa 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -1041,9 +1041,20 @@
           break;
         }
 
-#if BUILDFLAG(ENABLE_PDF_UNSEASONED)
         if (info.name ==
             ASCIIToUTF16(ChromeContentClient::kPDFInternalPluginName)) {
+          // For a PDF plugin, `params.url` holds the plugin's stream url. If
+          // `params` contains an 'original-url' attribute, reset `params.url`
+          // with its original URL value so that it can be used to determine
+          // the plugin's origin.
+          for (size_t i = 0; i < params.attribute_names.size(); ++i) {
+            if (params.attribute_names[i] == "original-url") {
+              params.url = GURL(params.attribute_values[i].Utf16());
+              break;
+            }
+          }
+
+#if BUILDFLAG(ENABLE_PDF_UNSEASONED)
           // Create unseasoned PDF plugin directly, for development purposes.
           // TODO(crbug.com/1123621): Implement a more permanent solution once
           // the new PDF viewer process model is approved and in place.
@@ -1054,12 +1065,11 @@
 #if BUILDFLAG(ENABLE_PRINTING)
           print_client =
               std::make_unique<ChromePdfViewWebPluginPrintClient>(render_frame);
-#endif
+#endif  // BUILDFLAG(ENABLE_PRINTING)
           return new chrome_pdf::PdfViewWebPlugin(
               std::move(pdf_service_remote), std::move(print_client), params);
-        }
 #endif  // BUILDFLAG(ENABLE_PDF_UNSEASONED)
-
+        }
         return render_frame->CreatePlugin(info, params);
       }
       case chrome::mojom::PluginStatus::kDisabled: {
diff --git a/chrome/renderer/extensions/notifications_native_handler.cc b/chrome/renderer/extensions/notifications_native_handler.cc
index ba5500b..aa1e815f 100644
--- a/chrome/renderer/extensions/notifications_native_handler.cc
+++ b/chrome/renderer/extensions/notifications_native_handler.cc
@@ -31,8 +31,8 @@
     const v8::FunctionCallbackInfo<v8::Value>& args) {
   NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes();
 
-  float scale_factor =
-      ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back());
+  float scale_factor = ui::GetScaleForResourceScaleFactor(
+      ui::GetSupportedResourceScaleFactors().back());
 
   v8::Isolate* isolate = GetIsolate();
   v8::HandleScope handle_scope(isolate);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index c2c7159..5ee912d 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -5217,6 +5217,8 @@
     "//v8",
   ]
 
+  public_deps = []
+
   if (enable_paint_preview) {
     deps += [ "//chrome/browser/paint_preview:services" ]
   }
@@ -6954,6 +6956,9 @@
 
       if (is_win) {
         sources += [ "../browser/ui/webui/print_preview/pdf_printer_handler_win_unittest.cc" ]
+
+        # Needed by test_print_job.h, test_printer_query.h.
+        public_deps += [ "//printing/mojom" ]
       }
 
       if (is_mac) {
diff --git a/chrome/test/chromedriver/capabilities.cc b/chrome/test/chromedriver/capabilities.cc
index 1cb7e18..be1ccfd 100644
--- a/chrome/test/chromedriver/capabilities.cc
+++ b/chrome/test/chromedriver/capabilities.cc
@@ -519,13 +519,11 @@
 
 Status ParseDevToolsEventsLoggingPrefs(const base::Value& option,
                                        Capabilities* capabilities) {
-  const base::ListValue* devtools_events_logging_prefs = nullptr;
-  if (!option.GetAsList(&devtools_events_logging_prefs))
+  if (!option.is_list())
     return Status(kInvalidArgument, "must be a list");
-  if (devtools_events_logging_prefs->empty())
+  if (option.GetList().empty())
     return Status(kInvalidArgument, "list must contain values");
-  capabilities->devtools_events_logging_prefs.reset(
-      devtools_events_logging_prefs->DeepCopy());
+  capabilities->devtools_events_logging_prefs = option.Clone();
   return Status(kOk);
 }
 
diff --git a/chrome/test/chromedriver/capabilities.h b/chrome/test/chromedriver/capabilities.h
index 271444f..4dd2f54 100644
--- a/chrome/test/chromedriver/capabilities.h
+++ b/chrome/test/chromedriver/capabilities.h
@@ -15,6 +15,7 @@
 
 #include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "base/values.h"
 #include "chrome/test/chromedriver/chrome/device_metrics.h"
 #include "chrome/test/chromedriver/chrome/devtools_http_client.h"
 #include "chrome/test/chromedriver/chrome/log.h"
@@ -23,8 +24,6 @@
 
 namespace base {
 class CommandLine;
-class DictionaryValue;
-class ListValue;
 }
 
 class Status;
@@ -177,7 +176,7 @@
 
   PerfLoggingPrefs perf_logging_prefs;
 
-  std::unique_ptr<base::ListValue> devtools_events_logging_prefs;
+  base::Value devtools_events_logging_prefs;
 
   std::unique_ptr<base::DictionaryValue> prefs;
 
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index 88e058a..a17d76c7 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -810,7 +810,7 @@
   base::ListValue* cookies_tmp;
   if (!result->GetList("cookies", &cookies_tmp))
     return Status(kUnknownError, "DevTools didn't return cookies");
-  cookies->reset(cookies_tmp->DeepCopy());
+  *cookies = cookies_tmp->CreateDeepCopy();
   return Status(kOk);
 }
 
diff --git a/chrome/test/chromedriver/devtools_events_logger.cc b/chrome/test/chromedriver/devtools_events_logger.cc
index e068147..6962b41f 100644
--- a/chrome/test/chromedriver/devtools_events_logger.cc
+++ b/chrome/test/chromedriver/devtools_events_logger.cc
@@ -9,15 +9,13 @@
 #include "chrome/test/chromedriver/chrome/devtools_client.h"
 #include "chrome/test/chromedriver/chrome/devtools_client_impl.h"
 
-DevToolsEventsLogger::DevToolsEventsLogger(Log* log,
-                                           const base::ListValue* prefs)
-    : log_(log),
-      prefs_(prefs) {}
+DevToolsEventsLogger::DevToolsEventsLogger(Log* log, const base::Value& prefs)
+    : log_(log), prefs_(prefs) {}
 
 inline DevToolsEventsLogger::~DevToolsEventsLogger() {}
 
 Status DevToolsEventsLogger::OnConnected(DevToolsClient* client) {
-  for (const auto& entry : prefs_->GetList()) {
+  for (const auto& entry : prefs_.GetList()) {
     std::string event;
     entry.GetAsString(&event);
     events_.insert(event);
diff --git a/chrome/test/chromedriver/devtools_events_logger.h b/chrome/test/chromedriver/devtools_events_logger.h
index 570e7e56..496f289 100644
--- a/chrome/test/chromedriver/devtools_events_logger.h
+++ b/chrome/test/chromedriver/devtools_events_logger.h
@@ -25,7 +25,7 @@
 class DevToolsEventsLogger : public DevToolsEventListener {
  public:
   // Creates a |DevToolsEventsLogger| with specific preferences.
-  DevToolsEventsLogger(Log* log, const base::ListValue* prefs);
+  DevToolsEventsLogger(Log* log, const base::Value& prefs);
 
   ~DevToolsEventsLogger() override;
 
@@ -38,7 +38,7 @@
  private:
   Log* log_;  // The log where to create entries.
 
-  const base::ListValue* prefs_;
+  const base::Value& prefs_;
   std::unordered_set<std::string> events_;
 
   DISALLOW_COPY_AND_ASSIGN(DevToolsEventsLogger);
diff --git a/chrome/test/chromedriver/logging.cc b/chrome/test/chromedriver/logging.cc
index 79e6f9c2..a4f2f19 100644
--- a/chrome/test/chromedriver/logging.cc
+++ b/chrome/test/chromedriver/logging.cc
@@ -388,10 +388,8 @@
       }
     } else if (type == WebDriverLog::kDevToolsType) {
       logs.push_back(std::make_unique<WebDriverLog>(type, Log::kAll));
-      devtools_listeners.push_back(
-          std::make_unique<DevToolsEventsLogger>(
-            logs.back().get(),
-            capabilities.devtools_events_logging_prefs.get()));
+      devtools_listeners.push_back(std::make_unique<DevToolsEventsLogger>(
+          logs.back().get(), capabilities.devtools_events_logging_prefs));
     } else if (type == WebDriverLog::kBrowserType) {
       browser_log_level = level;
     } else if (type != WebDriverLog::kDriverType) {
diff --git a/chrome/test/chromedriver/util_unittest.cc b/chrome/test/chromedriver/util_unittest.cc
index 79ae394..976f07b2 100644
--- a/chrome/test/chromedriver/util_unittest.cc
+++ b/chrome/test/chromedriver/util_unittest.cc
@@ -292,10 +292,10 @@
   base::ListValue lv2;
   lv2.AppendString("2");
 
-  std::unique_ptr<base::ListValue> params(lv1.DeepCopy());
+  base::Value params = lv1.Clone();
 
   base::DictionaryValue dict;
-  dict.SetList(key, std::move(params));
+  dict.SetPath(key, std::move(params));
   const base::ListValue* res = &lv2;
   bool has_value;
   bool has_dict = GetOptionalList(&dict, key, &res, &has_value);
diff --git a/chrome/test/data/pdf/basic_test.js b/chrome/test/data/pdf/basic_test.js
index 3cc35497..5cd05ec 100644
--- a/chrome/test/data/pdf/basic_test.js
+++ b/chrome/test/data/pdf/basic_test.js
@@ -32,7 +32,7 @@
     chrome.test.assertEq('embed', plugin.localName);
 
     chrome.test.assertTrue(
-        plugin.getAttribute('src').indexOf('/pdf/test.pdf') !== -1);
+        plugin.getAttribute('original-url').indexOf('/pdf/test.pdf') !== -1);
     chrome.test.succeed();
   },
 
diff --git a/chrome/test/data/pdf/redirects_fail_test.js b/chrome/test/data/pdf/redirects_fail_test.js
index 5f78818..82df54c 100644
--- a/chrome/test/data/pdf/redirects_fail_test.js
+++ b/chrome/test/data/pdf/redirects_fail_test.js
@@ -16,8 +16,8 @@
     }
   }, false);
 
-  plugin.setAttribute('src', url);
-  plugin.setAttribute('stream-url', streamUrl);
+  plugin.setAttribute('original-url', url);
+  plugin.setAttribute('src', streamUrl);
   plugin.setAttribute('full-frame', '');
   plugin.setAttribute('headers', headers);
   document.body.appendChild(plugin);
diff --git a/chrome/test/data/xr/e2e_test_files/html/webxr_test_basic_all_ar_features.html b/chrome/test/data/xr/e2e_test_files/html/webxr_test_basic_all_ar_features.html
new file mode 100644
index 0000000..bf7db17
--- /dev/null
+++ b/chrome/test/data/xr/e2e_test_files/html/webxr_test_basic_all_ar_features.html
@@ -0,0 +1,66 @@
+<!--
+Tests that AR session is stable for specified amount of time when all features
+are enabled.
+-->
+<html>
+  <head>
+    <link rel="stylesheet" type="text/css" href="../resources/webxr_e2e.css">
+    <meta name="timeout" content="long">  <!-- this is a long-running test! -->
+  </head>
+  <body>
+    <canvas id="webgl-canvas"></canvas>
+    <script src="../../../../../../third_party/blink/web_tests/resources/testharness.js"></script>
+    <script src="../resources/webxr_e2e.js"></script>
+    <script>var shouldAutoCreateNonImmersiveSession = false;</script>
+    <script src="../resources/webxr_boilerplate.js"></script>
+    <script>
+      immersiveArSessionInit.requiredFeatures = [
+        'anchors', 'camera-access', 'depth-sensing', 'hit-test', 'light-estimation', 'plane-detection',
+      ];
+
+      // Depth sensing needs to be configured:
+      immersiveArSessionInit.depthSensing = {
+        usagePreference: ['cpu-optimized'],
+        dataFormatPreference: ['luminance-alpha'],
+      };
+
+      setup({single_test: true});
+
+      function disableCameraAccess() {
+        const indexToRemove = immersiveArSessionInit.requiredFeatures.indexOf("camera-access");
+        if(indexToRemove != -1) {
+          immersiveArSessionInit.requiredFeatures.splice(indexToRemove, 1);
+        }
+      }
+
+      function stepStartTest(durationInSeconds) {
+
+        updateSingleTestProgressMessage("state: starting test, will run for: " + durationInSeconds + "s");
+
+        let firstFrameTime = null;
+        let frame_num = 0;
+
+        onARFrameCallback = (session, frame, t) => {
+          if (firstFrameTime == null) {
+            updateSingleTestProgressMessage("state: first frame received, timestamp: " + t);
+            firstFrameTime = t;
+          }
+
+          const timePassedInMs = t - firstFrameTime;
+
+          frame_num++;
+          if (frame_num % 120) {
+            updateSingleTestProgressMessage("state: waiting for tests to finish, time passed (ms): " + timePassedInMs + ", desired duration (s): " + durationInSeconds);
+          }
+
+          if (timePassedInMs > durationInSeconds * 1000) {
+            updateSingleTestProgressMessage("state: test completed");
+
+            onARFrameCallback = null;
+            done();
+          }
+        };
+      }
+    </script>
+  </body>
+</html>
diff --git a/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js b/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
index f17c2da..8ff4238 100644
--- a/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
+++ b/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
@@ -220,7 +220,7 @@
       break;
     case sessionTypes.AR:
       if (onARFrameCallback) {
-        onARFrameCallback(session, frame);
+        onARFrameCallback(session, frame, t);
       }
       break;
     default:
diff --git a/chrome/updater/crash_client.h b/chrome/updater/crash_client.h
index f96ee05..173f1f4 100644
--- a/chrome/updater/crash_client.h
+++ b/chrome/updater/crash_client.h
@@ -8,13 +8,9 @@
 #include <memory>
 #include <string>
 
+#include "base/no_destructor.h"
 #include "base/sequence_checker.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 namespace crashpad {
 class CrashReportDatabase;
 }  // namespace crashpad
diff --git a/chrome/updater/policy/dm_policy_manager.cc b/chrome/updater/policy/dm_policy_manager.cc
index dcb23966..c786299 100644
--- a/chrome/updater/policy/dm_policy_manager.cc
+++ b/chrome/updater/policy/dm_policy_manager.cc
@@ -57,7 +57,7 @@
 DMPolicyManager::~DMPolicyManager() = default;
 
 bool DMPolicyManager::IsManaged() const {
-  return true;
+  return base::IsMachineExternallyManaged();
 }
 
 std::string DMPolicyManager::source() const {
diff --git a/chrome/updater/policy/dm_policy_manager_unittest.cc b/chrome/updater/policy/dm_policy_manager_unittest.cc
index 1999b0d..4e04e1c 100644
--- a/chrome/updater/policy/dm_policy_manager_unittest.cc
+++ b/chrome/updater/policy/dm_policy_manager_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/policy/dm_policy_manager.h"
 
+#include "base/enterprise_util.h"
 #include "build/build_config.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/unittest_util.h"
@@ -107,7 +108,7 @@
 
   auto policy_manager(std::make_unique<DMPolicyManager>(omaha_settings));
 
-  EXPECT_TRUE(policy_manager->IsManaged());
+  EXPECT_EQ(policy_manager->IsManaged(), base::IsMachineExternallyManaged());
   EXPECT_EQ(policy_manager->source(), "DeviceManagement");
 
   int last_check_period_minutes = 0;
@@ -181,7 +182,7 @@
 
   auto policy_manager(std::make_unique<DMPolicyManager>(omaha_settings));
 
-  EXPECT_TRUE(policy_manager->IsManaged());
+  EXPECT_EQ(policy_manager->IsManaged(), base::IsMachineExternallyManaged());
   EXPECT_EQ(policy_manager->source(), "DeviceManagement");
 
   int last_check_period_minutes = 0;
@@ -271,7 +272,7 @@
 
   auto policy_manager(std::make_unique<DMPolicyManager>(omaha_settings));
 
-  EXPECT_TRUE(policy_manager->IsManaged());
+  EXPECT_EQ(policy_manager->IsManaged(), base::IsMachineExternallyManaged());
   EXPECT_EQ(policy_manager->source(), "DeviceManagement");
 
   int last_check_period_minutes = 0;
diff --git a/chrome/updater/policy/mac/managed_preference_policy_manager_impl.mm b/chrome/updater/policy/mac/managed_preference_policy_manager_impl.mm
index 20803cb..38e2795 100644
--- a/chrome/updater/policy/mac/managed_preference_policy_manager_impl.mm
+++ b/chrome/updater/policy/mac/managed_preference_policy_manager_impl.mm
@@ -4,6 +4,7 @@
 
 #import "chrome/updater/policy/mac/managed_preference_policy_manager_impl.h"
 
+#include "base/enterprise_util.h"
 #include "base/mac/scoped_nsobject.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/policy/manager.h"
@@ -216,7 +217,7 @@
 
 - (instancetype)initWithDictionary:(CRUUpdatePolicyDictionary*)policies {
   if (([super init])) {
-    _managed = (policies.count > 0);
+    _managed = policies.count > 0 && base::IsMachineExternallyManaged();
 
     // Always create a global policy instance for default values.
     _globalPolicy.reset([[CRUManagedPreferenceGlobalPolicySettings alloc]
diff --git a/chrome/updater/policy/mac/managed_preference_policy_manager_impl_unittest.mm b/chrome/updater/policy/mac/managed_preference_policy_manager_impl_unittest.mm
index 34639989..816692a7 100644
--- a/chrome/updater/policy/mac/managed_preference_policy_manager_impl_unittest.mm
+++ b/chrome/updater/policy/mac/managed_preference_policy_manager_impl_unittest.mm
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/policy/mac/managed_preference_policy_manager_impl.h"
 
+#include "base/enterprise_util.h"
 #include "base/mac/scoped_nsobject.h"
 #include "chrome/updater/constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -33,7 +34,7 @@
       [[CRUManagedPreferencePolicyManager alloc]
           initWithDictionary:policyDict]);
   EXPECT_NSEQ([policyManager source], @"ManagedPreference");
-  EXPECT_TRUE([policyManager managed]);
+  EXPECT_EQ([policyManager managed], base::IsMachineExternallyManaged());
 
   // Verify global level policies.
   EXPECT_EQ([policyManager lastCheckPeriodMinutes], kPolicyNotSet);
@@ -77,7 +78,7 @@
       [[CRUManagedPreferencePolicyManager alloc]
           initWithDictionary:policyDict]);
   EXPECT_NSEQ([policyManager source], @"ManagedPreference");
-  EXPECT_TRUE([policyManager managed]);
+  EXPECT_EQ([policyManager managed], base::IsMachineExternallyManaged());
 
   // Verify global level policies are set to default.
   EXPECT_EQ([policyManager lastCheckPeriodMinutes], kPolicyNotSet);
@@ -116,7 +117,7 @@
       [[CRUManagedPreferencePolicyManager alloc]
           initWithDictionary:policyDict]);
   EXPECT_NSEQ([policyManager source], @"ManagedPreference");
-  EXPECT_TRUE([policyManager managed]);
+  EXPECT_EQ([policyManager managed], base::IsMachineExternallyManaged());
 
   // Verify global level policies.
   EXPECT_EQ([policyManager lastCheckPeriodMinutes], kPolicyNotSet);
diff --git a/chrome/updater/policy/win/group_policy_manager.cc b/chrome/updater/policy/win/group_policy_manager.cc
index a69fb8b0..488ebb3 100644
--- a/chrome/updater/policy/win/group_policy_manager.cc
+++ b/chrome/updater/policy/win/group_policy_manager.cc
@@ -9,11 +9,11 @@
 
 #include <userenv.h>
 
+#include "base/enterprise_util.h"
 #include "base/scoped_generic.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/values.h"
 #include "base/win/registry.h"
-#include "base/win/win_util.h"
 #include "chrome/updater/policy/manager.h"
 #include "chrome/updater/win/win_constants.h"
 
@@ -81,7 +81,7 @@
 GroupPolicyManager::~GroupPolicyManager() = default;
 
 bool GroupPolicyManager::IsManaged() const {
-  return policies_.DictSize() > 0 && base::win::IsEnrolledToDomain();
+  return policies_.DictSize() > 0 && base::IsMachineExternallyManaged();
 }
 
 std::string GroupPolicyManager::source() const {
@@ -203,7 +203,7 @@
 void GroupPolicyManager::LoadAllPolicies() {
   scoped_hpolicy policy_lock;
 
-  if (base::win::IsEnrolledToDomain()) {
+  if (base::IsMachineExternallyManaged()) {
     // GPO rules mandate a call to EnterCriticalPolicySection() before reading
     // policies (and a matching LeaveCriticalPolicySection() call after read).
     // Acquire the lock for domain-joined machines because group policies are
diff --git a/chrome/updater/service_factory_win.cc b/chrome/updater/service_factory_win.cc
index 304dd85..1ded800a 100644
--- a/chrome/updater/service_factory_win.cc
+++ b/chrome/updater/service_factory_win.cc
@@ -4,7 +4,6 @@
 
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
-#include "base/no_destructor.h"
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/win/update_service_internal_proxy.h"
 #include "chrome/updater/win/update_service_proxy.h"
@@ -22,8 +21,8 @@
   }
 
   static const WRLModuleInitializer& Get() {
-    static const base::NoDestructor<WRLModuleInitializer> module;
-    return *module;
+    static const WRLModuleInitializer module;
+    return module;
   }
 };
 
diff --git a/chromecast/browser/cast_content_browser_client_receiver_bindings.cc b/chromecast/browser/cast_content_browser_client_receiver_bindings.cc
index f97f4c8..a748722 100644
--- a/chromecast/browser/cast_content_browser_client_receiver_bindings.cc
+++ b/chromecast/browser/cast_content_browser_client_receiver_bindings.cc
@@ -10,7 +10,6 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
-#include "base/no_destructor.h"
 #include "base/threading/sequence_local_storage_slot.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
@@ -171,10 +170,8 @@
   mojo_media_client->SetVideoGeometrySetterService(
       video_geometry_setter_service_.get());
 
-  static base::NoDestructor<
-      base::SequenceLocalStorageSlot<::media::MediaService>>
-      service;
-  service->emplace(std::move(mojo_media_client), std::move(receiver));
+  static base::SequenceLocalStorageSlot<::media::MediaService> service;
+  service.emplace(std::move(mojo_media_client), std::move(receiver));
 }
 
 void CastContentBrowserClient::CreateVideoGeometrySetterServiceOnMediaThread() {
diff --git a/chromecast/browser/system_connector.cc b/chromecast/browser/system_connector.cc
index f104af4..8b117d8 100644
--- a/chromecast/browser/system_connector.cc
+++ b/chromecast/browser/system_connector.cc
@@ -5,7 +5,6 @@
 #include "chromecast/browser/system_connector.h"
 
 #include "base/check_op.h"
-#include "base/no_destructor.h"
 #include "base/threading/sequence_local_storage_slot.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -18,10 +17,8 @@
 
 base::SequenceLocalStorageSlot<service_manager::Connector>&
 GetConnectorStorage() {
-  static base::NoDestructor<
-      base::SequenceLocalStorageSlot<service_manager::Connector>>
-      storage;
-  return *storage;
+  static base::SequenceLocalStorageSlot<service_manager::Connector> storage;
+  return storage;
 }
 
 void BindReceiverOnMainThread(
diff --git a/chromecast/browser/ui/aura/accessibility/automation_manager_aura.h b/chromecast/browser/ui/aura/accessibility/automation_manager_aura.h
index 88aa909d..2c44f86 100644
--- a/chromecast/browser/ui/aura/accessibility/automation_manager_aura.h
+++ b/chromecast/browser/ui/aura/accessibility/automation_manager_aura.h
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.h"
 #include "ui/accessibility/ax_action_handler.h"
 #include "ui/accessibility/ax_tree_serializer.h"
@@ -22,11 +23,6 @@
 
 class AXRootObjWrapper;
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 namespace extensions {
 class AutomationEventRouterInterface;
 }
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 290f703..0ef0bbb 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -911,6 +911,12 @@
       <message name="IDS_DIAGNOSTICS_NO_ETHERNET" desc="The label for the text displayed when a dongle is connected to a device but an Ethernet connection is not detected.">
         No Ethernet connection detected
       </message>
+      <message name="IDS_DIAGNOSTICS_OVERVIEW" desc="The label for the overview navigation panel item.">
+        Overview
+      </message>
+      <message name="IDS_DIAGNOSTICS_CONNECTIVITY" desc="The label for the connectivity navigation panel item.">
+        Connectivity
+      </message>
 
       <message name="IDS_ECHE_APP_DEFAULT_DEVICE_NAME" desc="The default device name used to display on Android endpoint of Eche app.">
         <ph name="GIVEN_NAME">$1<ex>Josh</ex></ph>'s <ph name="DEVICE_TYPE">$2<ex>Chromebook</ex></ph>
diff --git a/chromeos/chromeos_strings_grd/IDS_DIAGNOSTICS_CONNECTIVITY.png.sha1 b/chromeos/chromeos_strings_grd/IDS_DIAGNOSTICS_CONNECTIVITY.png.sha1
new file mode 100644
index 0000000..5a01d53
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_DIAGNOSTICS_CONNECTIVITY.png.sha1
@@ -0,0 +1 @@
+cc40055130fdba6aec5141fbb40958a2f5e2fae4
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_DIAGNOSTICS_OVERVIEW.png.sha1 b/chromeos/chromeos_strings_grd/IDS_DIAGNOSTICS_OVERVIEW.png.sha1
new file mode 100644
index 0000000..5a01d53
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_DIAGNOSTICS_OVERVIEW.png.sha1
@@ -0,0 +1 @@
+cc40055130fdba6aec5141fbb40958a2f5e2fae4
\ No newline at end of file
diff --git a/chromeos/components/cdm_factory_daemon/output_protection_impl.cc b/chromeos/components/cdm_factory_daemon/output_protection_impl.cc
index 51b5e23d..02fe6b38 100644
--- a/chromeos/components/cdm_factory_daemon/output_protection_impl.cc
+++ b/chromeos/components/cdm_factory_daemon/output_protection_impl.cc
@@ -16,7 +16,6 @@
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "ui/display/manager/display_configurator.h"
 #include "ui/display/manager/display_manager.h"
-#include "ui/display/screen.h"
 #include "ui/display/types/display_constants.h"
 
 namespace chromeos {
@@ -115,12 +114,6 @@
       display::ContentProtectionManager::ClientId client_id) override {
     content_protection_manager_->UnregisterClient(client_id);
   }
-  void AddObserver(display::DisplayObserver* observer) override {
-    display::Screen::GetScreen()->AddObserver(observer);
-  }
-  void RemoveObserver(display::DisplayObserver* observer) override {
-    display::Screen::GetScreen()->RemoveObserver(observer);
-  }
   const std::vector<display::DisplaySnapshot*>& cached_displays()
       const override {
     return display_configurator_->cached_displays();
@@ -174,10 +167,8 @@
 
 OutputProtectionImpl::~OutputProtectionImpl() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (client_id_) {
-    delegate_->RemoveObserver(this);
+  if (client_id_)
     delegate_->UnregisterClient(client_id_);
-  }
 }
 
 void OutputProtectionImpl::QueryStatus(QueryStatusCallback callback) {
@@ -247,7 +238,7 @@
   // are on that thread (i.e. don't do it in the constructor).
   client_id_ = delegate_->RegisterClient();
   DCHECK(client_id_);
-  delegate_->AddObserver(this);
+  display_observer_.emplace(this);
   display_id_list_ = GetDisplayIdsFromSnapshots(delegate_->cached_displays());
 }
 
diff --git a/chromeos/components/cdm_factory_daemon/output_protection_impl.h b/chromeos/components/cdm_factory_daemon/output_protection_impl.h
index 451bbe4c..24632aa 100644
--- a/chromeos/components/cdm_factory_daemon/output_protection_impl.h
+++ b/chromeos/components/cdm_factory_daemon/output_protection_impl.h
@@ -47,10 +47,6 @@
     virtual void UnregisterClient(
         display::ContentProtectionManager::ClientId client_id) = 0;
 
-    // Delegate to ash::screen::GetScreen().
-    virtual void AddObserver(display::DisplayObserver* observer) = 0;
-    virtual void RemoveObserver(display::DisplayObserver* observer) = 0;
-
     // Delegate to display::DisplayConfigurator.
     virtual const std::vector<display::DisplaySnapshot*>& cached_displays()
         const = 0;
@@ -113,6 +109,8 @@
   std::unique_ptr<DisplaySystemDelegate> delegate_;
   display::ContentProtectionManager::ClientId client_id_;
 
+  absl::optional<display::ScopedOptionalDisplayObserver> display_observer_;
+
   std::vector<int64_t> display_id_list_;
 
   uint32_t desired_protection_mask_{0};
diff --git a/chromeos/components/cdm_factory_daemon/output_protection_impl_unittest.cc b/chromeos/components/cdm_factory_daemon/output_protection_impl_unittest.cc
index c347e31..d102b9c 100644
--- a/chromeos/components/cdm_factory_daemon/output_protection_impl_unittest.cc
+++ b/chromeos/components/cdm_factory_daemon/output_protection_impl_unittest.cc
@@ -51,8 +51,6 @@
   MOCK_METHOD(void,
               UnregisterClient,
               (display::ContentProtectionManager::ClientId));
-  MOCK_METHOD(void, AddObserver, (display::DisplayObserver*));
-  MOCK_METHOD(void, RemoveObserver, (display::DisplayObserver*));
   MOCK_METHOD(const std::vector<display::DisplaySnapshot*>&,
               cached_displays,
               (),
@@ -89,7 +87,6 @@
 
     EXPECT_CALL(*delegate_, RegisterClient())
         .WillOnce(Return(absl::optional<uint64_t>(kFakeClientId)));
-    EXPECT_CALL(*delegate_, AddObserver(_));
   }
 
   void UpdateDisplays(size_t count) {
@@ -102,7 +99,6 @@
 
   ~OutputProtectionImplTest() override {
     EXPECT_CALL(*delegate_, UnregisterClient(_));
-    EXPECT_CALL(*delegate_, RemoveObserver(_));
     output_protection_mojo_.reset();
     base::RunLoop().RunUntilIdle();
   }
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 3d10666..ba1ad24 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -84,7 +84,6 @@
     "//components/content_settings/core/common:unit_tests",
     "//components/crash/core/common:unit_tests",
     "//components/crx_file:unit_tests",
-    "//components/desks_storage:unit_tests",
     "//components/device_event_log:unit_tests",
     "//components/dom_distiller/core:unit_tests",
     "//components/download:unit_tests",
@@ -461,6 +460,7 @@
     deps += [
       "//components/arc:unit_tests",
       "//components/arc/mojom:unit_tests",
+      "//components/desks_storage:unit_tests",
       "//components/guest_os:unit_tests",
       "//components/metrics/structured:unit_tests",
       "//components/ownership:unit_tests",
diff --git a/components/autofill_assistant/browser/autofill_assistant_onboarding_fetcher.cc b/components/autofill_assistant/browser/autofill_assistant_onboarding_fetcher.cc
index aee9564..9deafb43 100644
--- a/components/autofill_assistant/browser/autofill_assistant_onboarding_fetcher.cc
+++ b/components/autofill_assistant/browser/autofill_assistant_onboarding_fetcher.cc
@@ -8,7 +8,6 @@
 #include "base/containers/flat_map.h"
 #include "base/json/json_reader.h"
 #include "base/metrics/histogram_functions.h"
-#include "base/no_destructor.h"
 #include "base/strings/string_util.h"
 #include "components/autofill_assistant/browser/features.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -81,7 +80,7 @@
 
 void AutofillAssistantOnboardingFetcher::StartFetch(const std::string& locale,
                                                     int timeout_ms) {
-  static const base::NoDestructor<base::TimeDelta> kFetchTimeout(
+  static const base::TimeDelta kFetchTimeout(
       base::TimeDelta::FromMilliseconds(timeout_ms));
   if (url_loader_) {
     return;
@@ -96,7 +95,7 @@
                                           kTrafficAnnotationDefinition);
   url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
                                                  traffic_annotation);
-  url_loader_->SetTimeoutDuration(*kFetchTimeout);
+  url_loader_->SetTimeoutDuration(kFetchTimeout);
   url_loader_->DownloadToString(
       url_loader_factory_.get(),
       base::BindOnce(&AutofillAssistantOnboardingFetcher::OnFetchComplete,
diff --git a/components/browser_sync/BUILD.gn b/components/browser_sync/BUILD.gn
index 291f1fa..c92f09d 100644
--- a/components/browser_sync/BUILD.gn
+++ b/components/browser_sync/BUILD.gn
@@ -25,7 +25,6 @@
     "//build:chromeos_buildflags",
     "//components/autofill/core/browser",
     "//components/autofill/core/common",
-    "//components/desks_storage",
     "//components/history/core/browser",
     "//components/history/core/common",
     "//components/password_manager/core/browser",
@@ -42,7 +41,10 @@
   ]
 
   if (is_chromeos_ash) {
-    deps += [ "//ash/constants" ]
+    deps += [
+      "//ash/constants",
+      "//components/desks_storage",
+    ]
   }
 }
 
diff --git a/components/browser_ui/styles/android/java/res/values-night/styles.xml b/components/browser_ui/styles/android/java/res/values-night/styles.xml
index bdf87785..0ff8180 100644
--- a/components/browser_ui/styles/android/java/res/values-night/styles.xml
+++ b/components/browser_ui/styles/android/java/res/values-night/styles.xml
@@ -16,6 +16,7 @@
         <item name="android:colorBackground">@color/modern_grey_900</item>
         <item name="colorSurface">@color/modern_grey_900</item>
         <item name="colorOnBackground">@color/modern_white</item>
+        <item name="colorOnPrimary">@color/modern_blue_800</item>
         <item name="colorOnSurface">@color/modern_grey_100</item>
         <item name="colorOnSurfaceVariant">@color/white_alpha_70</item>
         <item name="colorOnSurfaceInverse">@color/modern_grey_800</item>
diff --git a/components/browser_ui/styles/android/java/res/values/styles.xml b/components/browser_ui/styles/android/java/res/values/styles.xml
index 39a8887f..b1b28c5 100644
--- a/components/browser_ui/styles/android/java/res/values/styles.xml
+++ b/components/browser_ui/styles/android/java/res/values/styles.xml
@@ -201,6 +201,7 @@
         <item name="colorSurface">@color/modern_white</item>
         <item name="colorSurfaceVariant">@color/neutral_variant_100</item>
         <item name="colorOnBackground">@color/modern_grey_900</item>
+        <item name="colorOnPrimary">@color/modern_white</item>
         <item name="colorOnSurface">@color/modern_grey_900</item>
         <item name="colorOnSurfaceVariant">@color/modern_grey_700</item>
         <item name="colorOnSurfaceInverse">@color/modern_white</item>
diff --git a/components/component_updater/installer_policies/origin_trials_component_installer.cc b/components/component_updater/installer_policies/origin_trials_component_installer.cc
index e24a05af..b74c94f 100644
--- a/components/component_updater/installer_policies/origin_trials_component_installer.cc
+++ b/components/component_updater/installer_policies/origin_trials_component_installer.cc
@@ -72,7 +72,7 @@
 
 bool OriginTrialsComponentInstallerPolicy::
     SupportsGroupPolicyEnabledComponentUpdates() const {
-  return false;
+  return true;
 }
 
 bool OriginTrialsComponentInstallerPolicy::RequiresNetworkEncryption() const {
diff --git a/components/crash/content/app/breakpad_win.cc b/components/crash/content/app/breakpad_win.cc
index 4c79b7e..0502be7 100644
--- a/components/crash/content/app/breakpad_win.cc
+++ b/components/crash/content/app/breakpad_win.cc
@@ -156,12 +156,11 @@
     }
   }
 
-  static base::NoDestructor<google_breakpad::CustomClientInfo>
-      custom_client_info;
-  custom_client_info->entries = &custom_entries->front();
-  custom_client_info->count = custom_entries->size();
+  static google_breakpad::CustomClientInfo custom_client_info;
+  custom_client_info.entries = &custom_entries->front();
+  custom_client_info.count = custom_entries->size();
 
-  return custom_client_info.get();
+  return &custom_client_info;
 }
 
 }  // namespace
diff --git a/components/cronet/stale_host_resolver.cc b/components/cronet/stale_host_resolver.cc
index ad56a6c..2dd046a 100644
--- a/components/cronet/stale_host_resolver.cc
+++ b/components/cronet/stale_host_resolver.cc
@@ -4,6 +4,7 @@
 
 #include "components/cronet/stale_host_resolver.h"
 
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
@@ -19,9 +20,12 @@
 #include "net/base/network_isolation_key.h"
 #include "net/dns/context_host_resolver.h"
 #include "net/dns/dns_util.h"
+#include "net/dns/host_resolver.h"
 #include "net/dns/host_resolver_source.h"
 #include "net/dns/public/resolve_error_info.h"
 #include "net/log/net_log_with_source.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/scheme_host_port.h"
 
 namespace cronet {
 
@@ -328,6 +332,17 @@
 
 std::unique_ptr<net::HostResolver::ResolveHostRequest>
 StaleHostResolver::CreateRequest(
+    url::SchemeHostPort host,
+    net::NetworkIsolationKey network_isolation_key,
+    net::NetLogWithSource net_log,
+    absl::optional<ResolveHostParameters> optional_parameters) {
+  // TODO(crbug.com/1206799): Propagate scheme.
+  return CreateRequest(net::HostPortPair::FromSchemeHostPort(host),
+                       network_isolation_key, net_log, optional_parameters);
+}
+
+std::unique_ptr<net::HostResolver::ResolveHostRequest>
+StaleHostResolver::CreateRequest(
     const net::HostPortPair& host,
     const net::NetworkIsolationKey& network_isolation_key,
     const net::NetLogWithSource& net_log,
diff --git a/components/cronet/stale_host_resolver.h b/components/cronet/stale_host_resolver.h
index 335cbc3..57b88e5 100644
--- a/components/cronet/stale_host_resolver.h
+++ b/components/cronet/stale_host_resolver.h
@@ -11,7 +11,11 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/default_tick_clock.h"
 #include "net/base/completion_once_callback.h"
+#include "net/base/network_isolation_key.h"
 #include "net/dns/host_resolver.h"
+#include "net/log/net_log_with_source.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/scheme_host_port.h"
 
 namespace base {
 class TickClock;
@@ -79,6 +83,11 @@
   // If stale data is returned, the StaleHostResolver allows the underlying
   // request to continue in order to repopulate the cache.
   std::unique_ptr<ResolveHostRequest> CreateRequest(
+      url::SchemeHostPort host,
+      net::NetworkIsolationKey network_isolation_key,
+      net::NetLogWithSource net_log,
+      absl::optional<ResolveHostParameters> optional_parameters) override;
+  std::unique_ptr<ResolveHostRequest> CreateRequest(
       const net::HostPortPair& host,
       const net::NetworkIsolationKey& network_isolation_key,
       const net::NetLogWithSource& net_log,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
index 45b4b76..affe5cd 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
@@ -395,8 +395,7 @@
 
 void DataReductionProxyCompressionStats::InitListPref(const char* pref) {
   std::unique_ptr<base::ListValue> pref_value =
-      std::unique_ptr<base::ListValue>(
-          pref_service_->GetList(pref)->DeepCopy());
+      pref_service_->GetList(pref)->CreateDeepCopy();
   list_pref_map_[pref] = std::move(pref_value);
 }
 
diff --git a/components/desks_storage/BUILD.gn b/components/desks_storage/BUILD.gn
index 8d918d27..3a6203c 100644
--- a/components/desks_storage/BUILD.gn
+++ b/components/desks_storage/BUILD.gn
@@ -13,6 +13,8 @@
     "core/desk_sync_bridge.h",
     "core/desk_template.cc",
     "core/desk_template.h",
+    "core/local_desk_data_manager.cc",
+    "core/local_desk_data_manager.h",
   ]
   deps = [
     "//base",
@@ -28,7 +30,10 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "core/desk_sync_bridge_unittest.cc" ]
+  sources = [
+    "core/desk_sync_bridge_unittest.cc",
+    "core/local_desks_data_manager_unittests.cc",
+  ]
   deps = [
     ":desks_storage",
     "//base",
diff --git a/components/desks_storage/core/desk_template.h b/components/desks_storage/core/desk_template.h
index e13b29f4..69096db 100644
--- a/components/desks_storage/core/desk_template.h
+++ b/components/desks_storage/core/desk_template.h
@@ -18,6 +18,9 @@
 // A desk template being saved. The UUID is a unique identifier for a
 // template. This class is a temporary placeholder. This could be replaced
 // by future ash::DeskTemplate when it is ready.
+//
+// NOTE: This definition will be deleted in an upcoming CL to be replaced by
+// ash::DeskTemplate.
 class DeskTemplate {
  public:
   // Creates a DeskTemplate from the protobuf format.
diff --git a/components/desks_storage/core/local_desk_data_manager.cc b/components/desks_storage/core/local_desk_data_manager.cc
new file mode 100644
index 0000000..799e030
--- /dev/null
+++ b/components/desks_storage/core/local_desk_data_manager.cc
@@ -0,0 +1,262 @@
+// 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 "components/desks_storage/core/local_desk_data_manager.h"
+
+#include "base/files/dir_reader_posix.h"
+#include "base/files/file_util.h"
+#include "base/task/post_task.h"
+#include "base/task/thread_pool.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "components/desks_storage/core/desk_model.h"
+#include "components/desks_storage/core/desk_template.h"
+#include "components/sync/protocol/workspace_desk_specifics.pb.h"
+
+namespace desks_storage {
+
+namespace {
+
+constexpr char kFileExtension[] = ".template";
+
+// WriteTemplateToFile is a method that takes a base::FilePath
+// |path_to_template| and a DeskTemplate unique pointer |entry|
+// and writes the entry out in its serialized form to the path
+// represented by |path_to_template|.
+//
+// WARNING: This private helper function utilizes blocking calls
+// and assumes that it is being called from a thread which can accept
+// such calls, please don't call this function from the main thread.
+bool WriteTemplateFile(const base::FilePath& path_to_template,
+                       std::unique_ptr<DeskTemplate> entry) {
+  std::string proto_string;
+  bool string_conversion_success =
+      entry->AsSyncProto().SerializeToString(&proto_string);
+
+  if (!string_conversion_success)
+    return false;
+
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+  bool write_success = base::WriteFile(path_to_template, proto_string);
+
+  if (!write_success)
+    return false;
+
+  return true;
+}
+
+// AppendTemplateUUID appends a std::string |entry| to a std::vector of uuids.
+// in order to be appended |entry| must contain .template within the string.
+// This method populates the std::vector as a side effect and has a void return
+// type hence |out_uuids|.
+void AppendTemplateUUID(const std::string& entry,
+                        std::vector<std::string>* out_uuids) {
+  const size_t extension_at = entry.find(kFileExtension);
+
+  if (extension_at == std::string::npos)
+    return;
+
+  out_uuids->push_back(entry.substr(0, extension_at));
+}
+
+// returns the fully qualified path to a template file given the file path to
+// the desk template directory.
+base::FilePath GetFullyQualifiedPath(base::FilePath file_path,
+                                     std::string uuid) {
+  std::string filename(uuid);
+  filename.append(kFileExtension);
+  return base::FilePath(
+      file_path.Append(base::FilePath::StringPieceType(filename.c_str())));
+}
+
+struct GetAllUuidsResult {
+  DeskModel::GetAllUuidsStatus status;
+  std::vector<std::string> uuids;
+};
+
+// This method gets all UUIDs available in the template directory.  This
+// is a task that is posted to the local storage object's task runner.
+GetAllUuidsResult GetAllUuidsTask(const base::FilePath local_template_path) {
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+  base::DirReaderPosix dir_reader(local_template_path.AsUTF8Unsafe().c_str());
+  if (!dir_reader.IsValid()) {
+    return {DeskModel::GetAllUuidsStatus::kFailure, {}};
+  }
+
+  std::vector<std::string> uuids;
+  while (dir_reader.Next()) {
+    if (dir_reader.name() == nullptr)
+      continue;
+
+    AppendTemplateUUID(std::string(dir_reader.name()), &uuids);
+  }
+
+  return {DeskModel::GetAllUuidsStatus::kOk, std::move(uuids)};
+}
+
+// Handles GetAllUuidsTask and calls the callback with the result.
+void HandleGetAllUuidsTask(DeskModel::GetAllUuidsCallback callback,
+                           GetAllUuidsResult result) {
+  std::move(callback).Run(result.status, std::move(result.uuids));
+}
+
+// Adds or updates an entry. This is a task that is posted to base::ThreadPool
+// in order to complete io operations.
+DeskModel::AddOrUpdateEntryStatus AddOrUpdateEntryTask(
+    const base::FilePath local_template_path,
+    std::unique_ptr<DeskTemplate> new_entry) {
+  const base::FilePath fully_qualified_path =
+      GetFullyQualifiedPath(local_template_path, new_entry->uuid());
+
+  if (WriteTemplateFile(fully_qualified_path, std::move(new_entry)))
+    return DeskModel::AddOrUpdateEntryStatus::kOk;
+  else
+    return DeskModel::AddOrUpdateEntryStatus::kFailure;
+}
+
+struct GetEntryByUuidResult {
+  DeskModel::GetEntryByUuidStatus status;
+  std::unique_ptr<DeskTemplate> desk_template;
+};
+
+// This method Handles getting the task of getting an entry by it's Uuid. Unlike
+// the other statuses this function returns the DeskTemplate pointer instead of
+// a status.  This is because this method has to instantiate the DeskTemplate
+// itself in order to use the DeskTemplate::FromProto factory method.
+GetEntryByUuidResult GetEntryByUuidTask(
+    const base::FilePath local_template_path,
+    const std::string& uuid) {
+  const base::FilePath fully_qualified_path =
+      GetFullyQualifiedPath(local_template_path, uuid);
+
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+  if (!base::PathExists(fully_qualified_path))
+    return {DeskModel::GetEntryByUuidStatus::kNotFound, nullptr};
+
+  std::string proto_string;
+  bool read_success =
+      base::ReadFileToString(fully_qualified_path, &proto_string);
+
+  if (!read_success)
+    return {DeskModel::GetEntryByUuidStatus::kFailure, nullptr};
+
+  sync_pb::WorkspaceDeskSpecifics desk_proto;
+  bool parse_success = desk_proto.ParseFromString(proto_string);
+
+  if (!parse_success)
+    return {DeskModel::GetEntryByUuidStatus::kFailure, nullptr};
+
+  return {DeskModel::GetEntryByUuidStatus::kOk,
+          DeskTemplate::FromProto(desk_proto)};
+}
+
+// Handles replies from |GetEntryByUuidTask| and calls callback.
+void HandleGetEntryByUuidTask(DeskModel::GetEntryByUuidCallback callback,
+                              GetEntryByUuidResult result) {
+  std::move(callback).Run(result.status, std::move(result.desk_template));
+}
+
+// This task deletes a single entry keyed by its |uuid|.
+DeskModel::DeleteEntryStatus DeleteSingleEntryTask(
+    const base::FilePath local_file_path,
+    const std::string& uuid) {
+  const base::FilePath fully_qualified_path =
+      GetFullyQualifiedPath(local_file_path, uuid);
+
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+  if (base::DeleteFile(fully_qualified_path))
+    return DeskModel::DeleteEntryStatus::kOk;
+
+  return DeskModel::DeleteEntryStatus::kFailure;
+}
+
+// Deletes all entries.
+DeskModel::DeleteEntryStatus DeleteAllEntriesTask(
+    const base::FilePath local_file_path) {
+  base::DirReaderPosix dir_reader(local_file_path.AsUTF8Unsafe().c_str());
+
+  if (!dir_reader.IsValid())
+    return DeskModel::DeleteEntryStatus::kFailure;
+
+  DeskModel::DeleteEntryStatus overall_delete_successes =
+      DeskModel::DeleteEntryStatus::kOk;
+  while (dir_reader.Next()) {
+    if (dir_reader.name() == nullptr)
+      continue;
+
+    std::string filename(dir_reader.name());
+    size_t extension_at = filename.find(kFileExtension);
+
+    if (extension_at == std::string::npos)
+      continue;
+
+    base::FilePath fully_qualified_path(local_file_path.Append(filename));
+    base::ScopedBlockingCall scoped_blocking_call(
+        FROM_HERE, base::BlockingType::MAY_BLOCK);
+    bool delete_success = base::DeleteFile(fully_qualified_path);
+    if ((overall_delete_successes == DeskModel::DeleteEntryStatus::kOk) &&
+        !delete_success)
+      overall_delete_successes = DeskModel::DeleteEntryStatus::kFailure;
+  }
+
+  return overall_delete_successes;
+}
+
+}  // namespace
+
+LocalDeskDataManager::LocalDeskDataManager(const base::FilePath& path)
+    : task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
+          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
+      local_path_(path) {}
+
+LocalDeskDataManager::~LocalDeskDataManager() = default;
+
+void LocalDeskDataManager::AddOrUpdateEntry(
+    std::unique_ptr<DeskTemplate> new_entry,
+    DeskModel::AddOrUpdateEntryCallback callback) {
+  task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(&AddOrUpdateEntryTask, base::FilePath(local_path_),
+                     std::move(new_entry)),
+      std::move(callback));
+}
+
+void LocalDeskDataManager::GetAllUuids(
+    DeskModel::GetAllUuidsCallback callback) {
+  task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE, base::BindOnce(&GetAllUuidsTask, base::FilePath(local_path_)),
+      base::BindOnce(&HandleGetAllUuidsTask, std::move(callback)));
+}
+
+void LocalDeskDataManager::GetEntryByUUID(
+    const std::string& uuid,
+    DeskModel::GetEntryByUuidCallback callback) {
+  task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(&GetEntryByUuidTask, base::FilePath(local_path_), uuid),
+      base::BindOnce(&HandleGetEntryByUuidTask, std::move(callback)));
+}
+
+void LocalDeskDataManager::DeleteEntry(
+    const std::string& uuid,
+    DeskModel::DeleteEntryCallback callback) {
+  task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(&DeleteSingleEntryTask, base::FilePath(local_path_), uuid),
+      std::move(callback));
+}
+
+void LocalDeskDataManager::DeleteAllEntries(
+    DeskModel::DeleteEntryCallback callback) {
+  task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(&DeleteAllEntriesTask, base::FilePath(local_path_)),
+      std::move(callback));
+}
+
+}  // namespace desks_storage
\ No newline at end of file
diff --git a/components/desks_storage/core/local_desk_data_manager.h b/components/desks_storage/core/local_desk_data_manager.h
new file mode 100644
index 0000000..e6429b05
--- /dev/null
+++ b/components/desks_storage/core/local_desk_data_manager.h
@@ -0,0 +1,51 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DESKS_STORAGE_CORE_LOCAL_DESK_DATA_MANAGER_H_
+#define COMPONENTS_DESKS_STORAGE_CORE_LOCAL_DESK_DATA_MANAGER_H_
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "components/desks_storage/core/desk_model.h"
+
+namespace desks_storage {
+
+class DeskTemplate;
+
+// The LocalDeskDataManager is the local storage implementation of
+// the DeskModel interface and handles storage operations for local
+// desk templates.
+class LocalDeskDataManager : public DeskModel {
+ public:
+  explicit LocalDeskDataManager(const base::FilePath& path);
+
+  LocalDeskDataManager(const LocalDeskDataManager&) = delete;
+  LocalDeskDataManager& operator=(const LocalDeskDataManager&) = delete;
+  ~LocalDeskDataManager() override;
+
+  // DeskModel:
+  void GetAllUuids(GetAllUuidsCallback callback) override;
+  void DeleteAllEntries(DeleteEntryCallback callback) override;
+  void GetEntryByUUID(const std::string& uuid,
+                      GetEntryByUuidCallback callback) override;
+  void AddOrUpdateEntry(std::unique_ptr<DeskTemplate> new_entry,
+                        AddOrUpdateEntryCallback callback) override;
+  void DeleteEntry(const std::string& uuid,
+                   DeleteEntryCallback callback) override;
+
+ private:
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  // This file path points to the user data directory's subdirectory:
+  // "/path/to/user/data/dir/templates"
+  const base::FilePath local_path_;
+};
+
+}  // namespace desks_storage
+
+#endif  // COMPONENTS_DESKS_STORAGE_CORE_LOCAL_DESK_DATA_MANAGER_H_
diff --git a/components/desks_storage/core/local_desks_data_manager_unittests.cc b/components/desks_storage/core/local_desks_data_manager_unittests.cc
new file mode 100644
index 0000000..aecf5b94
--- /dev/null
+++ b/components/desks_storage/core/local_desks_data_manager_unittests.cc
@@ -0,0 +1,241 @@
+// 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 "components/desks_storage/core/local_desk_data_manager.h"
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "base/task/post_task.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "components/desks_storage/core/desk_template.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace desks_storage {
+
+namespace {
+
+// Search |uuid_list| for |uuid_query| returns true if found false if not.
+//
+// we don't know what order the dir_reader will read the files back to us so
+// instead of relying on the operation returning the uuids in order we can
+// simply run a linear search for each of the uuids we expect to find.
+//
+// we don't use std::find here because we want to run each member through
+// the string compare method.
+bool FindUuidInUuidList(const std::string& uuid_query,
+                        const std::vector<std::string>& uuid_list) {
+  for (const std::string& uuid : uuid_list) {
+    if (uuid.compare(uuid_query) == 0)
+      return true;
+  }
+
+  return false;
+}
+
+// Verifies that the status passed into it is kOk
+void VerifyEntryAddedCorrectly(DeskModel::AddOrUpdateEntryStatus status) {
+  EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
+}
+
+}  // namespace
+
+class LocalDeskDataManagerTest : public testing::Test {
+ public:
+  LocalDeskDataManagerTest()
+      : sample_desk_template_one_(
+            std::make_unique<DeskTemplate>(std::string("01"),
+                                           std::string("desk_01"),
+                                           base::Time())),
+        sample_desk_template_two_(
+            std::make_unique<DeskTemplate>(std::string("02"),
+                                           std::string("desk_02"),
+                                           base::Time())),
+        sample_desk_template_three_(
+            std::make_unique<DeskTemplate>(std::string("03"),
+                                           std::string("desk_03"),
+                                           base::Time())),
+        modified_sample_desk_template_one_(
+            std::make_unique<DeskTemplate>(std::string("01"),
+                                           std::string("desk_01_mod"),
+                                           base::Time())) {}
+
+  LocalDeskDataManagerTest(const LocalDeskDataManagerTest&) = delete;
+  LocalDeskDataManagerTest& operator=(const LocalDeskDataManagerTest&) = delete;
+
+  ~LocalDeskDataManagerTest() override = default;
+
+  void SetUp() override {
+    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+    testing::Test::SetUp();
+  }
+
+  base::ScopedTempDir temp_dir_;
+  std::unique_ptr<DeskTemplate> sample_desk_template_one_;
+  std::unique_ptr<DeskTemplate> sample_desk_template_two_;
+  std::unique_ptr<DeskTemplate> sample_desk_template_three_;
+  std::unique_ptr<DeskTemplate> modified_sample_desk_template_one_;
+};
+
+TEST_F(LocalDeskDataManagerTest, CanAddEntry) {
+  base::test::TaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::IO);
+
+  LocalDeskDataManager data_manager(temp_dir_.GetPath());
+
+  data_manager.AddOrUpdateEntry(std::move(sample_desk_template_one_),
+                                base::BindOnce(&VerifyEntryAddedCorrectly));
+}
+
+TEST_F(LocalDeskDataManagerTest, CanGetAllUuids) {
+  base::test::TaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::IO);
+
+  LocalDeskDataManager data_manager(temp_dir_.GetPath());
+
+  data_manager.AddOrUpdateEntry(std::move(sample_desk_template_one_),
+                                base::BindOnce(&VerifyEntryAddedCorrectly));
+
+  data_manager.AddOrUpdateEntry(std::move(sample_desk_template_two_),
+                                base::BindOnce(&VerifyEntryAddedCorrectly));
+
+  data_manager.AddOrUpdateEntry(std::move(sample_desk_template_three_),
+                                base::BindOnce(&VerifyEntryAddedCorrectly));
+
+  // Because we're using a SequencedTaskRunner we can assume that all of the
+  // previous tasks have been completed by the time we actually attempt to
+  // read the UUIDs.
+  data_manager.GetAllUuids(
+      base::BindLambdaForTesting([](DeskModel::GetAllUuidsStatus status,
+                                    const std::vector<std::string>& uuids) {
+        EXPECT_EQ(status, DeskModel::GetAllUuidsStatus::kOk);
+        EXPECT_EQ(uuids.size(), static_cast<unsigned long>(3));
+        EXPECT_TRUE(FindUuidInUuidList(std::string("01"), uuids));
+        EXPECT_TRUE(FindUuidInUuidList(std::string("02"), uuids));
+        EXPECT_TRUE(FindUuidInUuidList(std::string("03"), uuids));
+
+        // Sanity check for the search function.
+        EXPECT_FALSE(FindUuidInUuidList(std::string("04"), uuids));
+      }));
+}
+
+TEST_F(LocalDeskDataManagerTest, CanGetEntryByUuid) {
+  base::test::TaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::IO);
+
+  LocalDeskDataManager data_manager(temp_dir_.GetPath());
+
+  data_manager.AddOrUpdateEntry(std::move(sample_desk_template_one_),
+                                base::BindOnce(&VerifyEntryAddedCorrectly));
+
+  data_manager.GetEntryByUUID(
+      std::string("01"),
+      base::BindLambdaForTesting(
+          [](DeskModel::GetEntryByUuidStatus status,
+             std::unique_ptr<DeskTemplate> result_template) {
+            EXPECT_EQ(DeskModel::GetEntryByUuidStatus::kOk, status);
+
+            EXPECT_EQ(result_template->uuid(), std::string("01"));
+            EXPECT_EQ(result_template->name(), std::string("desk_01"));
+            EXPECT_EQ(result_template->created_time(), base::Time());
+          }));
+}
+
+TEST_F(LocalDeskDataManagerTest, GetEntryByUuidFailsIfEntryDoesntExist) {
+  base::test::TaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::IO);
+
+  LocalDeskDataManager data_manager(temp_dir_.GetPath());
+
+  data_manager.GetEntryByUUID(
+      std::string("01"),
+      base::BindLambdaForTesting([](DeskModel::GetEntryByUuidStatus status,
+                                    std::unique_ptr<DeskTemplate> _) {
+        EXPECT_EQ(DeskModel::GetEntryByUuidStatus::kNotFound, status);
+      }));
+}
+
+TEST_F(LocalDeskDataManagerTest, CanUpdateEntry) {
+  base::test::TaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::IO);
+
+  LocalDeskDataManager data_manager(temp_dir_.GetPath());
+
+  data_manager.AddOrUpdateEntry(std::move(sample_desk_template_one_),
+                                base::BindOnce(&VerifyEntryAddedCorrectly));
+
+  data_manager.AddOrUpdateEntry(std::move(modified_sample_desk_template_one_),
+                                base::BindOnce(&VerifyEntryAddedCorrectly));
+
+  data_manager.GetEntryByUUID(
+      std::string("01"),
+      base::BindLambdaForTesting(
+          [](DeskModel::GetEntryByUuidStatus status,
+             std::unique_ptr<DeskTemplate> result_template) {
+            EXPECT_EQ(DeskModel::GetEntryByUuidStatus::kOk, status);
+
+            EXPECT_EQ(result_template->uuid(), std::string("01"));
+            EXPECT_EQ(result_template->name(), std::string("desk_01_mod"));
+            EXPECT_EQ(result_template->created_time(), base::Time());
+          }));
+}
+
+TEST_F(LocalDeskDataManagerTest, CanDeleteEntry) {
+  base::test::TaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::IO);
+
+  LocalDeskDataManager data_manager(temp_dir_.GetPath());
+
+  data_manager.AddOrUpdateEntry(std::move(sample_desk_template_one_),
+                                base::BindOnce(&VerifyEntryAddedCorrectly));
+
+  data_manager.DeleteEntry(
+      std::string("01"),
+      base::BindLambdaForTesting([](DeskModel::DeleteEntryStatus status) {
+        EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
+      }));
+
+  data_manager.GetAllUuids(
+      base::BindLambdaForTesting([](DeskModel::GetAllUuidsStatus status,
+                                    const std::vector<std::string>& uuids) {
+        EXPECT_EQ(status, DeskModel::GetAllUuidsStatus::kOk);
+        EXPECT_EQ(uuids.size(), 0ul);
+      }));
+}
+
+TEST_F(LocalDeskDataManagerTest, CanDeleteAllEntries) {
+  base::test::TaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::IO);
+
+  LocalDeskDataManager data_manager(temp_dir_.GetPath());
+
+  data_manager.AddOrUpdateEntry(std::move(sample_desk_template_one_),
+                                base::BindOnce(&VerifyEntryAddedCorrectly));
+
+  data_manager.AddOrUpdateEntry(std::move(sample_desk_template_two_),
+                                base::BindOnce(&VerifyEntryAddedCorrectly));
+
+  data_manager.AddOrUpdateEntry(std::move(sample_desk_template_three_),
+                                base::BindOnce(&VerifyEntryAddedCorrectly));
+
+  data_manager.DeleteAllEntries(
+      base::BindLambdaForTesting([](DeskModel::DeleteEntryStatus status) {
+        EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
+      }));
+
+  data_manager.GetAllUuids(
+      base::BindLambdaForTesting([](DeskModel::GetAllUuidsStatus status,
+                                    const std::vector<std::string>& uuids) {
+        EXPECT_EQ(status, DeskModel::GetAllUuidsStatus::kOk);
+        EXPECT_EQ(uuids.size(), 0ul);
+      }));
+}
+
+}  // namespace desks_storage
diff --git a/components/download/internal/common/resource_downloader.cc b/components/download/internal/common/resource_downloader.cc
index fe8a1a6..5abe9d6d 100644
--- a/components/download/internal/common/resource_downloader.cc
+++ b/components/download/internal/common/resource_downloader.cc
@@ -180,7 +180,8 @@
   url_loader_factory_->CreateLoaderAndStart(
       url_loader_.BindNewPipeAndPassReceiver(),
       0,  // request_id
-      network::mojom::kURLLoadOptionSendSSLInfoWithResponse,
+      network::mojom::kURLLoadOptionSendSSLInfoWithResponse |
+          network::mojom::kURLLoadOptionSniffMimeType,
       *(resource_request_.get()), std::move(url_loader_client_remote),
       net::MutableNetworkTrafficAnnotationTag(
           download_url_parameters->GetNetworkTrafficAnnotation()));
diff --git a/components/exo/client_controlled_shell_surface_unittest.cc b/components/exo/client_controlled_shell_surface_unittest.cc
index 0553e8a..09ceeee 100644
--- a/components/exo/client_controlled_shell_surface_unittest.cc
+++ b/components/exo/client_controlled_shell_surface_unittest.cc
@@ -1295,7 +1295,7 @@
 
   // Drag the window long enough (pass one fourth of the screen vertical
   // height) to snap the window to splitscreen.
-  shell->overview_controller()->EndOverview();
+  shell->overview_controller()->EndOverview(ash::OverviewEndAction::kTests);
   SendGestureEvents(window, gfx::Point(0, 210));
   EXPECT_EQ(ash::WindowState::Get(window)->GetStateType(),
             WindowStateType::kPrimarySnapped);
diff --git a/components/feed/core/proto/BUILD.gn b/components/feed/core/proto/BUILD.gn
index cf2c20d..5c847c6 100644
--- a/components/feed/core/proto/BUILD.gn
+++ b/components/feed/core/proto/BUILD.gn
@@ -24,6 +24,7 @@
     "v2/wire/client_info.proto",
     "v2/wire/consistency_token.proto",
     "v2/wire/content_id.proto",
+    "v2/wire/content_lifetime.proto",
     "v2/wire/data_operation.proto",
     "v2/wire/device.proto",
     "v2/wire/diagnostic_info.proto",
diff --git a/components/feed/core/proto/v2/wire/content_lifetime.proto b/components/feed/core/proto/v2/wire/content_lifetime.proto
new file mode 100644
index 0000000..4d5cade82
--- /dev/null
+++ b/components/feed/core/proto/v2/wire/content_lifetime.proto
@@ -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.
+
+syntax = "proto2";
+
+package feedwire;
+
+option optimize_for = LITE_RUNTIME;
+
+message ContentLifetime {
+  optional int64 stale_age_ms = 1;
+  optional int64 invalid_age_ms = 2;
+}
diff --git a/components/feed/core/proto/v2/wire/feed_response.proto b/components/feed/core/proto/v2/wire/feed_response.proto
index 575d463..c3324d4 100644
--- a/components/feed/core/proto/v2/wire/feed_response.proto
+++ b/components/feed/core/proto/v2/wire/feed_response.proto
@@ -7,6 +7,7 @@
 package feedwire;
 
 import "components/feed/core/proto/v2/wire/chrome_feed_response_metadata.proto";
+import "components/feed/core/proto/v2/wire/content_lifetime.proto";
 import "components/feed/core/proto/v2/wire/data_operation.proto";
 import "components/feed/core/proto/v2/wire/eventid.proto";
 import "components/feed/core/proto/v2/wire/server_experiment_data.proto";
@@ -22,5 +23,6 @@
   optional EventIdMessage event_id = 2;
   optional bool pinned_content_fulfilled = 4;
   optional ServerExperimentData server_experiment_data = 5;
+  optional ContentLifetime content_lifetime = 6;
   optional ChromeFeedResponseMetadata chrome_feed_response_metadata = 326233599;
 }
diff --git a/components/feed/core/v2/feed_network.h b/components/feed/core/v2/feed_network.h
index 322a080..bb005c2 100644
--- a/components/feed/core/v2/feed_network.h
+++ b/components/feed/core/v2/feed_network.h
@@ -77,7 +77,7 @@
   using Response = feedwire::webfeed::ListWebFeedsResponse;
   static constexpr NetworkRequestType kRequestType =
       NetworkRequestType::kListWebFeeds;
-  static base::StringPiece Method() { return "GET"; }
+  static base::StringPiece Method() { return "POST"; }
   static base::StringPiece RequestPath(const Request&) { return "v1/webFeeds"; }
 };
 
@@ -86,7 +86,7 @@
   using Response = feedwire::webfeed::ListRecommendedWebFeedsResponse;
   static constexpr NetworkRequestType kRequestType =
       NetworkRequestType::kListRecommendedWebFeeds;
-  static base::StringPiece Method() { return "GET"; }
+  static base::StringPiece Method() { return "POST"; }
   static base::StringPiece RequestPath(const Request&) {
     return "v1/recommendedWebFeeds";
   }
diff --git a/components/infobars/core/infobar_delegate.cc b/components/infobars/core/infobar_delegate.cc
index 96a25a9..352970f3 100644
--- a/components/infobars/core/infobar_delegate.cc
+++ b/components/infobars/core/infobar_delegate.cc
@@ -4,7 +4,6 @@
 
 #include "components/infobars/core/infobar_delegate.h"
 
-#include "base/no_destructor.h"
 #include "build/build_config.h"
 #include "components/infobars/core/infobar.h"
 #include "components/infobars/core/infobar_manager.h"
@@ -33,8 +32,8 @@
 }
 
 const gfx::VectorIcon& InfoBarDelegate::GetVectorIcon() const {
-  static base::NoDestructor<gfx::VectorIcon> empty_icon;
-  return *empty_icon;
+  static gfx::VectorIcon empty_icon;
+  return empty_icon;
 }
 
 gfx::Image InfoBarDelegate::GetIcon() const {
diff --git a/components/keyed_service/content/browser_context_dependency_manager.h b/components/keyed_service/content/browser_context_dependency_manager.h
index e521103..98d4ab9 100644
--- a/components/keyed_service/content/browser_context_dependency_manager.h
+++ b/components/keyed_service/content/browser_context_dependency_manager.h
@@ -9,14 +9,10 @@
 #include "base/callback_list.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/core/dependency_manager.h"
 #include "components/keyed_service/core/keyed_service_export.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 namespace content {
 class BrowserContext;
 }
diff --git a/components/leveldb_proto/internal/shared_proto_database.cc b/components/leveldb_proto/internal/shared_proto_database.cc
index 416fe74..810d69a2 100644
--- a/components/leveldb_proto/internal/shared_proto_database.cc
+++ b/components/leveldb_proto/internal/shared_proto_database.cc
@@ -10,6 +10,7 @@
 #include "base/callback_helpers.h"
 #include "base/memory/ptr_util.h"
 #include "base/sequenced_task_runner.h"
+#include "base/synchronization/lock.h"
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "components/leveldb_proto/internal/leveldb_database.h"
@@ -478,6 +479,7 @@
     delete_obsolete_task_.Reset(base::BindOnce(
         &SharedProtoDatabase::DestroyObsoleteSharedProtoDatabaseClients, this,
         std::move(keep_shared_db_alive)));
+    base::AutoLock lock(delete_obsolete_delay_lock_);
     task_runner_->PostDelayedTask(FROM_HERE, delete_obsolete_task_.callback(),
                                   delete_obsolete_delay_);
   }
@@ -605,6 +607,12 @@
       std::move(db_wrapper), std::move(done));
 }
 
+void SharedProtoDatabase::SetDeleteObsoleteDelayForTesting(
+    base::TimeDelta delay) {
+  base::AutoLock lock(delete_obsolete_delay_lock_);
+  delete_obsolete_delay_ = delay;
+}
+
 LevelDB* SharedProtoDatabase::GetLevelDBForTesting() const {
   return db_.get();
 }
diff --git a/components/leveldb_proto/internal/shared_proto_database.h b/components/leveldb_proto/internal/shared_proto_database.h
index 67b6adc..d155e79 100644
--- a/components/leveldb_proto/internal/shared_proto_database.h
+++ b/components/leveldb_proto/internal/shared_proto_database.h
@@ -15,6 +15,7 @@
 #include "base/containers/queue.h"
 #include "base/memory/ref_counted.h"
 #include "base/sequence_checker.h"
+#include "base/synchronization/lock.h"
 #include "components/leveldb_proto/internal/proto/shared_db_metadata.pb.h"
 #include "components/leveldb_proto/internal/shared_proto_database_client.h"
 #include "components/leveldb_proto/public/proto_database.h"
@@ -159,9 +160,7 @@
 
   LevelDB* GetLevelDBForTesting() const;
 
-  void set_delete_obsolete_delay_for_testing(base::TimeDelta delay) {
-    delete_obsolete_delay_ = delay;
-  }
+  void SetDeleteObsoleteDelayForTesting(base::TimeDelta delay);
 
   scoped_refptr<base::SequencedTaskRunner> database_task_runner_for_testing()
       const {
@@ -192,6 +191,7 @@
   bool create_if_missing_ = false;
 
   base::TimeDelta delete_obsolete_delay_ = base::TimeDelta::FromSeconds(120);
+  base::Lock delete_obsolete_delay_lock_;
   base::CancelableOnceClosure delete_obsolete_task_;
 
   DISALLOW_COPY_AND_ASSIGN(SharedProtoDatabase);
diff --git a/components/leveldb_proto/internal/shared_proto_database_unittest.cc b/components/leveldb_proto/internal/shared_proto_database_unittest.cc
index 79ec500..82e006a 100644
--- a/components/leveldb_proto/internal/shared_proto_database_unittest.cc
+++ b/components/leveldb_proto/internal/shared_proto_database_unittest.cc
@@ -254,7 +254,7 @@
 }
 
 TEST_F(SharedProtoDatabaseTest, DeleteObsoleteClients) {
-  db()->set_delete_obsolete_delay_for_testing(base::TimeDelta());
+  db()->SetDeleteObsoleteDelayForTesting(base::TimeDelta());
   EXPECT_CALL(*db(), DestroyObsoleteSharedProtoDatabaseClients(_)).Times(1);
   base::RunLoop run_init_loop;
   InitDB(true /* create_if_missing */, "TestDatabaseUMA",
diff --git a/components/leveldb_proto/public/proto_database_provider.cc b/components/leveldb_proto/public/proto_database_provider.cc
index 9a5caaa9..9ba0bd2 100644
--- a/components/leveldb_proto/public/proto_database_provider.cc
+++ b/components/leveldb_proto/public/proto_database_provider.cc
@@ -56,7 +56,7 @@
     base::TimeDelta delay) {
   base::AutoLock lock(get_db_lock_);
   if (db_)
-    db_->set_delete_obsolete_delay_for_testing(delay);  // IN-TEST
+    db_->SetDeleteObsoleteDelayForTesting(delay);  // IN-TEST
 }
 
 }  // namespace leveldb_proto
diff --git a/components/leveldb_proto/public/proto_database_provider.h b/components/leveldb_proto/public/proto_database_provider.h
index 97626b3..8036009 100644
--- a/components/leveldb_proto/public/proto_database_provider.h
+++ b/components/leveldb_proto/public/proto_database_provider.h
@@ -63,6 +63,7 @@
 
   virtual ~ProtoDatabaseProvider();
 
+  // This method is thread safe.
   void SetSharedDBDeleteObsoleteDelayForTesting(base::TimeDelta delay);
 
  private:
diff --git a/components/metrics/entropy_state.cc b/components/metrics/entropy_state.cc
index 4995f51..3833ed8f 100644
--- a/components/metrics/entropy_state.cc
+++ b/components/metrics/entropy_state.cc
@@ -6,7 +6,6 @@
 
 #include "base/command_line.h"
 #include "base/metrics/histogram_functions.h"
-#include "base/no_destructor.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "components/metrics/metrics_pref_names.h"
@@ -23,14 +22,15 @@
 const int kMaxLowEntropySize = 8000;
 
 // Generates a new non-identifying entropy source used to seed persistent
-// activities. Using a NoDestructor so that the new low entropy source value
-// will only be generated on first access. And thus, even though we may write
-// the new low entropy source value to prefs multiple times, it stays the same
+// activities. Make it static so that the new low entropy source value will
+// only be generated on first access. And thus, even though we may write the
+// new low entropy source value to prefs multiple times, it stays the same
 // value.
 int GenerateLowEntropySource() {
-  static const base::NoDestructor<int> low_entropy_source(
-      [] { return base::RandInt(0, kMaxLowEntropySize - 1); }());
-  return *low_entropy_source;
+  static const int low_entropy_source =
+      base::RandInt(0, kMaxLowEntropySize - 1);
+  ;
+  return low_entropy_source;
 }
 
 // Generates a new non-identifying low entropy source using the same method
@@ -38,9 +38,10 @@
 // used for statistical validation, and *not* for randomization or experiment
 // assignment.
 int GeneratePseudoLowEntropySource() {
-  static const base::NoDestructor<int> pseudo_low_entropy_source(
-      [] { return base::RandInt(0, kMaxLowEntropySize - 1); }());
-  return *pseudo_low_entropy_source;
+  static const int pseudo_low_entropy_source =
+      base::RandInt(0, kMaxLowEntropySize - 1);
+  ;
+  return pseudo_low_entropy_source;
 }
 
 }  // namespace
diff --git a/components/mirroring/service/video_capture_client.cc b/components/mirroring/service/video_capture_client.cc
index 85902fa..437dfe2 100644
--- a/components/mirroring/service/video_capture_client.cc
+++ b/components/mirroring/service/video_capture_client.cc
@@ -5,7 +5,6 @@
 #include "components/mirroring/service/video_capture_client.h"
 
 #include "base/bind.h"
-#include "base/no_destructor.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "media/base/bind_to_current_loop.h"
@@ -21,16 +20,16 @@
 
 // Required by mojom::VideoCaptureHost interface. Can be any nonzero value.
 const base::UnguessableToken& DeviceId() {
-  static const base::NoDestructor<base::UnguessableToken> device_id(
+  static const base::UnguessableToken device_id(
       base::UnguessableToken::Deserialize(1, 1));
-  return *device_id;
+  return device_id;
 }
 
 // Required by mojom::VideoCaptureHost interface. Can be any nonzero value.
 const base::UnguessableToken& SessionId() {
-  static const base::NoDestructor<base::UnguessableToken> session_id(
+  static const base::UnguessableToken session_id(
       base::UnguessableToken::Deserialize(1, 1));
-  return *session_id;
+  return session_id;
 }
 
 }  // namespace
diff --git a/components/ntp_tiles/BUILD.gn b/components/ntp_tiles/BUILD.gn
index 8e20403..42c9bdb 100644
--- a/components/ntp_tiles/BUILD.gn
+++ b/components/ntp_tiles/BUILD.gn
@@ -48,7 +48,6 @@
   public_deps = [
     "//base",
     "//components/history/core/browser",
-    "//components/suggestions",
   ]
   deps = [
     "//build:branding_buildflags",
diff --git a/components/ntp_tiles/DEPS b/components/ntp_tiles/DEPS
index 6b856fa..b1142d5 100644
--- a/components/ntp_tiles/DEPS
+++ b/components/ntp_tiles/DEPS
@@ -11,7 +11,6 @@
   "+components/search_engines",
   "+components/search",
   "+components/strings/grit/components_strings.h",
-  "+components/suggestions",
   "+components/sync_preferences",
   "+components/url_formatter",
   "+components/variations",
diff --git a/components/ntp_tiles/custom_links_store.cc b/components/ntp_tiles/custom_links_store.cc
index 799e3c60..f6cbb1d 100644
--- a/components/ntp_tiles/custom_links_store.cc
+++ b/components/ntp_tiles/custom_links_store.cc
@@ -14,7 +14,6 @@
 #include "components/ntp_tiles/pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
-#include "components/suggestions/suggestions_pref_names.h"
 
 namespace ntp_tiles {
 
diff --git a/components/ntp_tiles/features.cc b/components/ntp_tiles/features.cc
index e3840f31..4ce3356 100644
--- a/components/ntp_tiles/features.cc
+++ b/components/ntp_tiles/features.cc
@@ -21,7 +21,4 @@
 const base::Feature kUsePopularSitesSuggestions{
     "UsePopularSitesSuggestions", base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kDisplaySuggestionsServiceTiles{
-    "DisplaySuggestionsServiceTiles", base::FEATURE_DISABLED_BY_DEFAULT};
-
 }  // namespace ntp_tiles
diff --git a/components/ntp_tiles/features.h b/components/ntp_tiles/features.h
index 628ed7c..d9e819607 100644
--- a/components/ntp_tiles/features.h
+++ b/components/ntp_tiles/features.h
@@ -26,10 +26,6 @@
 // If this feature is enabled, we enable popular sites in the suggestions UI.
 extern const base::Feature kUsePopularSitesSuggestions;
 
-// If this feature is enabled, we use the remote service to populate suggestions
-// tiles.
-extern const base::Feature kDisplaySuggestionsServiceTiles;
-
 }  // namespace ntp_tiles
 
 #endif  // COMPONENTS_NTP_TILES_FEATURES_H_
diff --git a/components/ntp_tiles/metrics.cc b/components/ntp_tiles/metrics.cc
index e145379..198de53 100644
--- a/components/ntp_tiles/metrics.cc
+++ b/components/ntp_tiles/metrics.cc
@@ -22,7 +22,6 @@
 
 // Identifiers for the various tile sources.
 const char kHistogramClientName[] = "client";
-const char kHistogramServerName[] = "server";
 const char kHistogramPopularName[] = "popular_fetched";
 const char kHistogramBakedInName[] = "popular_baked_in";
 const char kHistogramAllowlistName[] = "allowlist";
@@ -51,8 +50,6 @@
       return kHistogramPopularName;
     case TileSource::ALLOWLIST:
       return kHistogramAllowlistName;
-    case TileSource::SUGGESTIONS_SERVICE:
-      return kHistogramServerName;
     case TileSource::HOMEPAGE:
       return kHistogramHomepageName;
     case TileSource::CUSTOM_LINKS:
diff --git a/components/ntp_tiles/metrics_unittest.cc b/components/ntp_tiles/metrics_unittest.cc
index 5018f4e..ab860ef 100644
--- a/components/ntp_tiles/metrics_unittest.cc
+++ b/components/ntp_tiles/metrics_unittest.cc
@@ -112,12 +112,12 @@
                            .Build());
   RecordTileImpression(Builder()
                            .WithIndex(5)
-                           .WithSource(TileSource::SUGGESTIONS_SERVICE)
+                           .WithSource(TileSource::POPULAR)
                            .WithVisualType(ICON_REAL)
                            .Build());
   RecordTileImpression(Builder()
                            .WithIndex(6)
-                           .WithSource(TileSource::SUGGESTIONS_SERVICE)
+                           .WithSource(TileSource::POPULAR)
                            .WithVisualType(ICON_DEFAULT)
                            .Build());
   RecordTileImpression(Builder()
@@ -137,10 +137,6 @@
                   base::Bucket(/*min=*/6, /*count=*/1),
                   base::Bucket(/*min=*/7, /*count=*/1)));
   EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.server"),
-      ElementsAre(base::Bucket(/*min=*/5, /*count=*/1),
-                  base::Bucket(/*min=*/6, /*count=*/1)));
-  EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.client"),
       ElementsAre(base::Bucket(/*min=*/0, /*count=*/1),
                   base::Bucket(/*min=*/1, /*count=*/1),
@@ -149,21 +145,21 @@
                   base::Bucket(/*min=*/4, /*count=*/1)));
   EXPECT_THAT(histogram_tester.GetAllSamples(
                   "NewTabPage.SuggestionsImpression.popular_fetched"),
-              ElementsAre(base::Bucket(/*min=*/7, /*count=*/1)));
+              ElementsAre(base::Bucket(/*min=*/5, /*count=*/1),
+                          base::Bucket(/*min=*/6, /*count=*/1),
+                          base::Bucket(/*min=*/7, /*count=*/1)));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType"),
               ElementsAre(base::Bucket(/*min=*/ICON_REAL, /*count=*/4),
                           base::Bucket(/*min=*/ICON_COLOR, /*count=*/3),
                           base::Bucket(/*min=*/ICON_DEFAULT, /*count=*/1)));
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.server"),
-              ElementsAre(base::Bucket(/*min=*/ICON_REAL, /*count=*/1),
-                          base::Bucket(/*min=*/ICON_DEFAULT, /*count=*/1)));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.client"),
               ElementsAre(base::Bucket(/*min=*/ICON_REAL, /*count=*/3),
                           base::Bucket(/*min=*/ICON_COLOR, /*count=*/2)));
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.TileType.popular_fetched"),
-      ElementsAre(base::Bucket(/*min=*/ICON_COLOR,
-                               /*count=*/1)));
+      ElementsAre(base::Bucket(/*min=*/ICON_REAL, /*count=*/1),
+                  base::Bucket(/*min=*/ICON_COLOR, /*count=*/1),
+                  base::Bucket(/*min=*/ICON_DEFAULT, /*count=*/1)));
   EXPECT_THAT(histogram_tester.GetAllSamples(
                   "NewTabPage.SuggestionsImpression.IconsReal"),
               ElementsAre(base::Bucket(/*min=*/0, /*count=*/1),
@@ -187,7 +183,7 @@
                            .WithTitleSource(TileTitleSource::UNKNOWN)
                            .Build());
   RecordTileImpression(Builder()
-                           .WithSource(TileSource::SUGGESTIONS_SERVICE)
+                           .WithSource(TileSource::TOP_SITES)
                            .WithTitleSource(TileTitleSource::INFERRED)
                            .Build());
   RecordTileImpression(Builder()
@@ -208,9 +204,8 @@
                            .Build());
 
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTitle.client"),
-              ElementsAre(base::Bucket(kUnknownTitleSource, /*count=*/1)));
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTitle.server"),
-              ElementsAre(base::Bucket(kInferredTitleSource, /*count=*/1)));
+              ElementsAre(base::Bucket(kUnknownTitleSource, /*count=*/1),
+                          base::Bucket(kInferredTitleSource, /*count=*/1)));
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.TileTitle.popular_fetched"),
       ElementsAre(base::Bucket(kManifestTitleSource, /*count=*/1),
@@ -233,7 +228,7 @@
   base::HistogramTester histogram_tester;
   RecordTileImpression(
       Builder()
-          .WithSource(TileSource::SUGGESTIONS_SERVICE)
+          .WithSource(TileSource::TOP_SITES)
           .WithDataGenerationTime(base::Time::Now() - kSuggestionAge)
           .Build());
 
@@ -244,7 +239,7 @@
                           (kSuggestionAge + kBucketTolerance).InSeconds(),
                           /*count=*/1)));
   EXPECT_THAT(histogram_tester.GetAllSamples(
-                  "NewTabPage.SuggestionsImpressionAge.server"),
+                  "NewTabPage.SuggestionsImpressionAge.client"),
               ElementsAre(IsBucketBetween(
                   (kSuggestionAge - kBucketTolerance).InSeconds(),
                   (kSuggestionAge + kBucketTolerance).InSeconds(),
@@ -286,8 +281,6 @@
               ElementsAre(base::Bucket(/*min=*/3, /*count=*/1)));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.client"),
               ElementsAre(base::Bucket(/*min=*/3, /*count=*/1)));
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.server"),
-              IsEmpty());
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.MostVisited.popular_fetched"),
       IsEmpty());
@@ -315,8 +308,6 @@
               ElementsAre(base::Bucket(/*min=*/3, /*count=*/1)));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.client"),
               ElementsAre(base::Bucket(/*min=*/3, /*count=*/1)));
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.MostVisited.server"),
-              IsEmpty());
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.MostVisited.popular_fetched"),
       IsEmpty());
@@ -339,8 +330,8 @@
                       .WithTitleSource(TileTitleSource::UNKNOWN)
                       .Build());
   RecordTileClick(Builder()
-                      .WithSource(TileSource::SUGGESTIONS_SERVICE)
-                      .WithTitleSource(TileTitleSource::UNKNOWN)
+                      .WithSource(TileSource::TOP_SITES)
+                      .WithTitleSource(TileTitleSource::TITLE_TAG)
                       .Build());
   RecordTileClick(Builder()
                       .WithSource(TileSource::POPULAR)
@@ -361,10 +352,8 @@
 
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.TileTitleClicked.client"),
-      ElementsAre(base::Bucket(kUnknownTitleSource, /*count=*/1)));
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.TileTitleClicked.server"),
-      ElementsAre(base::Bucket(kUnknownTitleSource, /*count=*/1)));
+      ElementsAre(base::Bucket(kUnknownTitleSource, /*count=*/1),
+                  base::Bucket(kTitleTagTitleSource, /*count=*/1)));
   EXPECT_THAT(histogram_tester.GetAllSamples(
                   "NewTabPage.TileTitleClicked.popular_fetched"),
               ElementsAre(base::Bucket(kManifestTitleSource, /*count=*/1),
@@ -374,10 +363,10 @@
               ElementsAre(base::Bucket(kMetaTagTitleSource, /*count=*/1),
                           base::Bucket(kTitleTagTitleSource, /*count=*/1)));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileTitleClicked"),
-              ElementsAre(base::Bucket(kUnknownTitleSource, /*count=*/2),
+              ElementsAre(base::Bucket(kUnknownTitleSource, /*count=*/1),
                           base::Bucket(kManifestTitleSource, /*count=*/1),
                           base::Bucket(kMetaTagTitleSource, /*count=*/1),
-                          base::Bucket(kTitleTagTitleSource, /*count=*/2)));
+                          base::Bucket(kTitleTagTitleSource, /*count=*/3)));
 }
 
 TEST(RecordTileClickTest, ShouldRecordClickAge) {
@@ -386,7 +375,7 @@
   base::HistogramTester histogram_tester;
   RecordTileClick(
       Builder()
-          .WithSource(TileSource::SUGGESTIONS_SERVICE)
+          .WithSource(TileSource::TOP_SITES)
           .WithDataGenerationTime(base::Time::Now() - kSuggestionAge)
           .Build());
 
@@ -396,7 +385,7 @@
                   (kSuggestionAge + kBucketTolerance).InSeconds(),
                   /*count=*/1)));
   EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.MostVisitedAge.server"),
+      histogram_tester.GetAllSamples("NewTabPage.MostVisitedAge.client"),
       ElementsAre(
           IsBucketBetween((kSuggestionAge - kBucketTolerance).InSeconds(),
                           (kSuggestionAge + kBucketTolerance).InSeconds(),
diff --git a/components/ntp_tiles/most_visited_sites.cc b/components/ntp_tiles/most_visited_sites.cc
index 3a57af9..21e3180 100644
--- a/components/ntp_tiles/most_visited_sites.cc
+++ b/components/ntp_tiles/most_visited_sites.cc
@@ -29,9 +29,6 @@
 #include "components/search/ntp_features.h"
 
 using history::TopSites;
-using suggestions::ChromeSuggestion;
-using suggestions::SuggestionsProfile;
-using suggestions::SuggestionsService;
 
 namespace ntp_tiles {
 
@@ -120,14 +117,12 @@
 MostVisitedSites::MostVisitedSites(
     PrefService* prefs,
     scoped_refptr<history::TopSites> top_sites,
-    SuggestionsService* suggestions,
     std::unique_ptr<PopularSites> popular_sites,
     std::unique_ptr<CustomLinksManager> custom_links,
     std::unique_ptr<IconCacher> icon_cacher,
     std::unique_ptr<MostVisitedSitesSupervisor> supervisor)
     : prefs_(prefs),
       top_sites_(top_sites),
-      suggestions_service_(suggestions),
       popular_sites_(std::move(popular_sites)),
       custom_links_(std::move(custom_links)),
       icon_cacher_(std::move(icon_cacher)),
@@ -139,7 +134,6 @@
 
   // top_sites_ can be null in tests.
   // TODO(sfiera): have iOS use a dummy TopSites in its tests.
-  DCHECK(suggestions_service_);
   if (supervisor_)
     supervisor_->SetObserver(this);
 }
@@ -168,8 +162,6 @@
   switch (source) {
     case TileSource::TOP_SITES:
       return top_sites_ != nullptr;
-    case TileSource::SUGGESTIONS_SERVICE:
-      return suggestions_service_ != nullptr;
     case TileSource::POPULAR_BAKED_IN:
     case TileSource::POPULAR:
       return popular_sites_ != nullptr;
@@ -228,14 +220,10 @@
           custom_links_->RegisterCallbackForOnChanged(base::BindRepeating(
               &MostVisitedSites::OnCustomLinksChanged, base::Unretained(this)));
     }
-
-    suggestions_subscription_ = suggestions_service_->AddCallback(
-        base::BindRepeating(&MostVisitedSites::OnSuggestionsProfileChanged,
-                            base::Unretained(this)));
   }
 
-  // Immediately build the current set of tiles, getting suggestions from the
-  // SuggestionsService's cache or, if that is empty, sites from TopSites.
+  // Immediately build the current set of tiles, getting suggestions from
+  // TopSites.
   BuildCurrentTiles();
   // Also start a request for fresh suggestions.
   Refresh();
@@ -249,12 +237,8 @@
   if (top_sites_) {
     // TopSites updates itself after a delay. To ensure up-to-date results,
     // force an update now.
-    // TODO(mastiz): Is seems unnecessary to refresh TopSites if we will end up
-    // using server-side suggestions.
     top_sites_->SyncWithHistory();
   }
-
-  suggestions_service_->FetchSuggestionsData();
 }
 
 void MostVisitedSites::RefreshTiles() {
@@ -424,23 +408,11 @@
     else
       top_sites_->RemoveBlockedUrl(url);
   }
-
-  // Only blocklist in the server-side suggestions service if it's active.
-  if (mv_source_ == TileSource::SUGGESTIONS_SERVICE) {
-    if (add_url)
-      suggestions_service_->BlocklistURL(url);
-    else
-      suggestions_service_->UndoBlocklistURL(url);
-  }
 }
 
 void MostVisitedSites::ClearBlockedUrls() {
   if (top_sites_)
     top_sites_->ClearBlockedUrls();
-
-  // Only update the server-side blocklist if it's active.
-  if (mv_source_ == TileSource::SUGGESTIONS_SERVICE)
-    suggestions_service_->ClearBlocklist();
 }
 
 void MostVisitedSites::OnBlockedSitesChanged() {
@@ -484,10 +456,9 @@
 
 void MostVisitedSites::OnMostVisitedURLsAvailable(
     const history::MostVisitedURLList& visited_list) {
-  // Ignore the event if tiles are provided by the Suggestions Service or custom
-  // links, which take precedence.
-  if (IsCustomLinksInitialized() ||
-      mv_source_ == TileSource::SUGGESTIONS_SERVICE) {
+  // Ignore the event if tiles are provided by custom links, which take
+  // precedence.
+  if (IsCustomLinksInitialized()) {
     return;
   }
 
@@ -518,76 +489,14 @@
   InitiateNotificationForNewTiles(std::move(tiles));
 }
 
-void MostVisitedSites::OnSuggestionsProfileChanged(
-    const SuggestionsProfile& suggestions_profile) {
-  // Ignore the event if tiles are provided by custom links, which take
-  // precedence.
-  if (IsCustomLinksInitialized() ||
-      (suggestions_profile.suggestions_size() == 0 &&
-       mv_source_ != TileSource::SUGGESTIONS_SERVICE)) {
-    return;
-  }
-
-  BuildCurrentTilesGivenSuggestionsProfile(suggestions_profile);
-}
-
 void MostVisitedSites::BuildCurrentTiles() {
   if (IsCustomLinksInitialized()) {
     BuildCustomLinks(custom_links_->GetLinks());
     return;
   }
 
-  BuildCurrentTilesGivenSuggestionsProfile(
-      suggestions_service_->GetSuggestionsDataFromCache().value_or(
-          SuggestionsProfile()));
-}
-
-void MostVisitedSites::BuildCurrentTilesGivenSuggestionsProfile(
-    const suggestions::SuggestionsProfile& suggestions_profile) {
-  size_t num_tiles = suggestions_profile.suggestions_size();
-  // With no server suggestions, fall back to local TopSites.
-  if (num_tiles == 0 ||
-      !base::FeatureList::IsEnabled(kDisplaySuggestionsServiceTiles)) {
-    mv_source_ = TileSource::TOP_SITES;
-    InitiateTopSitesQuery();
-    return;
-  }
-  if (GetMaxNumSites() < num_tiles)
-    num_tiles = GetMaxNumSites();
-
-  const base::Time profile_timestamp =
-      base::Time::UnixEpoch() +
-      base::TimeDelta::FromMicroseconds(suggestions_profile.timestamp());
-
-  NTPTilesVector tiles;
-  for (size_t i = 0; i < num_tiles; ++i) {
-    const ChromeSuggestion& suggestion_pb = suggestions_profile.suggestions(i);
-    GURL url(suggestion_pb.url());
-    if (supervisor_ && supervisor_->IsBlocked(url))
-      continue;
-
-    NTPTile tile;
-    tile.title =
-        custom_links_
-            ? GenerateShortTitle(base::UTF8ToUTF16(suggestion_pb.title()))
-            : base::UTF8ToUTF16(suggestion_pb.title());
-    tile.url = url;
-    tile.source = TileSource::SUGGESTIONS_SERVICE;
-    // The title is an aggregation of multiple history entries of one site.
-    tile.title_source = TileTitleSource::INFERRED;
-    tile.allowlist_icon_path = GetAllowlistLargeIconPath(url);
-    tile.favicon_url = GURL(suggestion_pb.favicon_url());
-    tile.data_generation_time = profile_timestamp;
-
-    icon_cacher_->StartFetchMostLikely(
-        url, base::BindRepeating(&MostVisitedSites::OnIconMadeAvailable,
-                                 base::Unretained(this), url));
-
-    tiles.push_back(std::move(tile));
-  }
-
-  mv_source_ = TileSource::SUGGESTIONS_SERVICE;
-  InitiateNotificationForNewTiles(std::move(tiles));
+  mv_source_ = TileSource::TOP_SITES;
+  InitiateTopSitesQuery();
 }
 
 NTPTilesVector MostVisitedSites::CreateAllowlistEntryPointTiles(
diff --git a/components/ntp_tiles/most_visited_sites.h b/components/ntp_tiles/most_visited_sites.h
index 301dec5..b86c71a30 100644
--- a/components/ntp_tiles/most_visited_sites.h
+++ b/components/ntp_tiles/most_visited_sites.h
@@ -31,8 +31,6 @@
 #include "components/ntp_tiles/popular_sites.h"
 #include "components/ntp_tiles/section_type.h"
 #include "components/ntp_tiles/tile_source.h"
-#include "components/suggestions/proto/suggestions.pb.h"
-#include "components/suggestions/suggestions_service.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
@@ -122,7 +120,6 @@
   //  optional and if null, the associated features will be disabled.
   MostVisitedSites(PrefService* prefs,
                    scoped_refptr<history::TopSites> top_sites,
-                   suggestions::SuggestionsService* suggestions,
                    std::unique_ptr<PopularSites> popular_sites,
                    std::unique_ptr<CustomLinksManager> custom_links,
                    std::unique_ptr<IconCacher> icon_cacher,
@@ -137,9 +134,6 @@
 
   // Returns the corresponding object passed at construction.
   history::TopSites* top_sites() { return top_sites_.get(); }
-  suggestions::SuggestionsService* suggestions() {
-    return suggestions_service_;
-  }
   PopularSites* popular_sites() { return popular_sites_.get(); }
   MostVisitedSitesSupervisor* supervisor() { return supervisor_.get(); }
 
@@ -266,8 +260,7 @@
   // including the "Add shortcut" button.
   size_t GetMaxNumSites() const;
 
-  // Initialize the query to Top Sites. Called if the SuggestionsService
-  // returned no data.
+  // Initialize the query to Top Sites.
   void InitiateTopSitesQuery();
 
   // If there's a allowlist entry point for the URL, return the large icon path.
@@ -277,18 +270,10 @@
   void OnMostVisitedURLsAvailable(
       const history::MostVisitedURLList& visited_list);
 
-  // Callback for when an update is reported by the SuggestionsService.
-  void OnSuggestionsProfileChanged(
-      const suggestions::SuggestionsProfile& suggestions_profile);
-
   // Builds the current tileset based on available caches and notifies the
   // observer.
   void BuildCurrentTiles();
 
-  // Same as above the SuggestionsProfile is provided, no need to read cache.
-  void BuildCurrentTilesGivenSuggestionsProfile(
-      const suggestions::SuggestionsProfile& suggestions_profile);
-
   // Creates allowlist entry point suggestions whose hosts weren't used yet.
   NTPTilesVector CreateAllowlistEntryPointTiles(
       const std::set<std::string>& used_hosts,
@@ -360,7 +345,6 @@
 
   PrefService* prefs_;
   scoped_refptr<history::TopSites> top_sites_;
-  suggestions::SuggestionsService* suggestions_service_;
   std::unique_ptr<PopularSites> const popular_sites_;
   std::unique_ptr<CustomLinksManager> const custom_links_;
   std::unique_ptr<IconCacher> const icon_cacher_;
@@ -381,14 +365,12 @@
   bool is_custom_links_enabled_ = true;
   bool is_shortcuts_visible_ = true;
 
-  base::CallbackListSubscription suggestions_subscription_;
-
   base::ScopedObservation<history::TopSites, history::TopSitesObserver>
       top_sites_observation_{this};
 
   base::CallbackListSubscription custom_links_subscription_;
 
-  // The main source of personal tiles - either TOP_SITES or SUGGESTIONS_SEVICE.
+  // The main source of personal tiles - either TOP_SITES or CUSTOM_LINKS.
   TileSource mv_source_;
 
   // Current set of tiles. Optional so that the observer can be notified
diff --git a/components/ntp_tiles/most_visited_sites_unittest.cc b/components/ntp_tiles/most_visited_sites_unittest.cc
index e7352df..ee23bae 100644
--- a/components/ntp_tiles/most_visited_sites_unittest.cc
+++ b/components/ntp_tiles/most_visited_sites_unittest.cc
@@ -56,9 +56,6 @@
 using history::MostVisitedURL;
 using history::MostVisitedURLList;
 using history::TopSites;
-using suggestions::ChromeSuggestion;
-using suggestions::SuggestionsProfile;
-using suggestions::SuggestionsService;
 using testing::_;
 using testing::AllOf;
 using testing::AnyNumber;
@@ -144,23 +141,6 @@
   return tile;
 }
 
-ChromeSuggestion MakeSuggestion(const std::string& title,
-                                const std::string& url) {
-  ChromeSuggestion suggestion;
-  suggestion.set_title(title);
-  suggestion.set_url(url);
-  return suggestion;
-}
-
-SuggestionsProfile MakeProfile(
-    const std::vector<ChromeSuggestion>& suggestions) {
-  SuggestionsProfile profile;
-  for (const ChromeSuggestion& suggestion : suggestions) {
-    *profile.add_suggestions() = suggestion;
-  }
-  return profile;
-}
-
 MostVisitedURL MakeMostVisitedURL(const std::u16string& title,
                                   const std::string& url) {
   MostVisitedURL result;
@@ -196,19 +176,6 @@
   ~MockTopSites() override = default;
 };
 
-class MockSuggestionsService : public SuggestionsService {
- public:
-  MOCK_METHOD0(FetchSuggestionsData, bool());
-  MOCK_CONST_METHOD0(GetSuggestionsDataFromCache,
-                     absl::optional<SuggestionsProfile>());
-  MOCK_METHOD1(
-      AddCallback,
-      base::CallbackListSubscription(const ResponseCallback& callback));
-  MOCK_METHOD1(BlocklistURL, bool(const GURL& candidate_url));
-  MOCK_METHOD1(UndoBlocklistURL, bool(const GURL& url));
-  MOCK_METHOD0(ClearBlocklist, void());
-};
-
 class MockMostVisitedSitesObserver : public MostVisitedSites::Observer {
  public:
   MOCK_METHOD1(OnURLsAvailable,
@@ -393,12 +360,8 @@
 
 }  // namespace
 
-// Param is a tuple with two components:
-// * The first specifies whether Popular Sites is enabled via variations.
-// * The second specifies whether Suggestions Service tiles are enabled via
-//   variations.
-class MostVisitedSitesTest
-    : public ::testing::TestWithParam<std::tuple<bool, bool>> {
+// Param specifies whether Popular Sites is enabled via variations.
+class MostVisitedSitesTest : public ::testing::TestWithParam<bool> {
  protected:
   using TopSitesCallbackList =
       base::OnceCallbackList<TopSites::GetMostVisitedURLsCallback::RunType>;
@@ -415,11 +378,6 @@
     } else {
       disabled_features.push_back(kUsePopularSitesSuggestions);
     }
-    if (IsDisplaySuggestionsServiceTilesEnabled()) {
-      enabled_features.push_back(kDisplaySuggestionsServiceTiles);
-    } else {
-      disabled_features.push_back(kDisplaySuggestionsServiceTiles);
-    }
 
     feature_list_.InitWithFeatures(enabled_features, disabled_features);
     if (IsPopularSitesFeatureEnabled())
@@ -472,22 +430,17 @@
     EXPECT_CALL(*icon_cacher, StartFetchMostLikely(_, _)).Times(AtLeast(0));
 
     most_visited_sites_ = std::make_unique<MostVisitedSites>(
-        &pref_service_, mock_top_sites_, &mock_suggestions_service_,
-        popular_sites_factory_.New(), std::move(mock_custom_links),
-        std::move(icon_cacher),
+        &pref_service_, mock_top_sites_, popular_sites_factory_.New(),
+        std::move(mock_custom_links), std::move(icon_cacher),
         /*supervisor=*/nullptr);
   }
 
-  bool IsPopularSitesFeatureEnabled() const { return std::get<0>(GetParam()); }
-  bool IsDisplaySuggestionsServiceTilesEnabled() const {
-    return std::get<1>(GetParam());
-  }
+  bool IsPopularSitesFeatureEnabled() const { return GetParam(); }
 
   bool VerifyAndClearExpectations() {
     base::RunLoop().RunUntilIdle();
     const bool success =
         Mock::VerifyAndClearExpectations(mock_top_sites_.get()) &&
-        Mock::VerifyAndClearExpectations(&mock_suggestions_service_) &&
         Mock::VerifyAndClearExpectations(&mock_observer_);
     // For convenience, restore the expectations for IsBlocked().
     if (IsPopularSitesFeatureEnabled()) {
@@ -510,24 +463,9 @@
     return raw_client_ptr;
   }
 
-  void DisableRemoteSuggestions() {
-    EXPECT_CALL(mock_suggestions_service_, AddCallback(_))
-        .Times(AnyNumber())
-        .WillRepeatedly(Invoke(&suggestions_service_callbacks_,
-                               &SuggestionsService::ResponseCallbackList::Add));
-    EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache())
-        .Times(AnyNumber())
-        .WillRepeatedly(Return(SuggestionsProfile()));  // Empty cache.
-    EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData())
-        .Times(AnyNumber())
-        .WillRepeatedly(Return(true));
-  }
-
   void EnableCustomLinks() { is_custom_links_enabled_ = true; }
 
   bool is_custom_links_enabled_ = false;
-  base::RepeatingCallbackList<SuggestionsService::ResponseCallback::RunType>
-      suggestions_service_callbacks_;
   TopSitesCallbackList top_sites_callbacks_;
 
   base::test::SingleThreadTaskEnvironment task_environment_;
@@ -536,7 +474,6 @@
   PopularSitesFactoryForTest popular_sites_factory_{&pref_service_};
   scoped_refptr<StrictMock<MockTopSites>> mock_top_sites_ =
       base::MakeRefCounted<StrictMock<MockTopSites>>();
-  StrictMock<MockSuggestionsService> mock_suggestions_service_;
   StrictMock<MockMostVisitedSitesObserver> mock_observer_;
   StrictMock<MockMostVisitedSitesObserver> mock_other_observer_;
   std::unique_ptr<MostVisitedSites> most_visited_sites_;
@@ -551,16 +488,14 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_P(MostVisitedSitesTest, ShouldRefreshBothBackends) {
+TEST_P(MostVisitedSitesTest, ShouldRefreshBackends) {
   EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
-  EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData());
   most_visited_sites_->Refresh();
 }
 
 TEST_P(MostVisitedSitesTest, ShouldIncludeTileForHomepage) {
   FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
   homepage_client->SetHomepageTileEnabled(true);
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{}));
   EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
@@ -575,7 +510,6 @@
 }
 
 TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomepageWithoutClient) {
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{}));
   EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
@@ -596,7 +530,6 @@
   FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
   homepage_client->SetHomepageTileEnabled(true);
   homepage_client->SetHomepageTitle(kHomepageTitle);
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{}));
   EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
@@ -624,7 +557,6 @@
 TEST_P(MostVisitedSitesTest, ShouldUpdateHomepageTileWhenRefreshHomepageTile) {
   FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
   homepage_client->SetHomepageTileEnabled(true);
-  DisableRemoteSuggestions();
 
   // Ensure that home tile is available as usual.
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
@@ -642,7 +574,6 @@
 
   // Disable home page and rebuild _without_ Resync. The tile should be gone.
   homepage_client->SetHomepageTileEnabled(false);
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{}));
   EXPECT_CALL(*mock_top_sites_, SyncWithHistory()).Times(0);
@@ -655,7 +586,6 @@
 TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomepageIfNoTileRequested) {
   FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
   homepage_client->SetHomepageTileEnabled(true);
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{}));
   EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
@@ -673,7 +603,6 @@
 TEST_P(MostVisitedSitesTest, ShouldReturnHomepageIfOneTileRequested) {
   FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
   homepage_client->SetHomepageTileEnabled(true);
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>((
           MostVisitedURLList{MakeMostVisitedURL(u"Site 1", "http://site1/")})));
@@ -694,7 +623,6 @@
 TEST_P(MostVisitedSitesTest, ShouldHaveHomepageFirstInListWhenFull) {
   FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
   homepage_client->SetHomepageTileEnabled(true);
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>((MostVisitedURLList{
           MakeMostVisitedURL(u"Site 1", "http://site1/"),
@@ -723,7 +651,6 @@
 TEST_P(MostVisitedSitesTest, ShouldHaveHomepageFirstInListWhenNotFull) {
   FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
   homepage_client->SetHomepageTileEnabled(true);
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>((MostVisitedURLList{
           MakeMostVisitedURL(u"Site 1", "http://site1/"),
@@ -752,7 +679,6 @@
 TEST_P(MostVisitedSitesTest, ShouldDeduplicateHomepageWithTopSites) {
   FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
   homepage_client->SetHomepageTileEnabled(true);
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(
           (MostVisitedURLList{MakeMostVisitedURL(u"Site 1", "http://site1/"),
@@ -776,7 +702,6 @@
 TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomepageIfThereIsNone) {
   FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
   homepage_client->SetHomepageTileEnabled(false);
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{}));
   EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
@@ -798,7 +723,6 @@
   FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
   homepage_client->SetHomepageTileEnabled(true);
   homepage_client->SetHomepageUrl(GURL(kEmptyHomepageUrl));
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{}));
   EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
@@ -816,7 +740,6 @@
 TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomepageIfBlocked) {
   FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
   homepage_client->SetHomepageTileEnabled(true);
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(
           (MostVisitedURLList{MakeMostVisitedURL(u"", kHomepageUrl)})));
@@ -842,8 +765,6 @@
 TEST_P(MostVisitedSitesTest, ShouldPinHomepageAgainIfBlockedUndone) {
   FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
   homepage_client->SetHomepageTileEnabled(true);
-
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillOnce(base::test::RunOnceCallback<0>(
           (MostVisitedURLList{MakeMostVisitedURL(u"", kHomepageUrl)})));
@@ -861,8 +782,6 @@
                                                   /*max_num_sites=*/3);
   base::RunLoop().RunUntilIdle();
   VerifyAndClearExpectations();
-
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillOnce(base::test::RunOnceCallback<0>(MostVisitedURLList{}));
   EXPECT_CALL(*mock_top_sites_, IsBlocked(Eq(GURL(kHomepageUrl))))
@@ -880,8 +799,6 @@
 
 TEST_P(MostVisitedSitesTest, ShouldNotIncludeTileForExploreSitesIfNoClient) {
   // Does not register an explore sites client.
-
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{
           MakeMostVisitedURL(u"ESPN", "http://espn.com/"),
@@ -903,7 +820,6 @@
 // and popular sites.
 TEST_P(MostVisitedSitesTest, ShouldIncludeTileForExploreSites) {
   RegisterNewExploreSitesClient();
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{
           MakeMostVisitedURL(u"ESPN", "http://espn.com/"),
@@ -922,7 +838,6 @@
 
 TEST_P(MostVisitedSitesTest, RemovesPersonalSiteIfExploreSitesTilePresent) {
   RegisterNewExploreSitesClient();
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{
           MakeMostVisitedURL(u"ESPN", "http://espn.com/"),
@@ -945,15 +860,10 @@
 
 TEST_P(MostVisitedSitesTest, ShouldInformSuggestionSourcesWhenBlocked) {
   EXPECT_CALL(*mock_top_sites_, AddBlockedUrl(Eq(GURL(kHomepageUrl)))).Times(1);
-  EXPECT_CALL(mock_suggestions_service_, BlocklistURL(Eq(GURL(kHomepageUrl))))
-      .Times(AnyNumber());
   most_visited_sites_->AddOrRemoveBlockedUrl(GURL(kHomepageUrl),
                                              /*add_url=*/true);
   EXPECT_CALL(*mock_top_sites_, RemoveBlockedUrl(Eq(GURL(kHomepageUrl))))
       .Times(1);
-  EXPECT_CALL(mock_suggestions_service_,
-              UndoBlocklistURL(Eq(GURL(kHomepageUrl))))
-      .Times(AnyNumber());
   most_visited_sites_->AddOrRemoveBlockedUrl(GURL(kHomepageUrl),
                                              /*add_url=*/false);
 }
@@ -962,7 +872,6 @@
        ShouldDeduplicatePopularSitesWithMostVisitedIffHostAndTitleMatches) {
   pref_service_.SetString(prefs::kPopularSitesOverrideCountry, "US");
   RecreateMostVisitedSites();  // Refills cache with ESPN and Google News.
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{
           MakeMostVisitedURL(u"ESPN", "http://espn.com/"),
@@ -1004,11 +913,6 @@
           MostVisitedURLList{MakeMostVisitedURL(u"Site 1", "http://site1/")}));
 
   InSequence seq;
-  EXPECT_CALL(mock_suggestions_service_, AddCallback(_))
-      .WillOnce(Invoke(&suggestions_service_callbacks_,
-                       &SuggestionsService::ResponseCallbackList::Add));
-  EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache())
-      .WillOnce(Return(SuggestionsProfile()));  // Empty cache.
   if (IsPopularSitesFeatureEnabled()) {
     EXPECT_CALL(
         mock_observer_,
@@ -1028,13 +932,10 @@
                                                  TileSource::TOP_SITES))))));
   }
   EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
-  EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData())
-      .WillOnce(Return(true));
 
   most_visited_sites_->AddMostVisitedURLsObserver(&mock_observer_,
                                                   /*max_num_sites=*/3);
   VerifyAndClearExpectations();
-  EXPECT_FALSE(suggestions_service_callbacks_.empty());
   CHECK(top_sites_callbacks_.empty());
 
   // Update by TopSites is propagated.
@@ -1052,7 +953,6 @@
 
 // Tests that multiple observers can be added to the MostVisitedSites.
 TEST_P(MostVisitedSitesTest, MultipleObservers) {
-  DisableRemoteSuggestions();
   EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
       .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{
           MakeMostVisitedURL(u"ESPN", "http://espn.com/"),
@@ -1103,8 +1003,7 @@
 
 INSTANTIATE_TEST_SUITE_P(MostVisitedSitesTest,
                          MostVisitedSitesTest,
-                         ::testing::Combine(::testing::Bool(),
-                                            ::testing::Bool()));
+                         ::testing::Bool());
 
 TEST(MostVisitedSitesTest, ShouldDeduplicateDomainWithNoWwwDomain) {
   EXPECT_TRUE(MostVisitedSites::IsHostOrMobilePageKnown({"www.mobile.de"},
@@ -1161,23 +1060,6 @@
         .WillOnce(SaveArg<0>(sections));
   }
 
-  void ExpectBuildWithSuggestions(
-      const std::vector<ChromeSuggestion>& suggestions,
-      std::map<SectionType, NTPTilesVector>* sections) {
-    EXPECT_CALL(mock_suggestions_service_, AddCallback(_))
-        .WillOnce(Invoke(&suggestions_service_callbacks_,
-                         &SuggestionsService::ResponseCallbackList::Add));
-    EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache())
-        .WillOnce(Return(MakeProfile(suggestions)));
-    EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
-    EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData())
-        .WillOnce(Return(true));
-    EXPECT_CALL(*mock_custom_links_, IsInitialized())
-        .WillRepeatedly(Return(false));
-    EXPECT_CALL(mock_observer_, OnURLsAvailable(_))
-        .WillOnce(SaveArg<0>(sections));
-  }
-
   void ExpectBuildWithCustomLinks(
       const std::vector<CustomLinksManager::Link>& expected_links,
       std::map<SectionType, NTPTilesVector>* sections) {
@@ -1194,7 +1076,6 @@
   const char kTestUrl[] = "http://site1/";
   const char16_t kTestTitle[] = u"Site 1";
   std::map<SectionType, NTPTilesVector> sections;
-  DisableRemoteSuggestions();
 
   // Build tiles when custom links is not initialized. Tiles should be Top
   // Sites.
@@ -1241,7 +1122,6 @@
   std::vector<CustomLinksManager::Link> expected_links(
       {CustomLinksManager::Link{GURL(kTestUrl), kTestTitle}});
   std::map<SectionType, NTPTilesVector> sections;
-  DisableRemoteSuggestions();
 
   // Build tiles when custom links is not initialized. Tiles should be Top
   // Sites.
@@ -1290,7 +1170,6 @@
   std::vector<CustomLinksManager::Link> expected_links(
       {CustomLinksManager::Link{GURL(kTestUrl), kTestTitle}});
   std::map<SectionType, NTPTilesVector> sections;
-  DisableRemoteSuggestions();
 
   // Build tiles when custom links is not initialized. Tiles should be Top
   // Sites.
@@ -1316,10 +1195,6 @@
       ElementsAre(MatchesTile(kTestTitle, kTestUrl, TileSource::CUSTOM_LINKS)));
 
   // Initiate notification for new Top Sites. This should be ignored.
-  EXPECT_CALL(*mock_custom_links_, IsInitialized()).WillOnce(Return(true));
-  // Notify with an empty SuggestionsProfile first to trigger a query for
-  // TopSites.
-  suggestions_service_callbacks_.Notify(SuggestionsProfile());
   VerifyAndClearExpectations();
   EXPECT_CALL(mock_observer_, OnURLsAvailable(_)).Times(0);
   top_sites_callbacks_.Notify(
@@ -1328,76 +1203,37 @@
 }
 
 TEST_P(MostVisitedSitesWithCustomLinksTest,
-       ShouldFavorCustomLinksOverSuggestions) {
-  const char kTestUrl[] = "http://site1/";
-  const char kTestTitle[] = "Site 1";
-  const char16_t kTestTitle16[] = u"Site 1";
-  std::vector<CustomLinksManager::Link> expected_links(
-      {CustomLinksManager::Link{GURL(kTestUrl), kTestTitle16}});
-  std::map<SectionType, NTPTilesVector> sections;
-
-  // Build tiles when custom links is not initialized. Tiles should be from
-  // suggestions.
-  EXPECT_CALL(*mock_custom_links_, RegisterCallbackForOnChanged(_));
-  ExpectBuildWithSuggestions({MakeSuggestion(kTestTitle, kTestUrl)}, &sections);
-  most_visited_sites_->AddMostVisitedURLsObserver(&mock_observer_,
-                                                  /*max_num_sites=*/1);
-  base::RunLoop().RunUntilIdle();
-  NTPTilesVector tiles = sections.at(SectionType::PERSONALIZED);
-  ASSERT_THAT(tiles.size(), Ge(1ul));
-  ASSERT_THAT(tiles[0], MatchesTile(kTestTitle16, kTestUrl,
-                                    TileSource::SUGGESTIONS_SERVICE));
-
-  // Initialize custom links and rebuild tiles. Tiles should be custom links.
-  EXPECT_CALL(*mock_custom_links_, Initialize(_)).WillOnce(Return(true));
-  ExpectBuildWithCustomLinks(expected_links, &sections);
-  most_visited_sites_->InitializeCustomLinks();
-  most_visited_sites_->RefreshTiles();
-  base::RunLoop().RunUntilIdle();
-  ASSERT_THAT(sections.at(SectionType::PERSONALIZED),
-              ElementsAre(MatchesTile(kTestTitle16, kTestUrl,
-                                      TileSource::CUSTOM_LINKS)));
-
-  // Initiate notification for new suggestions. This should be ignored.
-  EXPECT_CALL(*mock_custom_links_, IsInitialized())
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(mock_observer_, OnURLsAvailable(_)).Times(0);
-  suggestions_service_callbacks_.Notify(
-      MakeProfile({MakeSuggestion("Site 2", "http://site2/")}));
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(MostVisitedSitesWithCustomLinksTest,
        DisableCustomLinksWhenNotInitialized) {
   const char kTestUrl[] = "http://site1/";
-  const char kTestTitle[] = "Site 1";
   const char16_t kTestTitle16[] = u"Site 1";
   std::vector<CustomLinksManager::Link> expected_links(
       {CustomLinksManager::Link{GURL(kTestUrl), kTestTitle16}});
   std::map<SectionType, NTPTilesVector> sections;
 
   // Build tiles when custom links is not initialized. Tiles should be from
-  // suggestions.
+  // Top Sites.
   EXPECT_CALL(*mock_custom_links_, RegisterCallbackForOnChanged(_));
-  ExpectBuildWithSuggestions({MakeSuggestion(kTestTitle, kTestUrl)}, &sections);
+  ExpectBuildWithTopSites(
+      MostVisitedURLList{MakeMostVisitedURL(kTestTitle16, kTestUrl)},
+      &sections);
   most_visited_sites_->AddMostVisitedURLsObserver(&mock_observer_,
                                                   /*max_num_sites=*/1);
   base::RunLoop().RunUntilIdle();
   NTPTilesVector tiles = sections.at(SectionType::PERSONALIZED);
   ASSERT_THAT(tiles.size(), Ge(1ul));
-  ASSERT_THAT(tiles[0], MatchesTile(kTestTitle16, kTestUrl,
-                                    TileSource::SUGGESTIONS_SERVICE));
+  ASSERT_THAT(tiles[0],
+              MatchesTile(kTestTitle16, kTestUrl, TileSource::TOP_SITES));
 
   // Disable custom links. Tiles should rebuild.
-  EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache())
-      .WillOnce(Return(MakeProfile({MakeSuggestion(kTestTitle, kTestUrl)})));
+  EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
+      .WillRepeatedly(base::test::RunOnceCallback<0>(
+          MostVisitedURLList{MakeMostVisitedURL(kTestTitle16, kTestUrl)}));
   EXPECT_CALL(mock_observer_, OnURLsAvailable(_)).Times(1);
   most_visited_sites_->EnableCustomLinks(false);
   base::RunLoop().RunUntilIdle();
 
   // Try to disable custom links again. This should not rebuild the tiles.
-  EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache())
-      .Times(0);
+  EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)).Times(0);
   EXPECT_CALL(*mock_custom_links_, GetLinks()).Times(0);
   most_visited_sites_->EnableCustomLinks(false);
   base::RunLoop().RunUntilIdle();
@@ -1405,7 +1241,6 @@
 
 TEST_P(MostVisitedSitesWithCustomLinksTest, DisableCustomLinksWhenInitialized) {
   const char kTestUrl[] = "http://site1/";
-  const char kTestTitle[] = "Site 1";
   const char16_t kTestTitle16[] = u"Site 1";
   std::vector<CustomLinksManager::Link> expected_links(
       {CustomLinksManager::Link{GURL(kTestUrl), kTestTitle16}});
@@ -1414,12 +1249,7 @@
   // Build tiles when custom links is initialized and not disabled. Tiles should
   // be custom links.
   EXPECT_CALL(*mock_custom_links_, RegisterCallbackForOnChanged(_));
-  EXPECT_CALL(mock_suggestions_service_, AddCallback(_))
-      .WillOnce(Invoke(&suggestions_service_callbacks_,
-                       &SuggestionsService::ResponseCallbackList::Add));
   EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
-  EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData())
-      .WillOnce(Return(false));
   ExpectBuildWithCustomLinks(expected_links, &sections);
   most_visited_sites_->AddMostVisitedURLsObserver(&mock_observer_,
                                                   /*max_num_sites=*/1);
@@ -1428,18 +1258,19 @@
               ElementsAre(MatchesTile(kTestTitle16, kTestUrl,
                                       TileSource::CUSTOM_LINKS)));
 
-  // Disable custom links. Tiles should rebuild and return suggestions.
-  EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache())
-      .WillOnce(Return(MakeProfile({MakeSuggestion(kTestTitle, kTestUrl)})));
+  // Disable custom links. Tiles should rebuild and return Top Sites.
+  EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
+      .WillRepeatedly(base::test::RunOnceCallback<0>(
+          MostVisitedURLList{MakeMostVisitedURL(kTestTitle16, kTestUrl)}));
   EXPECT_CALL(*mock_custom_links_, IsInitialized())
       .WillRepeatedly(Return(false));
   EXPECT_CALL(mock_observer_, OnURLsAvailable(_))
       .WillOnce(SaveArg<0>(&sections));
   most_visited_sites_->EnableCustomLinks(false);
   base::RunLoop().RunUntilIdle();
-  EXPECT_THAT(sections.at(SectionType::PERSONALIZED),
-              ElementsAre(MatchesTile(kTestTitle16, kTestUrl,
-                                      TileSource::SUGGESTIONS_SERVICE)));
+  EXPECT_THAT(
+      sections.at(SectionType::PERSONALIZED),
+      ElementsAre(MatchesTile(kTestTitle16, kTestUrl, TileSource::TOP_SITES)));
 
   // Re-enable custom links. Tiles should rebuild and return custom links.
   ExpectBuildWithCustomLinks(expected_links, &sections);
@@ -1463,7 +1294,6 @@
       u"Books, "
       u"DVDs & more";
   std::map<SectionType, NTPTilesVector> sections;
-  DisableRemoteSuggestions();
 
   // Build tiles from Top Sites. The tiles should have short titles.
   EXPECT_CALL(*mock_custom_links_, RegisterCallbackForOnChanged(_));
@@ -1495,54 +1325,12 @@
 }
 
 TEST_P(MostVisitedSitesWithCustomLinksTest,
-       ShouldGenerateShortTitleForSuggestions) {
-  std::string kTestUrl1 = "https://www.imdb.com/";
-  std::string kTestTitle1 = "IMDb - Movies, TV and Celebrities - IMDb";
-  std::string kTestUrl2 = "https://drive.google.com/";
-  std::string kTestTitle2 =
-      "Google Drive - Cloud Storage & File Backup for Photos, Docs & More";
-  std::string kTestUrl3 = "https://amazon.com/";
-  std::string kTestTitle3 =
-      "Amazon.com: Online Shopping for Electronics, Apparel, Computers, Books, "
-      "DVDs & more";
-  std::map<SectionType, NTPTilesVector> sections;
-
-  // Build tiles from Suggestions. The tiles should have short titles.
-  EXPECT_CALL(*mock_custom_links_, RegisterCallbackForOnChanged(_));
-  ExpectBuildWithSuggestions({MakeSuggestion(kTestTitle1, kTestUrl1),
-                              MakeSuggestion(kTestTitle2, kTestUrl2),
-                              MakeSuggestion(kTestTitle3, kTestUrl3)},
-                             &sections);
-  most_visited_sites_->AddMostVisitedURLsObserver(&mock_observer_,
-                                                  /*max_num_sites=*/3);
-  base::RunLoop().RunUntilIdle();
-
-  NTPTilesVector tiles = sections.at(SectionType::PERSONALIZED);
-  ASSERT_THAT(tiles.size(), Ge(3ul));
-  ASSERT_THAT(
-      tiles[0],
-      MatchesTile(/* The short title generated by the heuristic */ u"IMDb",
-                  kTestUrl1, TileSource::SUGGESTIONS_SERVICE));
-  ASSERT_THAT(
-      tiles[1],
-      MatchesTile(
-          /* The short title generated by the heuristic */ u"Google Drive",
-          kTestUrl2, TileSource::SUGGESTIONS_SERVICE));
-  ASSERT_THAT(
-      tiles[2],
-      MatchesTile(
-          /* The short title generated by the heuristic */ u"Amazon.com",
-          kTestUrl3, TileSource::SUGGESTIONS_SERVICE));
-}
-
-TEST_P(MostVisitedSitesWithCustomLinksTest,
        ShouldNotCrashIfReceiveAnEmptyTitle) {
   std::string kTestUrl1 = "https://site1/";
   std::u16string kTestTitle1 = u"";  // Empty title
   std::string kTestUrl2 = "https://site2/";
   std::u16string kTestTitle2 = u"       ";  // Title only contains spaces
   std::map<SectionType, NTPTilesVector> sections;
-  DisableRemoteSuggestions();
 
   // Build tiles from Top Sites. The tiles should have short titles.
   EXPECT_CALL(*mock_custom_links_, RegisterCallbackForOnChanged(_));
@@ -1568,7 +1356,6 @@
   std::vector<CustomLinksManager::Link> expected_links(
       {CustomLinksManager::Link{GURL(kTestUrl), kTestTitle}});
   std::map<SectionType, NTPTilesVector> sections;
-  DisableRemoteSuggestions();
 
   // Build initial tiles with Top Sites.
   EXPECT_CALL(*mock_custom_links_, RegisterCallbackForOnChanged(_));
@@ -1622,7 +1409,6 @@
   std::vector<CustomLinksManager::Link> expected_links(
       {CustomLinksManager::Link{GURL(kTestUrl), kTestTitle}});
   std::map<SectionType, NTPTilesVector> sections;
-  DisableRemoteSuggestions();
 
   // Build initial tiles with Top Sites.
   EXPECT_CALL(*mock_custom_links_, RegisterCallbackForOnChanged(_));
@@ -1679,7 +1465,6 @@
   std::vector<CustomLinksManager::Link> expected_links(
       {CustomLinksManager::Link{GURL(kTestUrl), kTestTitle}});
   std::map<SectionType, NTPTilesVector> sections;
-  DisableRemoteSuggestions();
 
   // Build initial tiles with Top Sites.
   EXPECT_CALL(*mock_custom_links_, RegisterCallbackForOnChanged(_));
@@ -1747,7 +1532,6 @@
   std::vector<CustomLinksManager::Link> expected_links(
       {CustomLinksManager::Link{GURL(kTestUrl2), kTestTitle2}});
   std::map<SectionType, NTPTilesVector> sections;
-  DisableRemoteSuggestions();
 
   // Build initial tiles with Top Sites.
   base::RepeatingClosure custom_links_callback;
@@ -1798,473 +1582,12 @@
               MatchesTile(kTestTitle1, kTestUrl1, TileSource::TOP_SITES));
 }
 
-// These tests expect Most Likely to be enabled, and exclude Android and iOS,
-// so this will continue to be the case.
+// These exclude Android and iOS.
 INSTANTIATE_TEST_SUITE_P(MostVisitedSitesWithCustomLinksTest,
                          MostVisitedSitesWithCustomLinksTest,
-                         ::testing::Combine(::testing::Bool(),
-                                            ::testing::Values(true)));
+                         ::testing::Bool());
 #endif
 
-class MostVisitedSitesWithCacheHitTest : public MostVisitedSitesTest {
- public:
-  // Constructor sets the common expectations for the case where suggestions
-  // service has cached results when the observer is registered.
-  MostVisitedSitesWithCacheHitTest() {
-    InSequence seq;
-    EXPECT_CALL(mock_suggestions_service_, AddCallback(_))
-        .WillOnce(Invoke(&suggestions_service_callbacks_,
-                         &SuggestionsService::ResponseCallbackList::Add));
-    EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache())
-        .WillOnce(Return(MakeProfile({
-            MakeSuggestion("Site 1", "http://site1/"),
-            MakeSuggestion("Site 2", "http://site2/"),
-            MakeSuggestion("Site 3", "http://site3/"),
-        })));
-
-    if (IsPopularSitesFeatureEnabled()) {
-      EXPECT_CALL(
-          mock_observer_,
-          OnURLsAvailable(Contains(Pair(
-              SectionType::PERSONALIZED,
-              ElementsAre(MatchesTile(u"Site 1", "http://site1/",
-                                      TileSource::SUGGESTIONS_SERVICE),
-                          MatchesTile(u"Site 2", "http://site2/",
-                                      TileSource::SUGGESTIONS_SERVICE),
-                          MatchesTile(u"Site 3", "http://site3/",
-                                      TileSource::SUGGESTIONS_SERVICE),
-                          MatchesTile(u"PopularSite1", "http://popularsite1/",
-                                      TileSource::POPULAR))))));
-    } else {
-      EXPECT_CALL(
-          mock_observer_,
-          OnURLsAvailable(Contains(Pair(
-              SectionType::PERSONALIZED,
-              ElementsAre(MatchesTile(u"Site 1", "http://site1/",
-                                      TileSource::SUGGESTIONS_SERVICE),
-                          MatchesTile(u"Site 2", "http://site2/",
-                                      TileSource::SUGGESTIONS_SERVICE),
-                          MatchesTile(u"Site 3", "http://site3/",
-                                      TileSource::SUGGESTIONS_SERVICE))))));
-    }
-    EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
-    EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData())
-        .WillOnce(Return(true));
-
-    most_visited_sites_->AddMostVisitedURLsObserver(&mock_observer_,
-                                                    /*max_num_sites=*/4);
-    VerifyAndClearExpectations();
-
-    EXPECT_FALSE(suggestions_service_callbacks_.empty());
-    EXPECT_TRUE(top_sites_callbacks_.empty());
-  }
-};
-
-TEST_P(MostVisitedSitesWithCacheHitTest, ShouldFavorSuggestionsServiceCache) {
-  // Constructor sets basic expectations for a suggestions service cache hit.
-}
-
-TEST_P(MostVisitedSitesWithCacheHitTest,
-       ShouldPropagateUpdateBySuggestionsService) {
-  EXPECT_CALL(mock_observer_,
-              OnURLsAvailable(Contains(Pair(
-                  SectionType::PERSONALIZED,
-                  ElementsAre(MatchesTile(u"Site 4", "http://site4/",
-                                          TileSource::SUGGESTIONS_SERVICE),
-                              MatchesTile(u"Site 5", "http://site5/",
-                                          TileSource::SUGGESTIONS_SERVICE),
-                              MatchesTile(u"Site 6", "http://site6/",
-                                          TileSource::SUGGESTIONS_SERVICE),
-                              MatchesTile(u"Site 7", "http://site7/",
-                                          TileSource::SUGGESTIONS_SERVICE))))));
-  suggestions_service_callbacks_.Notify(
-      MakeProfile({MakeSuggestion("Site 4", "http://site4/"),
-                   MakeSuggestion("Site 5", "http://site5/"),
-                   MakeSuggestion("Site 6", "http://site6/"),
-                   MakeSuggestion("Site 7", "http://site7/")}));
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(MostVisitedSitesWithCacheHitTest, ShouldTruncateList) {
-  EXPECT_CALL(
-      mock_observer_,
-      OnURLsAvailable(Contains(Pair(SectionType::PERSONALIZED, SizeIs(4)))));
-  suggestions_service_callbacks_.Notify(
-      MakeProfile({MakeSuggestion("Site 4", "http://site4/"),
-                   MakeSuggestion("Site 5", "http://site5/"),
-                   MakeSuggestion("Site 6", "http://site6/"),
-                   MakeSuggestion("Site 7", "http://site7/"),
-                   MakeSuggestion("Site 8", "http://site8/")}));
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(MostVisitedSitesWithCacheHitTest,
-       ShouldCompleteWithPopularSitesIffEnabled) {
-  if (IsPopularSitesFeatureEnabled()) {
-    EXPECT_CALL(
-        mock_observer_,
-        OnURLsAvailable(Contains(Pair(
-            SectionType::PERSONALIZED,
-            ElementsAre(MatchesTile(u"Site 4", "http://site4/",
-                                    TileSource::SUGGESTIONS_SERVICE),
-                        MatchesTile(u"PopularSite1", "http://popularsite1/",
-                                    TileSource::POPULAR),
-                        MatchesTile(u"PopularSite2", "http://popularsite2/",
-                                    TileSource::POPULAR))))));
-  } else {
-    EXPECT_CALL(
-        mock_observer_,
-        OnURLsAvailable(Contains(
-            Pair(SectionType::PERSONALIZED,
-                 ElementsAre(MatchesTile(u"Site 4", "http://site4/",
-                                         TileSource::SUGGESTIONS_SERVICE))))));
-  }
-  suggestions_service_callbacks_.Notify(
-      MakeProfile({MakeSuggestion("Site 4", "http://site4/")}));
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(MostVisitedSitesWithCacheHitTest,
-       ShouldSwitchToTopSitesIfEmptyUpdateBySuggestionsService) {
-  EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
-      .WillOnce(
-          Invoke(&top_sites_callbacks_, &TopSitesCallbackList::AddUnsafe));
-  suggestions_service_callbacks_.Notify(SuggestionsProfile());
-  VerifyAndClearExpectations();
-
-  EXPECT_CALL(
-      mock_observer_,
-      OnURLsAvailable(Contains(Pair(
-          SectionType::PERSONALIZED,
-          ElementsAre(
-              MatchesTile(u"Site 4", "http://site4/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 5", "http://site5/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 6", "http://site6/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 7", "http://site7/",
-                          TileSource::TOP_SITES))))));
-  top_sites_callbacks_.Notify(
-      MostVisitedURLList({MakeMostVisitedURL(u"Site 4", "http://site4/"),
-                          MakeMostVisitedURL(u"Site 5", "http://site5/"),
-                          MakeMostVisitedURL(u"Site 6", "http://site6/"),
-                          MakeMostVisitedURL(u"Site 7", "http://site7/")}));
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(MostVisitedSitesWithCacheHitTest, ShouldFetchFaviconsIfEnabled) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(kNtpMostLikelyFaviconsFromServerFeature);
-
-  EXPECT_CALL(mock_observer_, OnURLsAvailable(_));
-  EXPECT_CALL(*icon_cacher_, StartFetchMostLikely(GURL("http://site4/"), _));
-
-  suggestions_service_callbacks_.Notify(
-      MakeProfile({MakeSuggestion("Site 4", "http://site4/")}));
-  base::RunLoop().RunUntilIdle();
-}
-
-// Tests only apply when the suggestions service is enabled.
-INSTANTIATE_TEST_SUITE_P(MostVisitedSitesWithCacheHitTest,
-                         MostVisitedSitesWithCacheHitTest,
-                         ::testing::Combine(::testing::Bool(),
-                                            testing::Values(true)));
-
-class MostVisitedSitesWithEmptyCacheTest : public MostVisitedSitesTest {
- public:
-  // Constructor sets the common expectations for the case where suggestions
-  // service doesn't have cached results when the observer is registered.
-  MostVisitedSitesWithEmptyCacheTest() {
-    InSequence seq;
-    EXPECT_CALL(mock_suggestions_service_, AddCallback(_))
-        .WillOnce(Invoke(&suggestions_service_callbacks_,
-                         &SuggestionsService::ResponseCallbackList::Add));
-    EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache())
-        .WillOnce(Return(SuggestionsProfile()));  // Empty cache.
-    EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
-        .WillOnce(
-            Invoke(&top_sites_callbacks_, &TopSitesCallbackList::AddUnsafe));
-    EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
-    EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData())
-        .WillOnce(Return(true));
-
-    most_visited_sites_->AddMostVisitedURLsObserver(&mock_observer_,
-                                                    /*max_num_sites=*/3);
-    VerifyAndClearExpectations();
-
-    EXPECT_FALSE(suggestions_service_callbacks_.empty());
-    EXPECT_FALSE(top_sites_callbacks_.empty());
-  }
-};
-
-TEST_P(MostVisitedSitesWithEmptyCacheTest,
-       ShouldQueryTopSitesAndSuggestionsService) {
-  // Constructor sets basic expectations for a suggestions service cache miss.
-}
-
-TEST_P(MostVisitedSitesWithEmptyCacheTest,
-       ShouldCompleteWithPopularSitesIffEnabled) {
-  if (IsPopularSitesFeatureEnabled()) {
-    EXPECT_CALL(
-        mock_observer_,
-        OnURLsAvailable(Contains(Pair(
-            SectionType::PERSONALIZED,
-            ElementsAre(MatchesTile(u"Site 4", "http://site4/",
-                                    TileSource::SUGGESTIONS_SERVICE),
-                        MatchesTile(u"PopularSite1", "http://popularsite1/",
-                                    TileSource::POPULAR),
-                        MatchesTile(u"PopularSite2", "http://popularsite2/",
-                                    TileSource::POPULAR))))));
-  } else {
-    EXPECT_CALL(
-        mock_observer_,
-        OnURLsAvailable(Contains(
-            Pair(SectionType::PERSONALIZED,
-                 ElementsAre(MatchesTile(u"Site 4", "http://site4/",
-                                         TileSource::SUGGESTIONS_SERVICE))))));
-  }
-  suggestions_service_callbacks_.Notify(
-      MakeProfile({MakeSuggestion("Site 4", "http://site4/")}));
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(MostVisitedSitesWithEmptyCacheTest,
-       ShouldIgnoreTopSitesIfSuggestionsServiceFaster) {
-  // Reply from suggestions service triggers and update to our observer.
-  EXPECT_CALL(mock_observer_,
-              OnURLsAvailable(Contains(Pair(
-                  SectionType::PERSONALIZED,
-                  ElementsAre(MatchesTile(u"Site 1", "http://site1/",
-                                          TileSource::SUGGESTIONS_SERVICE),
-                              MatchesTile(u"Site 2", "http://site2/",
-                                          TileSource::SUGGESTIONS_SERVICE),
-                              MatchesTile(u"Site 3", "http://site3/",
-                                          TileSource::SUGGESTIONS_SERVICE))))));
-  suggestions_service_callbacks_.Notify(
-      MakeProfile({MakeSuggestion("Site 1", "http://site1/"),
-                   MakeSuggestion("Site 2", "http://site2/"),
-                   MakeSuggestion("Site 3", "http://site3/")}));
-  VerifyAndClearExpectations();
-
-  // Reply from top sites is ignored (i.e. not reported to observer).
-  top_sites_callbacks_.Notify(
-      MostVisitedURLList({MakeMostVisitedURL(u"Site 4", "http://site4/")}));
-  VerifyAndClearExpectations();
-
-  // Update by TopSites is also ignored.
-  mock_top_sites_->NotifyTopSitesChanged(
-      history::TopSitesObserver::ChangeReason::MOST_VISITED);
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(MostVisitedSitesWithEmptyCacheTest,
-       ShouldExposeTopSitesIfSuggestionsServiceFasterButEmpty) {
-  // Empty reply from suggestions service causes no update to our observer.
-  suggestions_service_callbacks_.Notify(SuggestionsProfile());
-  VerifyAndClearExpectations();
-
-  // Reply from top sites is propagated to observer.
-  EXPECT_CALL(
-      mock_observer_,
-      OnURLsAvailable(Contains(Pair(
-          SectionType::PERSONALIZED,
-          ElementsAre(
-              MatchesTile(u"Site 1", "http://site1/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 2", "http://site2/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 3", "http://site3/",
-                          TileSource::TOP_SITES))))));
-  top_sites_callbacks_.Notify(
-      MostVisitedURLList({MakeMostVisitedURL(u"Site 1", "http://site1/"),
-                          MakeMostVisitedURL(u"Site 2", "http://site2/"),
-                          MakeMostVisitedURL(u"Site 3", "http://site3/")}));
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(MostVisitedSitesWithEmptyCacheTest,
-       ShouldFavorSuggestionsServiceAlthoughSlower) {
-  // Reply from top sites is propagated to observer.
-  EXPECT_CALL(
-      mock_observer_,
-      OnURLsAvailable(Contains(Pair(
-          SectionType::PERSONALIZED,
-          ElementsAre(
-              MatchesTile(u"Site 1", "http://site1/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 2", "http://site2/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 3", "http://site3/",
-                          TileSource::TOP_SITES))))));
-  top_sites_callbacks_.Notify(
-      MostVisitedURLList({MakeMostVisitedURL(u"Site 1", "http://site1/"),
-                          MakeMostVisitedURL(u"Site 2", "http://site2/"),
-                          MakeMostVisitedURL(u"Site 3", "http://site3/")}));
-  VerifyAndClearExpectations();
-
-  // Reply from suggestions service overrides top sites.
-  InSequence seq;
-  EXPECT_CALL(mock_observer_,
-              OnURLsAvailable(Contains(Pair(
-                  SectionType::PERSONALIZED,
-                  ElementsAre(MatchesTile(u"Site 4", "http://site4/",
-                                          TileSource::SUGGESTIONS_SERVICE),
-                              MatchesTile(u"Site 5", "http://site5/",
-                                          TileSource::SUGGESTIONS_SERVICE),
-                              MatchesTile(u"Site 6", "http://site6/",
-                                          TileSource::SUGGESTIONS_SERVICE))))));
-  suggestions_service_callbacks_.Notify(
-      MakeProfile({MakeSuggestion("Site 4", "http://site4/"),
-                   MakeSuggestion("Site 5", "http://site5/"),
-                   MakeSuggestion("Site 6", "http://site6/")}));
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(MostVisitedSitesWithEmptyCacheTest,
-       ShouldIgnoreSuggestionsServiceIfSlowerAndEmpty) {
-  // Reply from top sites is propagated to observer.
-  EXPECT_CALL(
-      mock_observer_,
-      OnURLsAvailable(Contains(Pair(
-          SectionType::PERSONALIZED,
-          ElementsAre(
-              MatchesTile(u"Site 1", "http://site1/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 2", "http://site2/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 3", "http://site3/",
-                          TileSource::TOP_SITES))))));
-  top_sites_callbacks_.Notify(
-      MostVisitedURLList({MakeMostVisitedURL(u"Site 1", "http://site1/"),
-                          MakeMostVisitedURL(u"Site 2", "http://site2/"),
-                          MakeMostVisitedURL(u"Site 3", "http://site3/")}));
-  VerifyAndClearExpectations();
-
-  // Reply from suggestions service is empty and thus ignored.
-  suggestions_service_callbacks_.Notify(SuggestionsProfile());
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(MostVisitedSitesWithEmptyCacheTest, ShouldPropagateUpdateByTopSites) {
-  // Reply from top sites is propagated to observer.
-  EXPECT_CALL(
-      mock_observer_,
-      OnURLsAvailable(Contains(Pair(
-          SectionType::PERSONALIZED,
-          ElementsAre(
-              MatchesTile(u"Site 1", "http://site1/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 2", "http://site2/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 3", "http://site3/",
-                          TileSource::TOP_SITES))))));
-  top_sites_callbacks_.Notify(
-      MostVisitedURLList({MakeMostVisitedURL(u"Site 1", "http://site1/"),
-                          MakeMostVisitedURL(u"Site 2", "http://site2/"),
-                          MakeMostVisitedURL(u"Site 3", "http://site3/")}));
-  VerifyAndClearExpectations();
-
-  // Reply from suggestions service is empty and thus ignored.
-  suggestions_service_callbacks_.Notify(SuggestionsProfile());
-  VerifyAndClearExpectations();
-  EXPECT_TRUE(top_sites_callbacks_.empty());
-
-  // Update from top sites is propagated to observer.
-  EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
-      .WillOnce(base::test::RunOnceCallback<0>(
-          MostVisitedURLList{MakeMostVisitedURL(u"Site 4", "http://site4/"),
-                             MakeMostVisitedURL(u"Site 5", "http://site5/"),
-                             MakeMostVisitedURL(u"Site 6", "http://site6/")}));
-  EXPECT_CALL(
-      mock_observer_,
-      OnURLsAvailable(Contains(Pair(
-          SectionType::PERSONALIZED,
-          ElementsAre(
-              MatchesTile(u"Site 4", "http://site4/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 5", "http://site5/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 6", "http://site6/",
-                          TileSource::TOP_SITES))))));
-  mock_top_sites_->NotifyTopSitesChanged(
-      history::TopSitesObserver::ChangeReason::MOST_VISITED);
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(MostVisitedSitesWithEmptyCacheTest,
-       ShouldSendEmptyListIfBothTopSitesAndSuggestionsServiceEmpty) {
-  if (IsPopularSitesFeatureEnabled()) {
-    EXPECT_CALL(
-        mock_observer_,
-        OnURLsAvailable(Contains(Pair(
-            SectionType::PERSONALIZED,
-            ElementsAre(MatchesTile(u"PopularSite1", "http://popularsite1/",
-                                    TileSource::POPULAR),
-                        MatchesTile(u"PopularSite2", "http://popularsite2/",
-                                    TileSource::POPULAR))))));
-  } else {
-    // The Android NTP doesn't finish initialization until it gets tiles, so a
-    // 0-tile notification is always needed.
-    EXPECT_CALL(mock_observer_, OnURLsAvailable(ElementsAre(Pair(
-                                    SectionType::PERSONALIZED, IsEmpty()))));
-  }
-  suggestions_service_callbacks_.Notify(SuggestionsProfile());
-  top_sites_callbacks_.Notify(MostVisitedURLList());
-
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(MostVisitedSitesWithEmptyCacheTest,
-       ShouldStillNotifyIfTopSitesUnchanged) {
-  EXPECT_CALL(
-      mock_observer_,
-      OnURLsAvailable(Contains(Pair(
-          SectionType::PERSONALIZED,
-          ElementsAre(
-              MatchesTile(u"Site 1", "http://site1/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 2", "http://site2/", TileSource::TOP_SITES),
-              MatchesTile(u"Site 3", "http://site3/",
-                          TileSource::TOP_SITES))))))
-      .Times(5);
-
-  suggestions_service_callbacks_.Notify(SuggestionsProfile());
-
-  top_sites_callbacks_.Notify(
-      MostVisitedURLList({MakeMostVisitedURL(u"Site 1", "http://site1/"),
-                          MakeMostVisitedURL(u"Site 2", "http://site2/"),
-                          MakeMostVisitedURL(u"Site 3", "http://site3/")}));
-  base::RunLoop().RunUntilIdle();
-
-  for (int i = 0; i < 4; ++i) {
-    EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_))
-        .WillOnce(
-            Invoke(&top_sites_callbacks_, &TopSitesCallbackList::AddUnsafe));
-    mock_top_sites_->NotifyTopSitesChanged(
-        history::TopSitesObserver::ChangeReason::MOST_VISITED);
-    EXPECT_FALSE(top_sites_callbacks_.empty());
-    top_sites_callbacks_.Notify(
-        MostVisitedURLList({MakeMostVisitedURL(u"Site 1", "http://site1/"),
-                            MakeMostVisitedURL(u"Site 2", "http://site2/"),
-                            MakeMostVisitedURL(u"Site 3", "http://site3/")}));
-    base::RunLoop().RunUntilIdle();
-  }
-}
-
-TEST_P(MostVisitedSitesWithEmptyCacheTest,
-       ShouldStillNotifyIfSuggestionsUnchanged) {
-  EXPECT_CALL(mock_observer_,
-              OnURLsAvailable(Contains(Pair(
-                  SectionType::PERSONALIZED,
-                  ElementsAre(MatchesTile(u"Site 1", "http://site1/",
-                                          TileSource::SUGGESTIONS_SERVICE),
-                              MatchesTile(u"Site 2", "http://site2/",
-                                          TileSource::SUGGESTIONS_SERVICE),
-                              MatchesTile(u"Site 3", "http://site3/",
-                                          TileSource::SUGGESTIONS_SERVICE))))))
-      .Times(5);
-
-  for (int i = 0; i < 5; ++i) {
-    suggestions_service_callbacks_.Notify(
-        MakeProfile({MakeSuggestion("Site 1", "http://site1/"),
-                     MakeSuggestion("Site 2", "http://site2/"),
-                     MakeSuggestion("Site 3", "http://site3/")}));
-  }
-}
-
-// Tests only apply when the suggestions service is enabled.
-INSTANTIATE_TEST_SUITE_P(MostVisitedSitesWithEmptyCacheTest,
-                         MostVisitedSitesWithEmptyCacheTest,
-                         ::testing::Combine(::testing::Bool(),
-                                            testing::Values(true)));
-
 // This a test for MostVisitedSites::MergeTiles(...) method, and thus has the
 // same scope as the method itself. This tests merging popular sites with
 // personal tiles.
diff --git a/components/ntp_tiles/tile_source.h b/components/ntp_tiles/tile_source.h
index ea067c1d..b758725 100644
--- a/components/ntp_tiles/tile_source.h
+++ b/components/ntp_tiles/tile_source.h
@@ -14,8 +14,6 @@
 enum class TileSource {
   // Tile comes from the personal top sites list, based on local history.
   TOP_SITES,
-  // Tile comes from the suggestions service, based on synced history.
-  SUGGESTIONS_SERVICE,
   // Tile is regionally popular.
   POPULAR,
   // Tile is a popular site baked into the binary.
diff --git a/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc b/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc
index f879bad5..07fdfea 100644
--- a/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc
+++ b/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc
@@ -79,12 +79,6 @@
                           base::Unretained(this)));
 
   client_->RegisterMessageCallback(
-      "fetchSuggestions",
-      base::BindRepeating(
-          &NTPTilesInternalsMessageHandler::HandleFetchSuggestions,
-          base::Unretained(this)));
-
-  client_->RegisterMessageCallback(
       "viewPopularSitesJson",
       base::BindRepeating(
           &NTPTilesInternalsMessageHandler::HandleViewPopularSitesJson,
@@ -96,7 +90,6 @@
   if (!client_->SupportsNTPTiles()) {
     base::Value disabled(base::Value::Type::DICTIONARY);
     disabled.SetBoolKey("topSites", false);
-    disabled.SetBoolKey("suggestionsService", false);
     disabled.SetBoolKey("popular", false);
     disabled.SetBoolKey("customLinks", false);
     disabled.SetBoolKey("allowlist", false);
@@ -108,7 +101,6 @@
   }
   DCHECK_EQ(0u, args->GetSize());
 
-  suggestions_status_.clear();
   popular_sites_json_.clear();
   most_visited_sites_ = client_->MakeMostVisitedSites();
   most_visited_sites_->AddMostVisitedURLsObserver(this, site_count_);
@@ -175,22 +167,6 @@
   SendSourceInfo();
 }
 
-void NTPTilesInternalsMessageHandler::HandleFetchSuggestions(
-    const base::ListValue* args) {
-  DCHECK_EQ(0u, args->GetSize());
-  if (!most_visited_sites_->DoesSourceExist(
-          ntp_tiles::TileSource::SUGGESTIONS_SERVICE)) {
-    return;
-  }
-
-  if (most_visited_sites_->suggestions()->FetchSuggestionsData()) {
-    suggestions_status_ = "fetching...";
-  } else {
-    suggestions_status_ = "history sync is disabled, or not yet initialized";
-  }
-  SendSourceInfo();
-}
-
 void NTPTilesInternalsMessageHandler::HandleViewPopularSitesJson(
     const base::ListValue* args) {
   DCHECK_EQ(0u, args->GetSize());
@@ -214,12 +190,6 @@
   value.SetBoolKey("allowlist",
                    most_visited_sites_->DoesSourceExist(TileSource::ALLOWLIST));
 
-  if (most_visited_sites_->DoesSourceExist(TileSource::SUGGESTIONS_SERVICE)) {
-    value.SetStringKey("suggestionsService.status", suggestions_status_);
-  } else {
-    value.SetBoolKey("suggestionsService", false);
-  }
-
   if (most_visited_sites_->DoesSourceExist(TileSource::POPULAR)) {
     auto* popular_sites = most_visited_sites_->popular_sites();
     value.SetStringKey("popular.url", popular_sites->GetURLToFetch().spec());
diff --git a/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.h b/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.h
index 63ee466..b50d2ada 100644
--- a/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.h
+++ b/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.h
@@ -78,7 +78,6 @@
   int site_count_;
   std::unique_ptr<MostVisitedSites> most_visited_sites_;
 
-  std::string suggestions_status_;
   std::string popular_sites_json_;
 
   base::CancelableTaskTracker cancelable_task_tracker_;
diff --git a/components/ntp_tiles/webui/resources/ntp_tiles_internals.html b/components/ntp_tiles/webui/resources/ntp_tiles_internals.html
index 3a97d0e..3165479f 100644
--- a/components/ntp_tiles/webui/resources/ntp_tiles_internals.html
+++ b/components/ntp_tiles/webui/resources/ntp_tiles_internals.html
@@ -31,21 +31,6 @@
           <td class="value" jsdisplay="$this">yes</td>
           <td class="value" jsdisplay="!$this">no</td>
         </tr>
-        <tr>
-          <th colspan="2">SUGGESTIONS_SERVICE</th>
-        </tr>
-      </tbody>
-      <tbody jsselect="suggestionsService">
-        <tr jsdisplay="$this">
-          <td class="detail">
-            <input id="suggestions-fetch" type="submit" value="Fetch">
-          </td>
-          <td class="value" jscontent="status"></td>
-        </tr>
-        <tr jsdisplay="!$this">
-          <td class="detail">enabled</td>
-          <td class="value">no</td>
-        </tr>
       </tbody>
       <tbody jsselect="popular">
         <tr>
@@ -115,12 +100,11 @@
           <td class="detail">Source</td>
           <td class="value" jsdisplay="source &lt; 0">???</td>
           <td class="value" jsdisplay="source == 0">TOP_SITES</td>
-          <td class="value" jsdisplay="source == 1">SUGGESTIONS_SERVICE</td>
-          <td class="value" jsdisplay="source == 2">POPULAR</td>
-          <td class="value" jsdisplay="source == 3">POPULAR_BAKED_IN</td>
-          <td class="value" jsdisplay="source == 4">CUSTOM_LINKS</td>
-          <td class="value" jsdisplay="source == 5">ALLOWLIST</td>
-          <td class="value" jsdisplay="source == 6">HOMEPAGE</td>
+          <td class="value" jsdisplay="source == 1">POPULAR</td>
+          <td class="value" jsdisplay="source == 2">POPULAR_BAKED_IN</td>
+          <td class="value" jsdisplay="source == 3">CUSTOM_LINKS</td>
+          <td class="value" jsdisplay="source == 4">ALLOWLIST</td>
+          <td class="value" jsdisplay="source == 5">HOMEPAGE</td>
           <td class="value" jsdisplay="source &gt; 6">???</td>
         </tr>
         <tr>
diff --git a/components/ntp_tiles/webui/resources/ntp_tiles_internals.js b/components/ntp_tiles/webui/resources/ntp_tiles_internals.js
index ff27a08..d56537a 100644
--- a/components/ntp_tiles/webui/resources/ntp_tiles_internals.js
+++ b/components/ntp_tiles/webui/resources/ntp_tiles_internals.js
@@ -23,11 +23,6 @@
                 }]);
   });
 
-  $('suggestions-fetch').addEventListener('click', function(event) {
-    event.preventDefault();
-    chrome.send('fetchSuggestions');
-  });
-
   $('popular-view-json').addEventListener('click', function(event) {
     event.preventDefault();
     if ($('popular-json-value').textContent === '') {
diff --git a/components/omnibox/browser/history_quick_provider_performance_unittest.cc b/components/omnibox/browser/history_quick_provider_performance_unittest.cc
index e71114a1..7efeb9b 100644
--- a/components/omnibox/browser/history_quick_provider_performance_unittest.cc
+++ b/components/omnibox/browser/history_quick_provider_performance_unittest.cc
@@ -32,7 +32,7 @@
 std::string GenerateFakeHashedString(size_t sym_count) {
   static constexpr char kSyms[] =
       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/=+?#";
-  static base::NoDestructor<std::mt19937> engine;
+  static std::mt19937 engine;
   std::uniform_int_distribution<size_t> index_distribution(
       0, base::size(kSyms) - 2 /* trailing \0 */);
 
@@ -40,7 +40,7 @@
   res.reserve(sym_count);
 
   std::generate_n(std::back_inserter(res), sym_count, [&index_distribution] {
-    return kSyms[index_distribution(*engine)];
+    return kSyms[index_distribution(engine)];
   });
 
   return res;
diff --git a/components/password_manager/core/browser/password_scripts_fetcher_impl.cc b/components/password_manager/core/browser/password_scripts_fetcher_impl.cc
index bd8acc8..703316a 100644
--- a/components/password_manager/core/browser/password_scripts_fetcher_impl.cc
+++ b/components/password_manager/core/browser/password_scripts_fetcher_impl.cc
@@ -9,7 +9,6 @@
 #include "base/json/json_reader.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
-#include "base/no_destructor.h"
 #include "base/version.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -165,7 +164,7 @@
 }
 
 void PasswordScriptsFetcherImpl::StartFetch() {
-  static const base::NoDestructor<base::TimeDelta> kFetchTimeout(
+  static const base::TimeDelta kFetchTimeout(
       base::TimeDelta::FromSeconds(kFetchTimeoutInSeconds));
   if (url_loader_)
     return;
@@ -195,7 +194,7 @@
         })");
   url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
                                                  traffic_annotation);
-  url_loader_->SetTimeoutDuration(*kFetchTimeout);
+  url_loader_->SetTimeoutDuration(kFetchTimeout);
   url_loader_->DownloadToString(
       url_loader_factory_.get(),
       base::BindOnce(&PasswordScriptsFetcherImpl::OnFetchComplete,
@@ -268,10 +267,10 @@
 }
 
 bool PasswordScriptsFetcherImpl::IsCacheStale() const {
-  static const base::NoDestructor<base::TimeDelta> kCacheTimeout(
+  static const base::TimeDelta kCacheTimeout(
       base::TimeDelta::FromMinutes(kCacheTimeoutInMinutes));
   return last_fetch_timestamp_.is_null() ||
-         base::TimeTicks::Now() - last_fetch_timestamp_ >= *kCacheTimeout;
+         base::TimeTicks::Now() - last_fetch_timestamp_ >= kCacheTimeout;
 }
 
 void PasswordScriptsFetcherImpl::RunResponseCallback(
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index cd9e734..2cf67a7f 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -139,6 +139,11 @@
 const base::Feature kSecondaryServerFieldPredictions = {
     "SecondaryServerFieldPredictions", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables the addition of passwords in Chrome Settings.
+// TODO(crbug/1226008): Remove once it's launched.
+const base::Feature kSupportForAddPasswordsInSettings = {
+    "SupportForAddPasswordsInSettings", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Treat heuritistics to find new password fields as reliable. This enables
 // password generation on more forms, but could lead to false positives.
 const base::Feature kTreatNewPasswordHeuristicsAsReliable = {
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h
index dfccebb2..0774350c 100644
--- a/components/password_manager/core/common/password_manager_features.h
+++ b/components/password_manager/core/common/password_manager_features.h
@@ -39,6 +39,7 @@
 extern const base::Feature kRecoverFromNeverSaveAndroid;
 extern const base::Feature kReparseServerPredictionsFollowingFormChange;
 extern const base::Feature kSecondaryServerFieldPredictions;
+extern const base::Feature kSupportForAddPasswordsInSettings;
 extern const base::Feature kTreatNewPasswordHeuristicsAsReliable;
 extern const base::Feature kUnifiedPasswordManagerAndroid;
 extern const base::Feature kUseNewHeaderForLegacySavePasswordBubble;
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java
index 632697b..86fc117 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java
@@ -22,6 +22,7 @@
     public static final String PAYMENT_REQUEST_SKIP_TO_GPAY = "PaymentRequestSkipToGPay";
     public static final String PAYMENT_REQUEST_SKIP_TO_GPAY_IF_NO_CARD =
             "PaymentRequestSkipToGPayIfNoCard";
+    public static final String SECURE_PAYMENT_CONFIRMATION = "SecurePaymentConfirmationBrowser";
     public static final String SERVICE_WORKER_PAYMENT_APPS = "ServiceWorkerPaymentApps";
     public static final String STRICT_HAS_ENROLLED_AUTOFILL_INSTRUMENT =
             "StrictHasEnrolledAutofillInstrument";
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java
index 04da245..1d077c217 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java
@@ -531,6 +531,16 @@
                     PaymentErrorReason.INVALID_DATA_FROM_RENDERER);
             return false;
         }
+        if (PaymentFeatureList.isEnabledOrExperimentalFeaturesEnabled(
+                    PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION)
+                && methodData.containsKey(MethodStrings.SECURE_PAYMENT_CONFIRMATION)
+                && (methodData.size() > 1 || options.requestPayerEmail || options.requestPayerPhone
+                        || options.requestShipping || options.requestPayerName)) {
+            mJourneyLogger.setAborted(AbortReason.INVALID_DATA_FROM_RENDERER);
+            disconnectFromClientWithDebugMessage(ErrorStrings.INVALID_PAYMENT_METHODS_OR_DATA,
+                    PaymentErrorReason.INVALID_DATA_FROM_RENDERER);
+            return false;
+        }
         mBrowserPaymentRequest.modifyMethodDataIfNeeded(methodData);
         methodData = Collections.unmodifiableMap(methodData);
 
@@ -888,7 +898,9 @@
         PaymentApp selectedApp = mBrowserPaymentRequest.getSelectedPaymentApp();
         // TODO(crbug.com/1211947): Deduplicate this part with
         // SecurePaymentConfirmationController::SetupModelAndShowDialogIfApplicable().
-        return selectedApp != null && selectedApp.getPaymentAppType() == PaymentAppType.INTERNAL
+        return PaymentFeatureList.isEnabledOrExperimentalFeaturesEnabled(
+                       PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION)
+                && selectedApp != null && selectedApp.getPaymentAppType() == PaymentAppType.INTERNAL
                 && selectedApp.getInstrumentMethodNames().size() == 1
                 && selectedApp.getInstrumentMethodNames().contains(
                         MethodStrings.SECURE_PAYMENT_CONFIRMATION)
diff --git a/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentRequestServiceTest.java b/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentRequestServiceTest.java
index 0efea44..5a7d2f91 100644
--- a/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentRequestServiceTest.java
+++ b/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentRequestServiceTest.java
@@ -162,6 +162,7 @@
     @After
     public void tearDown() {
         PaymentRequestService.resetShowingPaymentRequestForTest();
+        ShadowPaymentFeatureList.reset();
     }
 
     @Override
@@ -709,4 +710,37 @@
         show(service2);
         assertErrorAndReason(ErrorStrings.ANOTHER_UI_SHOWING, PaymentErrorReason.ALREADY_SHOWING);
     }
+
+    @Test
+    @Feature({"Payments"})
+    public void testSpcCanOnlyBeRequestedAlone_success() {
+        ShadowPaymentFeatureList.setEnabledFeature(PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION);
+        Assert.assertNotNull(defaultBuilder().setOnlySpcMethodWithoutPaymentOptions().build());
+    }
+
+    @Test
+    @Feature({"Payments"})
+    public void testSpcCanOnlyBeRequestedAlone_failedForHavingOptions() {
+        ShadowPaymentFeatureList.setEnabledFeature(PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION);
+        PaymentOptions options = new PaymentOptions();
+        options.requestShipping = true;
+        Assert.assertNull(defaultBuilder()
+                                  .setOnlySpcMethodWithoutPaymentOptions()
+                                  .setOptions(options)
+                                  .build());
+        assertErrorAndReason(ErrorStrings.INVALID_PAYMENT_METHODS_OR_DATA,
+                PaymentErrorReason.INVALID_DATA_FROM_RENDERER);
+    }
+
+    // The restriction is imposed only when the SPC flag is enabled.
+    @Test
+    @Feature({"Payments"})
+    public void testSpcCanOnlyBeRequestedAlone_notApplicableWhenSpcDisabled() {
+        PaymentOptions options = new PaymentOptions();
+        options.requestShipping = true;
+        Assert.assertNotNull(defaultBuilder()
+                                     .setOnlySpcMethodWithoutPaymentOptions()
+                                     .setOptions(options)
+                                     .build());
+    }
 }
diff --git a/components/payments/content/android/junit/src/org/chromium/components/payments/test_support/PaymentRequestServiceBuilder.java b/components/payments/content/android/junit/src/org/chromium/components/payments/test_support/PaymentRequestServiceBuilder.java
index 2154116..08ae6e3 100644
--- a/components/payments/content/android/junit/src/org/chromium/components/payments/test_support/PaymentRequestServiceBuilder.java
+++ b/components/payments/content/android/junit/src/org/chromium/components/payments/test_support/PaymentRequestServiceBuilder.java
@@ -10,6 +10,7 @@
 
 import org.chromium.components.payments.BrowserPaymentRequest;
 import org.chromium.components.payments.JourneyLogger;
+import org.chromium.components.payments.MethodStrings;
 import org.chromium.components.payments.PaymentAppService;
 import org.chromium.components.payments.PaymentRequestService;
 import org.chromium.components.payments.PaymentRequestService.Delegate;
@@ -223,6 +224,14 @@
         return this;
     }
 
+    public PaymentRequestServiceBuilder setOnlySpcMethodWithoutPaymentOptions() {
+        mMethodData = new PaymentMethodData[1];
+        mMethodData[0] = new PaymentMethodData();
+        mMethodData[0].supportedMethod = MethodStrings.SECURE_PAYMENT_CONFIRMATION;
+        mOptions = new PaymentOptions();
+        return this;
+    }
+
     public PaymentRequestServiceBuilder setGooglePayBridgeEligible(boolean eligible) {
         mGooglePayBridgeEligible = eligible;
         return this;
diff --git a/components/payments/content/android/payment_feature_list.cc b/components/payments/content/android/payment_feature_list.cc
index a1fc98f..64653e5 100644
--- a/components/payments/content/android/payment_feature_list.cc
+++ b/components/payments/content/android/payment_feature_list.cc
@@ -20,6 +20,7 @@
 // components/payments/core/features.h, content/public/common/content_features.h
 // or the .h file (for Android only features).
 const base::Feature* const kFeaturesExposedToJava[] = {
+    &::features::kSecurePaymentConfirmation,
     &::features::kServiceWorkerPaymentApps,
     &::features::kWebPayments,
     &::features::kWebPaymentsMinimalUI,
diff --git a/components/performance_manager/performance_manager_lifetime.cc b/components/performance_manager/performance_manager_lifetime.cc
index f0517f5..156f988 100644
--- a/components/performance_manager/performance_manager_lifetime.cc
+++ b/components/performance_manager/performance_manager_lifetime.cc
@@ -32,8 +32,8 @@
 }
 
 absl::optional<Decorators>* GetDecoratorsOverride() {
-  static base::NoDestructor<absl::optional<Decorators>> decorators_override;
-  return decorators_override.get();
+  static absl::optional<Decorators> decorators_override;
+  return &decorators_override;
 }
 
 void OnGraphCreated(Decorators decorators,
diff --git a/components/policy/core/browser/BUILD.gn b/components/policy/core/browser/BUILD.gn
index 75d6559..b3eb3275 100644
--- a/components/policy/core/browser/BUILD.gn
+++ b/components/policy/core/browser/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
 
 group("browser") {
   if (is_component_build) {
@@ -87,6 +88,10 @@
     "//net",
     "//third_party/icu",
   ]
+
+  if (use_libfuzzer) {
+    visibility += [ "//chrome/browser/chromeos:policy_fuzzer" ]
+  }
 }
 
 static_library("test_support") {
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index 53fed4b0..12025c3 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -1304,7 +1304,7 @@
   // Available memory, in KiB.
   optional uint32 available_memory_kib = 3;
   // Number of page faults since the last boot.
-  optional uint32 page_faults_since_last_boot = 4;
+  optional uint64 page_faults_since_last_boot = 4;
 }
 
 // Information about the device's backlights.
diff --git a/components/search/ntp_features.cc b/components/search/ntp_features.cc
index 32ee416..ebe5364b 100644
--- a/components/search/ntp_features.cc
+++ b/components/search/ntp_features.cc
@@ -122,6 +122,8 @@
     "NtpDriveModuleManagedUsersOnlyParam";
 const char kNtpDriveModuleCacheMaxAgeSParam[] =
     "NtpDriveModuleCacheMaxAgeSParam";
+const char kNtpDriveModuleExperimentGroupParam[] =
+    "NtpDriveModuleExperimentGroupParam";
 
 base::TimeDelta GetModulesLoadTimeout() {
   std::string param_value = base::GetFieldTrialParamValueByFeature(
diff --git a/components/search/ntp_features.h b/components/search/ntp_features.h
index 147056cd..5e2a7c4 100644
--- a/components/search/ntp_features.h
+++ b/components/search/ntp_features.h
@@ -64,6 +64,9 @@
 extern const char kNtpDriveModuleManagedUsersOnlyParam[];
 // Parameter determining the max age in seconds of the cache for drive data.
 extern const char kNtpDriveModuleCacheMaxAgeSParam[];
+// Parameter for communicating the experiment group of the Drive module
+// experiment.
+extern const char kNtpDriveModuleExperimentGroupParam[];
 
 // Returns the timeout after which the load of a module should be aborted.
 base::TimeDelta GetModulesLoadTimeout();
diff --git a/components/site_isolation/site_isolation_policy_unittest.cc b/components/site_isolation/site_isolation_policy_unittest.cc
index a9f9bf6..0f0df68 100644
--- a/components/site_isolation/site_isolation_policy_unittest.cc
+++ b/components/site_isolation/site_isolation_policy_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/site_isolation/site_isolation_policy.h"
 
 #include "base/base_switches.h"
+#include "base/no_destructor.h"
 #include "base/system/sys_info.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_entropy_provider.h"
diff --git a/components/translate/content/renderer/translate_agent.cc b/components/translate/content/renderer/translate_agent.cc
index b18415c..e94c3dae 100644
--- a/components/translate/content/renderer/translate_agent.cc
+++ b/components/translate/content/renderer/translate_agent.cc
@@ -15,6 +15,7 @@
 #include "base/location.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/histogram_macros_local.h"
+#include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
diff --git a/components/viz/common/viz_utils.cc b/components/viz/common/viz_utils.cc
index c7a8265..d50d6a0 100644
--- a/components/viz/common/viz_utils.cc
+++ b/components/viz/common/viz_utils.cc
@@ -14,7 +14,6 @@
 #include <string>
 
 #include "base/android/build_info.h"
-#include "base/no_destructor.h"
 #endif
 
 namespace viz {
@@ -37,7 +36,8 @@
   if (command_line.HasSwitch(kDisableWCGForTest))
     return false;
 
-  auto compute_always_use_wide_color_gamut = []() {
+  // As it takes some work to compute this, cache the result.
+  static bool is_always_use_wide_color_gamut_enabled = [] {
     const char* current_model =
         base::android::BuildInfo::GetInstance()->model();
     const std::array<std::string, 2> enabled_models = {
@@ -48,12 +48,9 @@
     }
 
     return false;
-  };
+  }();
 
-  // As it takes some work to compute this, cache the result.
-  static base::NoDestructor<bool> is_always_use_wide_color_gamut_enabled(
-      compute_always_use_wide_color_gamut());
-  return *is_always_use_wide_color_gamut_enabled;
+  return is_always_use_wide_color_gamut_enabled;
 }
 #endif
 
diff --git a/components/viz/service/display_embedder/skia_output_device.cc b/components/viz/service/display_embedder/skia_output_device.cc
index 3a123f8..e1521f3 100644
--- a/components/viz/service/display_embedder/skia_output_device.cc
+++ b/components/viz/service/display_embedder/skia_output_device.cc
@@ -233,6 +233,38 @@
   }
 
   pending_swaps_.pop();
+
+  // If there are skipped swaps at the front of the queue, they are now ready
+  // to be acknowledged without breaking ordering.
+  if (!pending_swaps_.empty()) {
+    auto iter = skipped_swap_info_.find(pending_swaps_.front().SwapId());
+    if (iter != skipped_swap_info_.end()) {
+      OutputSurfaceFrame frame = std::move(iter->second);
+      gfx::Size frame_size = frame.size;
+      skipped_swap_info_.erase(iter);
+      // Recursively call into FinishSwapBuffers until the head of the queue is
+      // no longer a skipped swap.
+      FinishSwapBuffers(
+          gfx::SwapCompletionResult(gfx::SwapResult::SWAP_SKIPPED), frame_size,
+          std::move(frame));
+    }
+  }
+}
+
+void SkiaOutputDevice::SwapBuffersSkipped(BufferPresentedCallback feedback,
+                                          OutputSurfaceFrame frame) {
+  StartSwapBuffers(std::move(feedback));
+  // If there are no other pending swaps, we can immediately close out the
+  // "skipped" swap that was just enqueued. If there are outstanding pending
+  // swaps, however, we need to wait for them to complete to avoid reordering
+  // complete/presentation callbacks.
+  if (pending_swaps_.size() == 1) {
+    gfx::Size frame_size = frame.size;
+    FinishSwapBuffers(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_SKIPPED),
+                      frame_size, std::move(frame));
+  } else {
+    skipped_swap_info_[swap_id_] = std::move(frame);
+  }
 }
 
 SkiaOutputDevice::SwapInfo::SwapInfo(
@@ -253,6 +285,10 @@
 
 SkiaOutputDevice::SwapInfo::~SwapInfo() = default;
 
+uint64_t SkiaOutputDevice::SwapInfo::SwapId() {
+  return params_.swap_response.swap_id;
+}
+
 const gpu::SwapBuffersCompleteParams& SkiaOutputDevice::SwapInfo::Complete(
     gfx::SwapCompletionResult result,
     const absl::optional<gfx::Rect>& damage_rect,
diff --git a/components/viz/service/display_embedder/skia_output_device.h b/components/viz/service/display_embedder/skia_output_device.h
index b0185225..0a19355 100644
--- a/components/viz/service/display_embedder/skia_output_device.h
+++ b/components/viz/service/display_embedder/skia_output_device.h
@@ -166,6 +166,13 @@
   virtual void EnsureBackbuffer();
   virtual void DiscardBackbuffer();
 
+  // Acknowledges a SwapBuffers request without actually attempting to swap.
+  // This should be called when the GPU thread decides to skip a swap that was
+  // invoked by the viz thread to ensure that we still run the relevant metrics
+  // bookkeeping.
+  virtual void SwapBuffersSkipped(BufferPresentedCallback feedback,
+                                  OutputSurfaceFrame frame);
+
   bool is_emulated_rgbx() const { return is_emulated_rgbx_; }
 
   void SetDrawTimings(base::TimeTicks submitted, base::TimeTicks started);
@@ -183,6 +190,7 @@
              base::TimeTicks task_ready);
     SwapInfo(SwapInfo&& other);
     ~SwapInfo();
+    uint64_t SwapId();
     const gpu::SwapBuffersCompleteParams& Complete(
         gfx::SwapCompletionResult result,
         const absl::optional<gfx::Rect>& damage_area,
@@ -252,6 +260,8 @@
   std::unique_ptr<ui::LatencyTracker> latency_tracker_;
   // task runner for latency tracker.
   scoped_refptr<base::SequencedTaskRunner> latency_tracker_runner_;
+  // A mapping from skipped swap ID to its corresponding OutputSurfaceFrame.
+  base::flat_map<uint64_t, OutputSurfaceFrame> skipped_swap_info_;
 
   DISALLOW_COPY_AND_ASSIGN(SkiaOutputDevice);
 };
diff --git a/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc b/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc
index 8cb6fd8..3ee73ec 100644
--- a/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc
+++ b/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc
@@ -13,9 +13,11 @@
 #include "base/callback_helpers.h"
 #include "base/memory/ptr_util.h"
 #include "base/test/bind.h"
+#include "base/test/mock_callback.h"
 #include "build/build_config.h"
 #include "components/viz/common/resources/resource_sizes.h"
 #include "components/viz/service/display_embedder/output_presenter_gl.h"
+#include "components/viz/service/display_embedder/skia_output_device.h"
 #include "components/viz/service/display_embedder/skia_output_surface_dependency_impl.h"
 #include "components/viz/service/gl/gpu_service_impl.h"
 #include "components/viz/test/test_gpu_service_holder.h"
@@ -195,13 +197,15 @@
 
   void SwapBuffersAsync(SwapCompletionCallback completion_callback,
                         PresentationCallback presentation_callback) override {
-    callbacks_.push_back(std::move(completion_callback));
+    swap_completion_callbacks_.push_back(std::move(completion_callback));
+    presentation_callbacks_.push_back(std::move(presentation_callback));
   }
 
   void CommitOverlayPlanesAsync(
       SwapCompletionCallback completion_callback,
       PresentationCallback presentation_callback) override {
-    callbacks_.push_back(std::move(completion_callback));
+    swap_completion_callbacks_.push_back(std::move(completion_callback));
+    presentation_callbacks_.push_back(std::move(presentation_callback));
   }
 
   bool ScheduleOverlayPlane(int z_order,
@@ -219,15 +223,20 @@
   }
 
   void SwapComplete() {
-    DCHECK(!callbacks_.empty());
-    std::move(callbacks_.front())
+    DCHECK(!swap_completion_callbacks_.empty());
+    std::move(swap_completion_callbacks_.front())
         .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK));
-    callbacks_.pop_front();
+    swap_completion_callbacks_.pop_front();
+
+    DCHECK(!presentation_callbacks_.empty());
+    std::move(presentation_callbacks_.front()).Run({});
+    presentation_callbacks_.pop_front();
   }
 
  protected:
   ~MockGLSurfaceAsync() override = default;
-  base::circular_deque<SwapCompletionCallback> callbacks_;
+  base::circular_deque<SwapCompletionCallback> swap_completion_callbacks_;
+  base::circular_deque<PresentationCallback> presentation_callbacks_;
 };
 
 class MemoryTrackerStub : public gpu::MemoryTracker {
@@ -260,6 +269,13 @@
 
 }  // namespace
 
+using DidSwapBufferCompleteCallback =
+    base::RepeatingCallback<void(gpu::SwapBuffersCompleteParams,
+                                 const gfx::Size& pixel_size,
+                                 gfx::GpuFenceHandle release_fence)>;
+using BufferPresentedCallback =
+    base::OnceCallback<void(const gfx::PresentationFeedback& feedback)>;
+
 class SkiaOutputDeviceBufferQueueTest : public TestOnGpu {
  public:
   SkiaOutputDeviceBufferQueueTest() = default;
@@ -270,6 +286,11 @@
         gpu_service_holder_->gpu_service(), surface_handle_);
   }
 
+  virtual DidSwapBufferCompleteCallback GetDidSwapBuffersCompleteCallback() {
+    return base::DoNothing::Repeatedly<gpu::SwapBuffersCompleteParams,
+                                       const gfx::Size&, gfx::GpuFenceHandle>();
+  }
+
   void SetUpOnGpu() override {
     gl_surface_ = base::MakeRefCounted<MockGLSurfaceAsync>();
     memory_tracker_ = std::make_unique<MemoryTrackerStub>();
@@ -286,9 +307,7 @@
         std::make_unique<gpu::SharedImageRepresentationFactory>(
             dependency_->GetSharedImageManager(), memory_tracker_.get());
 
-    auto present_callback =
-        base::DoNothing::Repeatedly<gpu::SwapBuffersCompleteParams,
-                                    const gfx::Size&, gfx::GpuFenceHandle>();
+    auto present_callback = GetDidSwapBuffersCompleteCallback();
 
     output_device_ = std::make_unique<SkiaOutputDeviceBufferQueue>(
         std::make_unique<OutputPresenterGL>(
@@ -380,7 +399,7 @@
     output_device_->SchedulePrimaryPlane(no_plane);
   }
 
-  void SwapBuffers() {
+  virtual void SwapBuffers() {
     auto present_callback =
         base::DoNothing::Once<const gfx::PresentationFeedback&>();
 
@@ -733,4 +752,76 @@
 }
 
 }  // namespace
+
+class SkiaOutputDeviceSwapSkippedTest : public SkiaOutputDeviceBufferQueueTest {
+ public:
+  SkiaOutputDeviceSwapSkippedTest() = default;
+
+  DidSwapBufferCompleteCallback GetDidSwapBuffersCompleteCallback() override {
+    return swap_buffers_complete_cb.Get();
+  }
+
+  void SwapBuffers() override {
+    output_device_->SwapBuffers(buffer_presented_cb.Get(),
+                                OutputSurfaceFrame());
+  }
+
+  void SwapBuffersSkipped() {
+    output_device_->SwapBuffersSkipped(buffer_presented_cb.Get(),
+                                       OutputSurfaceFrame());
+  }
+
+  base::MockCallback<DidSwapBufferCompleteCallback> swap_buffers_complete_cb;
+  base::MockCallback<BufferPresentedCallback> buffer_presented_cb;
+};
+
+namespace {
+
+MATCHER_P2(CheckSwapResponse, expected_swap_id, expected_result, "") {
+  return arg.swap_response.swap_id == expected_swap_id &&
+         arg.swap_response.result == expected_result;
+}
+
+MATCHER_P(CheckPresentationFeedback, expected_fail, "") {
+  return expected_fail == arg.failed();
+}
+
+TEST_F_GPU(SkiaOutputDeviceSwapSkippedTest, SkipWithoutPending) {
+  // Check that skipping a SwapBuffers without any pending swaps immediately
+  // invokes the complete/presented callbacks.
+  output_device_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), kDefaultFormat,
+                          gfx::OVERLAY_TRANSFORM_NONE);
+  EXPECT_CALL(swap_buffers_complete_cb,
+              Run(CheckSwapResponse(1U, gfx::SwapResult::SWAP_SKIPPED), _, _));
+  EXPECT_CALL(buffer_presented_cb,
+              Run(CheckPresentationFeedback(true /* failed */)));
+
+  SwapBuffersSkipped();
+}
+
+TEST_F_GPU(SkiaOutputDeviceSwapSkippedTest, SkipWithPending) {
+  // Check that skipping a SwapBuffers with existing pending swaps waits for
+  // the pending swaps to complete before invoking the complete/presented
+  // callbacks.
+  output_device_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), kDefaultFormat,
+                          gfx::OVERLAY_TRANSFORM_NONE);
+  EXPECT_NE(PaintAndSchedulePrimaryPlane(), nullptr);
+  EXPECT_NE(current_image(), nullptr);
+
+  SwapBuffers();
+  SwapBuffersSkipped();
+
+  EXPECT_CALL(swap_buffers_complete_cb,
+              Run(CheckSwapResponse(1U, gfx::SwapResult::SWAP_ACK), _, _));
+  EXPECT_CALL(buffer_presented_cb,
+              Run(CheckPresentationFeedback(false /* failed */)));
+  EXPECT_CALL(swap_buffers_complete_cb,
+              Run(CheckSwapResponse(2U, gfx::SwapResult::SWAP_SKIPPED), _, _));
+  EXPECT_CALL(buffer_presented_cb,
+              Run(CheckPresentationFeedback(true /* failed */)));
+
+  PageFlipComplete();
+}
+
+}  // namespace
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index 8207496..86df379 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -1600,6 +1600,8 @@
       if (frame->sub_buffer_rect &&
           capabilities().supports_post_sub_buffer &&
           frame->sub_buffer_rect->size() != size_) {
+        output_device_->SwapBuffersSkipped(buffer_presented_callback_,
+                                           std::move(*frame));
         output_surface_plane_.reset();
         destroy_after_swap_.clear();
         return;
diff --git a/content/browser/audio/audio_service.cc b/content/browser/audio/audio_service.cc
index 659d904a..450272a8 100644
--- a/content/browser/audio/audio_service.cc
+++ b/content/browser/audio/audio_service.cc
@@ -7,7 +7,6 @@
 #include "base/command_line.h"
 #include "base/deferred_sequenced_task_runner.h"
 #include "base/metrics/field_trial_params.h"
-#include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/sequence_local_storage_slot.h"
 #include "base/time/time.h"
@@ -109,10 +108,10 @@
       base::BindOnce(
           [](media::AudioManager* audio_manager,
              mojo::PendingReceiver<audio::mojom::AudioService> receiver) {
-            static base::NoDestructor<
-                base::SequenceLocalStorageSlot<std::unique_ptr<audio::Service>>>
+            static base::SequenceLocalStorageSlot<
+                std::unique_ptr<audio::Service>>
                 service;
-            service->GetOrCreateValue() = audio::CreateEmbeddedService(
+            service.GetOrCreateValue() = audio::CreateEmbeddedService(
                 audio_manager, std::move(receiver));
           },
           BrowserMainLoop::GetAudioManager(), std::move(receiver)));
@@ -162,10 +161,10 @@
   // any sequence, but to limit the lifetime of this Remote to the lifetime of
   // UI-thread sequence. This is to support re-creation after task environment
   // shutdown and reinitialization e.g. between unit tests.
-  static base::NoDestructor<
-      base::SequenceLocalStorageSlot<mojo::Remote<audio::mojom::AudioService>>>
+  static base::SequenceLocalStorageSlot<
+      mojo::Remote<audio::mojom::AudioService>>
       remote_slot;
-  auto& remote = remote_slot->GetOrCreateValue();
+  auto& remote = remote_slot.GetOrCreateValue();
   if (!remote)
     LaunchAudioService(&remote);
   return *remote.get();
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 4e7094c9..5d09ecf7 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -274,12 +274,7 @@
 void BindConversionInternalsHandler(
     content::RenderFrameHost* host,
     mojo::PendingReceiver<::mojom::ConversionInternalsHandler> receiver) {
-  auto* contents = WebContents::FromRenderFrameHost(host);
-  DCHECK_EQ(contents->GetLastCommittedURL().host_piece(),
-            kChromeUIConversionInternalsHost);
-  DCHECK(contents->GetLastCommittedURL().SchemeIs(kChromeUIScheme));
-
-  content::WebUI* web_ui = contents->GetWebUI();
+  content::WebUI* web_ui = host->GetWebUI();
 
   // Performs a safe downcast to the concrete ConversionInternalsUI subclass.
   ConversionInternalsUI* conversion_internals_ui =
@@ -295,17 +290,17 @@
     return;
   }
 
+  DCHECK_EQ(host->GetLastCommittedURL().host_piece(),
+            kChromeUIConversionInternalsHost);
+  DCHECK(host->GetLastCommittedURL().SchemeIs(kChromeUIScheme));
+
   conversion_internals_ui->BindInterface(std::move(receiver));
 }
 
 void BindProcessInternalsHandler(
     content::RenderFrameHost* host,
     mojo::PendingReceiver<::mojom::ProcessInternalsHandler> receiver) {
-  auto* contents = WebContents::FromRenderFrameHost(host);
-  DCHECK_EQ(contents->GetLastCommittedURL().host_piece(),
-            kChromeUIProcessInternalsHost);
-
-  content::WebUI* web_ui = contents->GetWebUI();
+  content::WebUI* web_ui = host->GetWebUI();
 
   // Performs a safe downcast to the concrete ProcessInternalsUI subclass.
   ProcessInternalsUI* process_internals_ui =
@@ -320,6 +315,10 @@
     return;
   }
 
+  DCHECK_EQ(host->GetLastCommittedURL().host_piece(),
+            kChromeUIProcessInternalsHost);
+  DCHECK(host->GetLastCommittedURL().SchemeIs(kChromeUIScheme));
+
   process_internals_ui->BindProcessInternalsHandler(std::move(receiver), host);
 }
 
diff --git a/content/browser/browser_thread_unittest.cc b/content/browser/browser_thread_unittest.cc
index 413de99..70fdae16 100644
--- a/content/browser/browser_thread_unittest.cc
+++ b/content/browser/browser_thread_unittest.cc
@@ -98,8 +98,8 @@
     ui_thread_ = std::make_unique<base::Thread>(
         BrowserThreadImpl::GetThreadName(BrowserThread::UI));
     base::Thread::Options ui_options;
-    ui_options.delegate = new SequenceManagerThreadDelegate();
-    ui_thread_->StartWithOptions(ui_options);
+    ui_options.delegate = std::make_unique<SequenceManagerThreadDelegate>();
+    ui_thread_->StartWithOptions(std::move(ui_options));
 
     io_thread_ = BrowserTaskExecutor::CreateIOThread();
     io_thread_->RegisterAsBrowserThread();
diff --git a/content/browser/device/device_service.cc b/content/browser/device/device_service.cc
index b0875ed9..d7a7866 100644
--- a/content/browser/device/device_service.cc
+++ b/content/browser/device/device_service.cc
@@ -5,7 +5,6 @@
 #include "content/public/browser/device_service.h"
 
 #include "base/memory/scoped_refptr.h"
-#include "base/no_destructor.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/sequence_local_storage_slot.h"
@@ -80,10 +79,9 @@
     mojo::PendingReceiver<device::mojom::DeviceService> receiver) {
   // Bind the lifetime of the service instance to that of the sequence it's
   // running on.
-  static base::NoDestructor<
-      base::SequenceLocalStorageSlot<std::unique_ptr<device::DeviceService>>>
+  static base::SequenceLocalStorageSlot<std::unique_ptr<device::DeviceService>>
       service_slot;
-  auto& service = service_slot->GetOrCreateValue();
+  auto& service = service_slot.GetOrCreateValue();
 
   if (service) {
     service->AddReceiver(std::move(receiver));
@@ -128,11 +126,11 @@
 }  // namespace
 
 device::mojom::DeviceService& GetDeviceService() {
-  static base::NoDestructor<base::SequenceLocalStorageSlot<
-      mojo::Remote<device::mojom::DeviceService>>>
+  static base::SequenceLocalStorageSlot<
+      mojo::Remote<device::mojom::DeviceService>>
       remote_slot;
   mojo::Remote<device::mojom::DeviceService>& remote =
-      remote_slot->GetOrCreateValue();
+      remote_slot.GetOrCreateValue();
   if (!remote) {
     // This may be called very early in startup, too early for some Device
     // Service initialization steps (for example, in browser test environments,
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index ade819f..c9e673b3 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -4148,6 +4148,26 @@
   EXPECT_TRUE(item->GetOriginalMimeType().empty());
 }
 
+// Verify that for download that is not triggered by navigation, MIME sniffing
+// is working.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest, SniffedMimeTypeForDownloadURL) {
+  GURL download_url =
+      embedded_test_server()->GetURL("/download/gzip-content.gz");
+  std::unique_ptr<DownloadTestObserver> observer(CreateWaiter(shell(), 1));
+  auto download_parameters = std::make_unique<download::DownloadUrlParameters>(
+      download_url, TRAFFIC_ANNOTATION_FOR_TESTS);
+  // Download URL without navigation.
+  DownloadManagerForShell(shell())->DownloadUrl(std::move(download_parameters));
+  observer->WaitForFinished();
+
+  // Verify download failed.
+  std::vector<download::DownloadItem*> downloads;
+  DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
+  EXPECT_EQ(1u, downloads.size());
+  EXPECT_EQ(download::DownloadItem::COMPLETE, downloads[0]->GetState());
+  EXPECT_STREQ("application/x-gzip", downloads[0]->GetMimeType().c_str());
+}
+
 IN_PROC_BROWSER_TEST_F(DownloadContentTest, DuplicateContentDisposition) {
   // double-content-disposition.txt is served with two Content-Disposition
   // headers, both of which are identical.
diff --git a/content/browser/font_access/font_enumeration_cache_fontconfig.h b/content/browser/font_access/font_enumeration_cache_fontconfig.h
index 9d254fcb..b296ce9 100644
--- a/content/browser/font_access/font_enumeration_cache_fontconfig.h
+++ b/content/browser/font_access/font_enumeration_cache_fontconfig.h
@@ -5,16 +5,12 @@
 #ifndef CONTENT_BROWSER_FONT_ACCESS_FONT_ENUMERATION_CACHE_FONTCONFIG_H_
 #define CONTENT_BROWSER_FONT_ACCESS_FONT_ENUMERATION_CACHE_FONTCONFIG_H_
 
+#include "base/no_destructor.h"
 #include "content/browser/font_access/font_enumeration_cache.h"
 #include "content/common/content_export.h"
 
 using blink::mojom::FontEnumerationStatus;
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}
-
 namespace content {
 
 // ChromeOS and Linux implementation of FontEnumerationCache.
diff --git a/content/browser/font_access/font_enumeration_cache_mac.h b/content/browser/font_access/font_enumeration_cache_mac.h
index 65ccb13..f3fc03f 100644
--- a/content/browser/font_access/font_enumeration_cache_mac.h
+++ b/content/browser/font_access/font_enumeration_cache_mac.h
@@ -6,17 +6,13 @@
 #define CONTENT_BROWSER_FONT_ACCESS_FONT_ENUMERATION_CACHE_MAC_H_
 
 #include "base/memory/scoped_refptr.h"
+#include "base/no_destructor.h"
 #include "base/task_runner.h"
 #include "content/browser/font_access/font_enumeration_cache.h"
 #include "content/common/content_export.h"
 
 using blink::mojom::FontEnumerationStatus;
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}
-
 namespace content {
 
 // Mac implementation of FontEnumerationCache.
diff --git a/content/browser/font_access/font_enumeration_cache_win.h b/content/browser/font_access/font_enumeration_cache_win.h
index b9bd0d5..dc30234 100644
--- a/content/browser/font_access/font_enumeration_cache_win.h
+++ b/content/browser/font_access/font_enumeration_cache_win.h
@@ -10,6 +10,7 @@
 
 #include "base/deferred_sequenced_task_runner.h"
 #include "base/memory/read_only_shared_memory_region.h"
+#include "base/no_destructor.h"
 #include "base/synchronization/atomic_flag.h"
 #include "content/browser/font_access/font_enumeration_cache.h"
 #include "content/common/content_export.h"
@@ -18,9 +19,6 @@
 using blink::mojom::FontEnumerationStatus;
 
 namespace base {
-template <typename T>
-class NoDestructor;
-
 class ElapsedTimer;
 }
 
diff --git a/content/browser/media/media_service.cc b/content/browser/media/media_service.cc
index c8d7216..46752c564 100644
--- a/content/browser/media/media_service.cc
+++ b/content/browser/media/media_service.cc
@@ -54,10 +54,10 @@
   // that of the UI-thread sequence. This ensures that the Remote is destroyed
   // when the task environment is torn down and reinitialized, e.g. between unit
   // tests.
-  static base::NoDestructor<
-      base::SequenceLocalStorageSlot<mojo::Remote<media::mojom::MediaService>>>
+  static base::SequenceLocalStorageSlot<
+      mojo::Remote<media::mojom::MediaService>>
       remote_slot;
-  auto& remote = remote_slot->GetOrCreateValue();
+  auto& remote = remote_slot.GetOrCreateValue();
   if (!remote) {
     auto receiver = remote.BindNewPipeAndPassReceiver();
     remote.reset_on_disconnect();
diff --git a/content/browser/media/service_factory.cc b/content/browser/media/service_factory.cc
index b9174bb..6988bf5 100644
--- a/content/browser/media/service_factory.cc
+++ b/content/browser/media/service_factory.cc
@@ -11,7 +11,6 @@
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/logging.h"
-#include "base/no_destructor.h"
 #include "base/threading/sequence_local_storage_slot.h"
 #include "base/time/time.h"
 #include "content/browser/service_sandbox_type.h"
@@ -154,8 +153,8 @@
   // objects to that of the UI-thread sequence. This ensures the Remotes are
   // destroyed when the task environment is torn down and reinitialized, e.g.,
   // between unit tests.
-  static base::NoDestructor<base::SequenceLocalStorageSlot<ServiceMap<T>>> slot;
-  return slot->GetOrCreateValue();
+  static base::SequenceLocalStorageSlot<ServiceMap<T>> slot;
+  return slot.GetOrCreateValue();
 }
 
 // Erases the service instance identified by `key`.
diff --git a/content/browser/media/session/audio_focus_delegate_default.cc b/content/browser/media/session/audio_focus_delegate_default.cc
index 4729b013..13abe82 100644
--- a/content/browser/media/session/audio_focus_delegate_default.cc
+++ b/content/browser/media/session/audio_focus_delegate_default.cc
@@ -5,7 +5,6 @@
 #include "content/browser/media/session/audio_focus_delegate.h"
 
 #include "base/bind.h"
-#include "base/no_destructor.h"
 #include "base/unguessable_token.h"
 #include "build/build_config.h"
 #include "content/browser/media/session/media_session_impl.h"
@@ -32,9 +31,8 @@
   // Use a shared audio focus group id for the whole browser. This will means
   // that tabs will share audio focus if the enforcement mode is set to
   // kSingleGroup.
-  static const base::NoDestructor<base::UnguessableToken> token(
-      base::UnguessableToken::Create());
-  return *token;
+  static const base::UnguessableToken token(base::UnguessableToken::Create());
+  return token;
 }
 
 // AudioFocusDelegateDefault is the default implementation of
diff --git a/content/browser/network_service_instance_impl.cc b/content/browser/network_service_instance_impl.cc
index c44b89fd..32ff2690 100644
--- a/content/browser/network_service_instance_impl.cc
+++ b/content/browser/network_service_instance_impl.cc
@@ -79,10 +79,10 @@
 base::Time g_last_network_service_crash;
 
 std::unique_ptr<network::NetworkService>& GetLocalNetworkService() {
-  static base::NoDestructor<
-      base::SequenceLocalStorageSlot<std::unique_ptr<network::NetworkService>>>
+  static base::SequenceLocalStorageSlot<
+      std::unique_ptr<network::NetworkService>>
       service;
-  return service->GetOrCreateValue();
+  return service.GetOrCreateValue();
 }
 
 // If this feature is enabled, the Network Service will run on its own thread
@@ -135,7 +135,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> task_runner;
   if (base::FeatureList::IsEnabled(kNetworkServiceDedicatedThread)) {
     base::Thread::Options options(base::MessagePumpType::IO, 0);
-    GetNetworkServiceDedicatedThread().StartWithOptions(options);
+    GetNetworkServiceDedicatedThread().StartWithOptions(std::move(options));
     task_runner = GetNetworkServiceDedicatedThread().task_runner();
   } else {
     task_runner = GetIOThreadTaskRunner({});
@@ -575,10 +575,10 @@
   DCHECK(!BrowserThread::IsThreadInitialized(BrowserThread::UI) ||
          BrowserThread::CurrentlyOn(BrowserThread::UI));
 #endif
-  static base::NoDestructor<base::SequenceLocalStorageSlot<
-      std::unique_ptr<cert_verifier::CertVerifierServiceFactoryImpl>>>
+  static base::SequenceLocalStorageSlot<
+      std::unique_ptr<cert_verifier::CertVerifierServiceFactoryImpl>>
       service_factory_slot;
-  service_factory_slot->GetOrCreateValue() =
+  service_factory_slot.GetOrCreateValue() =
       std::make_unique<cert_verifier::CertVerifierServiceFactoryImpl>(
           std::move(receiver));
 }
@@ -587,10 +587,10 @@
 // Lives on the UI thread.
 mojo::Remote<cert_verifier::mojom::CertVerifierServiceFactory>&
 GetCertVerifierServiceFactoryRemoteStorage() {
-  static base::NoDestructor<base::SequenceLocalStorageSlot<
-      mojo::Remote<cert_verifier::mojom::CertVerifierServiceFactory>>>
+  static base::SequenceLocalStorageSlot<
+      mojo::Remote<cert_verifier::mojom::CertVerifierServiceFactory>>
       cert_verifier_service_factory_remote;
-  return cert_verifier_service_factory_remote->GetOrCreateValue();
+  return cert_verifier_service_factory_remote.GetOrCreateValue();
 }
 
 // Returns a pointer to a CertVerifierServiceFactory usable on the UI thread.
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
index c827a6f..9db23bb1 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
@@ -19,6 +19,7 @@
 #include "base/macros.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "base/synchronization/atomic_flag.h"
 #include "base/time/time.h"
 #include "content/common/content_export.h"
@@ -26,11 +27,6 @@
 #include "third_party/blink/public/common/font_unique_name_lookup/font_unique_name_table.pb.h"
 #include "third_party/blink/public/mojom/dwrite_font_proxy/dwrite_font_proxy.mojom.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}
-
 namespace content {
 
 // Singleton class which encapsulates building the font unique name table lookup
diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc b/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc
index 4c6b0c47..cb22853 100644
--- a/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc
+++ b/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc
@@ -391,8 +391,9 @@
                          TouchSelectionControllerClientAuraSiteIsolationTest,
                          testing::Bool());
 
+// TODO(crbug.com/1225345): Re-enable this test.
 IN_PROC_BROWSER_TEST_P(TouchSelectionControllerClientAuraSiteIsolationTest,
-                       BasicSelectionIsolatedIframe) {
+                       DISABLED_BasicSelectionIsolatedIframe) {
   GURL test_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(a)"));
   EXPECT_TRUE(NavigateToURL(shell(), test_url));
diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc
index 8c57809..beed2dd5 100644
--- a/content/browser/renderer_host/media/video_capture_manager.cc
+++ b/content/browser/renderer_host/media/video_capture_manager.cc
@@ -17,7 +17,6 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
-#include "base/no_destructor.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task_runner_util.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -42,10 +41,10 @@
 }
 
 const base::UnguessableToken& FakeSessionId() {
-  static const base::NoDestructor<base::UnguessableToken> fake_session_id(
+  static const base::UnguessableToken fake_session_id(
       base::UnguessableToken::Deserialize(0xFFFFFFFFFFFFFFFFU,
                                           0xFFFFFFFFFFFFFFFFU));
-  return *fake_session_id;
+  return fake_session_id;
 }
 
 }  // namespace
diff --git a/content/browser/renderer_host/media/video_capture_unittest.cc b/content/browser/renderer_host/media/video_capture_unittest.cc
index a2cee0dc..d2dd3fc1 100644
--- a/content/browser/renderer_host/media/video_capture_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_unittest.cc
@@ -13,7 +13,6 @@
 #include "base/location.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/no_destructor.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -70,9 +69,9 @@
 // Id used to identify the capture session between renderer and
 // video_capture_host. This is an arbitrary value.
 const base::UnguessableToken& DeviceId() {
-  static const base::NoDestructor<base::UnguessableToken> device_id(
+  static const base::UnguessableToken device_id(
       base::UnguessableToken::Deserialize(555, 555));
-  return *device_id;
+  return device_id;
 }
 
 }  // namespace
diff --git a/content/browser/renderer_host/render_frame_host_delegate.cc b/content/browser/renderer_host/render_frame_host_delegate.cc
index 58c9d3ec..dadd5be 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.cc
+++ b/content/browser/renderer_host/render_frame_host_delegate.cc
@@ -89,6 +89,10 @@
   return true;
 }
 
+bool RenderFrameHostDelegate::HasEnteredFullscreenMode() {
+  return false;
+}
+
 void RenderFrameHostDelegate::FullscreenStateChanged(
     RenderFrameHostImpl* rfh,
     bool is_fullscreen,
diff --git a/content/browser/renderer_host/render_frame_host_delegate.h b/content/browser/renderer_host/render_frame_host_delegate.h
index 369377b..a4111f72 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.h
+++ b/content/browser/renderer_host/render_frame_host_delegate.h
@@ -302,6 +302,9 @@
   // Returns whether entering fullscreen with EnterFullscreenMode() is allowed.
   virtual bool CanEnterFullscreenMode();
 
+  // Returns whether this frame is already fullscreen.
+  virtual bool HasEnteredFullscreenMode();
+
   // Notification that the frame with the given host wants to enter fullscreen
   // mode. Must only be called if CanEnterFullscreenMode returns true.
   virtual void EnterFullscreenMode(
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index a9e3441e..4f9dde2 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -5474,11 +5474,16 @@
   // side when the renderer is compromised and the fullscreen request is denied.
   // Fullscreen can only be triggered by: a user activation, a user-generated
   // screen orientation change, or another feature-specific transient allowance.
+  // If the frame is already fullscreen (and this request is a noop or
+  // this request is changing from one display to another), then no user
+  // activation is checked or consumed.
+  //
   // CanEnterFullscreenWithoutUserActivation is only ever true in tests, to
   // allow fullscreen when mocking screen orientation changes.
   // TODO(lanwei): Investigate whether we can terminate the renderer when the
   // user activation has already been consumed.
-  if (!delegate_->HasSeenRecentScreenOrientationChange() &&
+  if (!delegate_->HasEnteredFullscreenMode() &&
+      !delegate_->HasSeenRecentScreenOrientationChange() &&
       !WindowPlacementAllowsFullscreen() && !HasSeenRecentXrOverlaySetup() &&
       !GetContentClient()
            ->browser()
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index b2d855b..6bfb14af 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -2285,6 +2285,12 @@
   FRIEND_TEST_ALL_PREFIXES(
       RenderFrameHostManagerUnloadBrowserTest,
       PendingDeleteRFHProcessShutdownDoesNotRemoveSubframes);
+  FRIEND_TEST_ALL_PREFIXES(SecurityExploitBrowserTest,
+                           AttemptDuplicateRenderViewHost);
+  FRIEND_TEST_ALL_PREFIXES(SecurityExploitBrowserTest,
+                           AttemptDuplicateRenderWidgetHost);
+  FRIEND_TEST_ALL_PREFIXES(SecurityExploitBrowserTest,
+                           BindToWebUIFromWebViaMojo);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
                            RenderViewHostIsNotReusedAfterDelayedUnloadACK);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
@@ -2301,8 +2307,6 @@
                            RenderFrameProxyNotRecreatedDuringProcessShutdown);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
                            UnloadACKArrivesPriorToProcessShutdownRequest);
-  FRIEND_TEST_ALL_PREFIXES(SecurityExploitBrowserTest,
-                           AttemptDuplicateRenderViewHost);
   FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest,
                            FullscreenAfterFrameUnload);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest, UnloadHandlerSubframes);
@@ -2329,8 +2333,6 @@
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessSSLBrowserTest,
                            UnloadHandlersArePowerfulGrandChild);
   FRIEND_TEST_ALL_PREFIXES(RenderFrameHostImplTest, ExpectedMainWorldOrigin);
-  FRIEND_TEST_ALL_PREFIXES(SecurityExploitBrowserTest,
-                           AttemptDuplicateRenderWidgetHost);
   FRIEND_TEST_ALL_PREFIXES(RenderDocumentHostUserDataTest,
                            CheckInPendingDeletionState);
   FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest, FrozenAndUnfrozenIPC);
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index c12ab7e0..43c270f8 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -5243,8 +5243,11 @@
     const SiteInfo& site_info) {
   if (IsKeepAliveRefCountDisabled())
     return;
+
+  // Unset |is_shutdown_delayed| and remove from the delayed-shutdown tracker.
+  // This may have already been done in CancelAllProcessShutdownDelays() if the
+  // process was reused before this task executed.
   if (is_shutdown_delayed_) {
-    // Remove from the delayed-shutdown tracker.
     if (base::FeatureList::IsEnabled(features::kSubframeShutdownDelay) &&
         ShouldTrackProcessForSite(GetBrowserContext(), this, site_info)) {
       SiteProcessCountTracker* tracker = SiteProcessCountTracker::GetInstance(
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 3e2034f0..18f90f71 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -562,6 +562,11 @@
                             const base::TimeDelta& unload_handler_timeout,
                             const SiteInfo& site_info);
   bool IsProcessShutdownDelayedForTesting() { return is_shutdown_delayed_; }
+  // Remove the host from the delayed-shutdown tracker, if present. This does
+  // not decrement |keep_alive_ref_count_|; if it was incremented by a shutdown
+  // delay, it will be decremented when the delay expires. This ensures that
+  // the host is not destroyed between cancelling its shutdown delay and the new
+  // navigation adding listeners to keep it alive.
   void CancelAllProcessShutdownDelays() override;
 
   // Binds |receiver| to the FileSystemManager instance owned by the render
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.cc b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
index a598fff..7337fd8 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.cc
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
@@ -38,12 +38,6 @@
 #include "ui/gfx/delegated_ink_point.h"
 #include "ui/touch_selection/touch_selection_controller.h"
 
-#if defined(OS_WIN)
-#include "content/browser/renderer_host/render_frame_host_impl.h"
-#include "ui/aura/window_tree_host.h"
-#include "ui/display/screen.h"
-#endif  // defined(OS_WIN)
-
 namespace {
 
 // In mouse lock mode, we need to prevent the (invisible) cursor from hitting
@@ -55,16 +49,6 @@
 // of the border area, in percentage of the corresponding dimension.
 const int kMouseLockBorderPercentage = 15;
 
-// While the mouse is locked we want the invisible mouse to stay within the
-// confines of the screen so we keep it in a capture region the size of the
-// screen.  However, on windows when the mouse hits the edge of the screen some
-// events trigger and cause strange issues to occur. To stop those events from
-// occuring we add a small border around the edge of the capture region.
-// This constant controls how many pixels wide that border is.
-#if defined(OS_WIN)
-const int KMouseCaptureRegionBorder = 5;
-#endif
-
 #if defined(OS_WIN)
 // A callback function for EnumThreadWindows to enumerate and dismiss
 // any owned popup windows.
@@ -149,20 +133,6 @@
   popup_child_event_handler_ = popup_child_event_handler;
 }
 
-#if defined(OS_WIN)
-void RenderWidgetHostViewEventHandler::UpdateMouseLockRegion() {
-  RECT window_rect =
-      display::Screen::GetScreen()
-          ->DIPToScreenRectInWindow(window_, window_->GetBoundsInScreen())
-          .ToRECT();
-  window_rect.left += KMouseCaptureRegionBorder;
-  window_rect.right -= KMouseCaptureRegionBorder;
-  window_rect.top += KMouseCaptureRegionBorder;
-  window_rect.bottom -= KMouseCaptureRegionBorder;
-  ::ClipCursor(&window_rect);
-}
-#endif
-
 blink::mojom::PointerLockResult RenderWidgetHostViewEventHandler::LockMouse(
     bool request_unadjusted_movement) {
   aura::Window* root_window = window_->GetRootWindow();
@@ -180,17 +150,7 @@
   }
   mouse_locked_ = true;
 
-#if !defined(OS_WIN)
-  window_->SetCapture();
-#else
-  UpdateMouseLockRegion();
-#endif
-  aura::client::CursorClient* cursor_client =
-      aura::client::GetCursorClient(root_window);
-  if (cursor_client) {
-    cursor_client->HideCursor();
-    cursor_client->LockCursor();
-  }
+  window_->GetHost()->LockMouse(window_);
 
   if (ShouldMoveToCenter(unlocked_global_mouse_position_))
     MoveCursorToCenter(nullptr);
@@ -241,12 +201,7 @@
   mouse_locked_ = false;
   mouse_locked_unadjusted_movement_.reset();
 
-  if (window_->HasCapture())
-    window_->ReleaseCapture();
-
-#if defined(OS_WIN)
-  ::ClipCursor(NULL);
-#endif
+  window_->GetHost()->UnlockMouse(window_);
 
   // Ensure that the global mouse position is updated here to its original
   // value. If we don't do this then the synthesized mouse move which is posted
@@ -258,12 +213,6 @@
   synthetic_move_position_ =
       gfx::ToFlooredPoint(unlocked_global_mouse_position_);
 
-  aura::client::CursorClient* cursor_client =
-      aura::client::GetCursorClient(root_window);
-  if (cursor_client) {
-    cursor_client->UnlockCursor();
-    cursor_client->ShowCursor();
-  }
   host_->LostMouseLock();
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.h b/content/browser/renderer_host/render_widget_host_view_event_handler.h
index 6d41f4d..6af11db 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.h
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.h
@@ -124,9 +124,6 @@
   // Sets the ContextMenuParams when a context menu is triggered. Required for
   // subsequent event processing.
   void SetContextMenuParams(const ContextMenuParams& params);
-
-  // Updates the cursor clip region. Used for mouse locking.
-  void UpdateMouseLockRegion();
 #endif  // defined(OS_WIN)
 
   bool accept_return_character() { return accept_return_character_; }
diff --git a/content/browser/sandbox_host_linux.h b/content/browser/sandbox_host_linux.h
index eccad333..7526010 100644
--- a/content/browser/sandbox_host_linux.h
+++ b/content/browser/sandbox_host_linux.h
@@ -9,15 +9,11 @@
 
 #include "base/check.h"
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/threading/simple_thread.h"
 #include "content/browser/sandbox_ipc_linux.h"
 #include "content/common/content_export.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}
-
 namespace content {
 
 // This is a singleton object which handles sandbox requests from the
diff --git a/content/browser/scheduler/browser_io_thread_delegate_unittest.cc b/content/browser/scheduler/browser_io_thread_delegate_unittest.cc
index ed39160..65b4904 100644
--- a/content/browser/scheduler/browser_io_thread_delegate_unittest.cc
+++ b/content/browser/scheduler/browser_io_thread_delegate_unittest.cc
@@ -25,8 +25,8 @@
   handle->EnableAllQueues();
 
   base::Thread::Options options;
-  options.delegate = delegate.release();
-  thread.StartWithOptions(options);
+  options.delegate = std::move(delegate);
+  thread.StartWithOptions(std::move(options));
 
   auto runner =
       handle->GetBrowserTaskRunner(BrowserTaskQueues::QueueType::kDefault);
@@ -44,8 +44,8 @@
   auto task_runner = delegate->GetDefaultTaskRunner();
 
   base::Thread::Options options;
-  options.delegate = delegate.release();
-  thread.StartWithOptions(options);
+  options.delegate = std::move(delegate);
+  thread.StartWithOptions(std::move(options));
 
   base::WaitableEvent event;
   task_runner->PostTask(FROM_HERE, base::BindOnce(&base::WaitableEvent::Signal,
diff --git a/content/browser/scheduler/browser_task_executor.cc b/content/browser/scheduler/browser_task_executor.cc
index 28e75e5..c04efa1 100644
--- a/content/browser/scheduler/browser_task_executor.cc
+++ b/content/browser/scheduler/browser_task_executor.cc
@@ -349,13 +349,13 @@
 
   base::Thread::Options options;
   options.message_pump_type = base::MessagePumpType::IO;
-  options.delegate = browser_io_thread_delegate.release();
+  options.delegate = std::move(browser_io_thread_delegate);
   // Up the priority of the |io_thread_| as some of its IPCs relate to
   // display tasks.
   if (base::FeatureList::IsEnabled(
           ::features::kBrowserUseDisplayThreadPriority))
     options.priority = base::ThreadPriority::DISPLAY;
-  if (!io_thread->StartWithOptions(options))
+  if (!io_thread->StartWithOptions(std::move(options)))
     LOG(FATAL) << "Failed to start BrowserThread:IO";
   return io_thread;
 }
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 4edd6f1..cb47453 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -1305,6 +1305,46 @@
   EXPECT_EQ(bad_message::RFH_CAN_COMMIT_URL_BLOCKED, kill_waiter.Wait());
 }
 
+// Tests that a web page cannot bind to a WebUI interface if a WebUI page is the
+// currently committed RenderFrameHost in the tab (https://crbug.com/1225929).
+IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, BindToWebUIFromWebViaMojo) {
+  // Navigate to a non-privileged web page, and simulate a renderer compromise
+  // by granting MojoJS.
+  GURL web_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  TestNavigationManager navigation(shell()->web_contents(), web_url);
+  shell()->LoadURL(web_url);
+  EXPECT_TRUE(navigation.WaitForResponse());
+  RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
+      shell()->web_contents()->GetMainFrame());
+  main_frame->GetFrameBindingsControl()->EnableMojoJsBindings();
+  navigation.ResumeNavigation();
+
+  // When the page unloads (after the cross-process navigation to an actual
+  // WebUI page below), try to bind to a WebUI interface from the web
+  // RenderFrameHost. Ensure the unload timer and bfcache are disabled so that
+  // the handler has a chance to run.
+  main_frame->DisableUnloadTimerForTesting();
+  DisableBackForwardCacheForTesting(shell()->web_contents(),
+                                    BackForwardCache::TEST_USES_UNLOAD_EVENT);
+  ASSERT_TRUE(ExecJs(main_frame, R"(
+        onunload = function () {
+          newMessagePipe = Mojo.createMessagePipe();
+          Mojo.bindInterface('mojom.ProcessInternalsHandler',
+                             newMessagePipe.handle0);
+        };
+      )"));
+
+  // Now navigate to a WebUI page and expect the previous renderer process to be
+  // killed when asking to bind to the WebUI interface.
+  GURL webui_url(
+      GetWebUIURL(kChromeUIProcessInternalsHost).Resolve("#general"));
+  RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame->GetProcess());
+  EXPECT_TRUE(NavigateToURL(shell(), webui_url));
+
+  // Verify that the previous renderer was terminated.
+  EXPECT_EQ(bad_message::RFH_INVALID_WEB_UI_CONTROLLER, kill_waiter.Wait());
+}
+
 class BeginNavigationTransitionReplacer : public FrameHostInterceptor {
  public:
   BeginNavigationTransitionReplacer(WebContents* web_contents,
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 4b0423d..168918b 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -154,19 +154,19 @@
   // NOTE: This use of sequence-local storage is only to ensure that the Remote
   // only lives as long as the UI-thread sequence, since the UI-thread sequence
   // may be torn down and reinitialized e.g. between unit tests.
-  static base::NoDestructor<base::SequenceLocalStorageSlot<
-      mojo::Remote<storage::mojom::StorageService>>>
+  static base::SequenceLocalStorageSlot<
+      mojo::Remote<storage::mojom::StorageService>>
       remote_slot;
-  return remote_slot->GetOrCreateValue();
+  return remote_slot.GetOrCreateValue();
 }
 
 void RunInProcessStorageService(
     mojo::PendingReceiver<storage::mojom::StorageService> receiver) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  static base::NoDestructor<base::SequenceLocalStorageSlot<
-      std::unique_ptr<storage::StorageServiceImpl>>>
+  static base::SequenceLocalStorageSlot<
+      std::unique_ptr<storage::StorageServiceImpl>>
       service_storage_slot;
-  service_storage_slot->GetOrCreateValue() =
+  service_storage_slot.GetOrCreateValue() =
       std::make_unique<storage::StorageServiceImpl>(std::move(receiver),
                                                     /*io_task_runner=*/nullptr);
 }
diff --git a/content/browser/tracing/background_tracing_manager_impl.h b/content/browser/tracing/background_tracing_manager_impl.h
index e9d7ba5..708498a 100644
--- a/content/browser/tracing/background_tracing_manager_impl.h
+++ b/content/browser/tracing/background_tracing_manager_impl.h
@@ -12,17 +12,13 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "content/browser/tracing/background_tracing_config_impl.h"
 #include "content/public/browser/background_tracing_manager.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/tracing/public/cpp/perfetto/trace_event_data_source.h"
 #include "services/tracing/public/mojom/background_tracing_agent.mojom.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 namespace tracing {
 namespace mojom {
 class BackgroundTracingAgent;
diff --git a/content/browser/video_capture_service.cc b/content/browser/video_capture_service.cc
index c408d3b..3f9dcbb 100644
--- a/content/browser/video_capture_service.cc
+++ b/content/browser/video_capture_service.cc
@@ -45,10 +45,10 @@
   // NOTE: This use of sequence-local storage is only to ensure that the Remote
   // only lives as long as the UI-thread sequence, since the UI-thread sequence
   // may be torn down and reinitialized e.g. between unit tests.
-  static base::NoDestructor<base::SequenceLocalStorageSlot<
-      mojo::Remote<video_capture::mojom::VideoCaptureService>>>
+  static base::SequenceLocalStorageSlot<
+      mojo::Remote<video_capture::mojom::VideoCaptureService>>
       remote_slot;
-  return remote_slot->GetOrCreateValue();
+  return remote_slot.GetOrCreateValue();
 }
 
 // This is a custom traits type we use in conjunction with mojo::ReceiverSetBase
@@ -79,10 +79,10 @@
 
 video_capture::mojom::VideoCaptureService& GetVideoCaptureService() {
   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    static base::NoDestructor<base::SequenceLocalStorageSlot<
-        mojo::Remote<video_capture::mojom::VideoCaptureService>>>
+    static base::SequenceLocalStorageSlot<
+        mojo::Remote<video_capture::mojom::VideoCaptureService>>
         storage;
-    auto& remote = storage->GetOrCreateValue();
+    auto& remote = storage.GetOrCreateValue();
     if (!remote.is_bound()) {
       GetUIThreadTaskRunner({})->PostTask(
           FROM_HERE, base::BindOnce(&BindProxyRemoteOnUIThread,
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 4d28d754..8eb069f 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3294,6 +3294,10 @@
   });
 }
 
+bool WebContentsImpl::HasEnteredFullscreenMode() {
+  return IsFullscreen();
+}
+
 void WebContentsImpl::EnterFullscreenMode(
     RenderFrameHostImpl* requesting_frame,
     const blink::mojom::FullscreenOptions& options) {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index f594cfd..46f7b55 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -650,6 +650,7 @@
               mojo::PendingReceiver<device::mojom::NFC>) override;
 #endif
   bool CanEnterFullscreenMode() override;
+  bool HasEnteredFullscreenMode() override;
   void EnterFullscreenMode(
       RenderFrameHostImpl* requesting_frame,
       const blink::mojom::FullscreenOptions& options) override;
diff --git a/content/browser/web_package/signed_exchange_utils.cc b/content/browser/web_package/signed_exchange_utils.cc
index ff01e25..dab316b 100644
--- a/content/browser/web_package/signed_exchange_utils.cc
+++ b/content/browser/web_package/signed_exchange_utils.cc
@@ -6,7 +6,6 @@
 
 #include "base/command_line.h"
 #include "base/feature_list.h"
-#include "base/no_destructor.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -254,9 +253,9 @@
   // uninitialized variables.) This way, we no longer have the unlikely (but
   // observed in the real world!) event where we have two requests with the same
   // request_id_.
-  static base::NoDestructor<std::atomic_int> request_id(-1);
+  static std::atomic_int request_id(-1);
 
-  return --*request_id;
+  return --request_id;
 }
 
 base::Time GetVerificationTime() {
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 5b90c3d..3deeacb1 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -69,7 +69,7 @@
 struct DataResource {
   const char* name;
   int id;
-  ui::ScaleFactor scale_factor;
+  ui::ResourceScaleFactor scale_factor;
 };
 
 class NestedMessageLoopRunnerImpl
@@ -150,8 +150,9 @@
     child_thread->RecordComputedAction(name.Action());
 }
 
-WebData BlinkPlatformImpl::GetDataResource(int resource_id,
-                                           ui::ScaleFactor scale_factor) {
+WebData BlinkPlatformImpl::GetDataResource(
+    int resource_id,
+    ui::ResourceScaleFactor scale_factor) {
   base::StringPiece resource =
       GetContentClient()->GetDataResource(resource_id, scale_factor);
   return WebData(resource.data(), resource.size());
diff --git a/content/child/blink_platform_impl.h b/content/child/blink_platform_impl.h
index 2f5cddc..16932b54 100644
--- a/content/child/blink_platform_impl.h
+++ b/content/child/blink_platform_impl.h
@@ -38,7 +38,7 @@
   bool IsLowEndDevice() override;
   void RecordAction(const blink::UserMetricsAction&) override;
   blink::WebData GetDataResource(int resource_id,
-                                 ui::ScaleFactor scale_factor) override;
+                                 ui::ResourceScaleFactor scale_factor) override;
   blink::WebData UncompressDataResource(int resource_id) override;
   blink::WebString QueryLocalizedString(int resource_id) override;
   blink::WebString QueryLocalizedString(int resource_id,
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/OWebContentsAccessibility.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/OWebContentsAccessibility.java
index a3b125f2..604a6d7 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/OWebContentsAccessibility.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/OWebContentsAccessibility.java
@@ -63,7 +63,7 @@
         for (int i = 0; i < positionInfoLength; i++) {
             Rect rect = new Rect(
                     coords[4 * i + 0], coords[4 * i + 1], coords[4 * i + 2], coords[4 * i + 3]);
-            convertWebRectToAndroidCoordinates(rect);
+            convertWebRectToAndroidCoordinates(rect, info.getExtras());
             boundingRects[i] = new RectF(rect);
         }
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
index 5690199..ef1d7a6c 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
@@ -1770,7 +1770,7 @@
         return charSequence;
     }
 
-    protected void convertWebRectToAndroidCoordinates(Rect rect) {
+    protected void convertWebRectToAndroidCoordinates(Rect rect, Bundle extras) {
         // Offset by the scroll position.
         AccessibilityCoordinates ac = mDelegate.getAccessibilityCoordinates();
         rect.offset(-(int) ac.getScrollX(), -(int) ac.getScrollY());
@@ -1789,11 +1789,17 @@
         mView.getLocationOnScreen(viewLocation);
         rect.offset(viewLocation[0], viewLocation[1]);
 
-        // Clip to the viewport bounds.
+        // Clip to the viewport bounds, and add unclipped values to the Bundle.
         int viewportRectTop = viewLocation[1] + (int) ac.getContentOffsetYPix();
         int viewportRectBottom = viewportRectTop + ac.getLastFrameViewportHeightPixInt();
-        if (rect.top < viewportRectTop) rect.top = viewportRectTop;
-        if (rect.bottom > viewportRectBottom) rect.bottom = viewportRectBottom;
+        if (rect.top < viewportRectTop) {
+            extras.putInt("AccessibilityNodeInfo.unclippedTop", rect.top);
+            rect.top = viewportRectTop;
+        }
+        if (rect.bottom > viewportRectBottom) {
+            extras.putInt("AccessibilityNodeInfo.unclippedBottom", rect.bottom);
+            rect.bottom = viewportRectBottom;
+        }
     }
 
     protected void requestSendAccessibilityEvent(AccessibilityEvent event) {
@@ -1835,7 +1841,7 @@
         node.setBoundsInParent(boundsInParent);
 
         Rect rect = new Rect(absoluteLeft, absoluteTop, absoluteLeft + width, absoluteTop + height);
-        convertWebRectToAndroidCoordinates(rect);
+        convertWebRectToAndroidCoordinates(rect, node.getExtras());
 
         node.setBoundsInScreen(rect);
 
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java
index 19e272f..bb8f137 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java
@@ -90,6 +90,8 @@
     private static final String INPUT_RANGE_EVENT_ERROR =
             "TYPE_VIEW_SCROLLED event not received before timeout.";
     private static final String CACHING_ERROR = "AccessibilityNodeInfo cache has stale data";
+    private static final String NODE_EXTRAS_UNCLIPPED_ERROR =
+            "AccessibilityNodeInfo object should have unclipped bounds in extras bundle";
 
     // Constant values for unit tests
     private static final int UNSUPPRESSED_EXPECTED_COUNT = 25;
@@ -1048,6 +1050,35 @@
         Assert.assertTrue(result[2].left < result[3].left);
     }
 
+    @Test
+    @SmallTest
+    @FlakyTest(message = "https://crbug.com/1225255")
+    public void testNodeInfo_extras_unclippedBounds() {
+        // Build a simple web page with a scrollable view.
+        setupTestFromFile("content/test/data/android/scroll_element_offscreen.html");
+
+        // Find the <div> that contains example paragraphs that can be scrolled.
+        int vvIdDiv = waitForNodeMatching(sClassNameMatcher, "android.view.View");
+        mNodeInfo = createAccessibilityNodeInfo(vvIdDiv);
+        Assert.assertNotNull(NODE_TIMEOUT_ERROR, mNodeInfo);
+
+        // Scroll window up so container goes slightly off-screen.
+        executeJS("scrollUp()");
+
+        // Signal end of test.
+        mActivityTestRule.sendEndOfTestSignal();
+
+        // Refresh the AccessibilityNodeInfo object for the container.
+        mNodeInfo = createAccessibilityNodeInfo(vvIdDiv);
+
+        // Check that the container has unclipped values set.
+        Assert.assertNotNull(NODE_EXTRAS_UNCLIPPED_ERROR, mNodeInfo.getExtras());
+        Assert.assertTrue(NODE_EXTRAS_UNCLIPPED_ERROR,
+                mNodeInfo.getExtras().getInt("AccessibilityNodeInfo.unclippedTop") < 0);
+        Assert.assertTrue(NODE_EXTRAS_UNCLIPPED_ERROR,
+                mNodeInfo.getExtras().getInt("AccessibilityNodeInfo.unclippedBottom") > 0);
+    }
+
     /**
      * Test |AccessibilityNodeInfo| object actions to ensure we are not adding ACTION_LONG_CLICK
      * to nodes due to verbose utterances issue.
diff --git a/content/public/browser/renderer_preferences_util.cc b/content/public/browser/renderer_preferences_util.cc
index e7bd0ec11..050e5114 100644
--- a/content/public/browser/renderer_preferences_util.cc
+++ b/content/public/browser/renderer_preferences_util.cc
@@ -5,7 +5,6 @@
 #include "content/public/browser/renderer_preferences_util.h"
 
 #include "base/command_line.h"
-#include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
 #include "build/build_config.h"
 #include "content/public/common/content_switches.h"
@@ -16,14 +15,14 @@
 
 void UpdateFontRendererPreferencesFromSystemSettings(
     blink::RendererPreferences* prefs) {
-  static const base::NoDestructor<gfx::FontRenderParams> params(
+  static const gfx::FontRenderParams params(
       gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr));
-  prefs->should_antialias_text = params->antialiasing;
-  prefs->use_subpixel_positioning = params->subpixel_positioning;
-  prefs->hinting = params->hinting;
-  prefs->use_autohinter = params->autohinter;
-  prefs->use_bitmaps = params->use_bitmaps;
-  prefs->subpixel_rendering = params->subpixel_rendering;
+  prefs->should_antialias_text = params.antialiasing;
+  prefs->use_subpixel_positioning = params.subpixel_positioning;
+  prefs->hinting = params.hinting;
+  prefs->use_autohinter = params.autohinter;
+  prefs->use_bitmaps = params.use_bitmaps;
+  prefs->subpixel_rendering = params.subpixel_rendering;
 }
 
 }  // namespace content
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 1598c5d..b6e892a 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -626,14 +626,8 @@
 // actually enable the feature by default. The feature is also controlled by the
 // Blink runtime feature "SecurePaymentConfirmation". Both have to be enabled
 // for SecurePaymentConfirmation to be available.
-const base::Feature kSecurePaymentConfirmation {
-  "SecurePaymentConfirmationBrowser",
-#if defined(OS_MAC) || defined(OS_WIN)
-      base::FEATURE_ENABLED_BY_DEFAULT
-#else
-      base::FEATURE_DISABLED_BY_DEFAULT
-#endif
-};
+const base::Feature kSecurePaymentConfirmation{
+    "SecurePaymentConfirmationBrowser", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Used to enable API changes for Secure Payment Confirmation.
 // TODO(crbug.com/1216464): Enable by default in M93.
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 6e121e3..4dbdc2d4 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -3590,7 +3590,8 @@
         blink::mojom::PortalClientInterfaceBase> client_endpoint,
     const blink::WebElement& portal_element) {
   int proxy_routing_id = MSG_ROUTING_NONE;
-  auto initial_replicated_state = blink::mojom::FrameReplicationState::New();
+  blink::mojom::FrameReplicationStatePtr initial_replicated_state =
+      blink::mojom::FrameReplicationState::New();
   blink::PortalToken portal_token;
   blink::RemoteFrameToken frame_token;
   base::UnguessableToken devtools_frame_token;
@@ -3598,7 +3599,7 @@
                                std::move(client_endpoint), &proxy_routing_id,
                                &initial_replicated_state, &portal_token,
                                &frame_token, &devtools_frame_token);
-  RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyForPortal(
+  RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyForPortalOrFencedFrame(
       agent_scheduling_group_, this, proxy_routing_id, frame_token,
       devtools_frame_token, portal_element);
   proxy->SetReplicatedState(std::move(initial_replicated_state));
@@ -3609,13 +3610,14 @@
     const blink::PortalToken& portal_token,
     const blink::WebElement& portal_element) {
   int proxy_routing_id = MSG_ROUTING_NONE;
+  blink::mojom::FrameReplicationStatePtr replicated_state =
+      blink::mojom::FrameReplicationState::New();
   blink::RemoteFrameToken frame_token;
   base::UnguessableToken devtools_frame_token;
-  auto replicated_state = blink::mojom::FrameReplicationState::New();
   GetFrameHost()->AdoptPortal(portal_token, &proxy_routing_id,
                               &replicated_state, &frame_token,
                               &devtools_frame_token);
-  RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyForPortal(
+  RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyForPortalOrFencedFrame(
       agent_scheduling_group_, this, proxy_routing_id, frame_token,
       devtools_frame_token, portal_element);
   proxy->SetReplicatedState(std::move(replicated_state));
@@ -3623,10 +3625,21 @@
 }
 
 blink::WebRemoteFrame* RenderFrameImpl::CreateFencedFrame(
-    const blink::WebElement& fenced_frame_element) {
-  // TODO(crbug.com/1123606): Initialize and register remote frame and return it
-  // from here.
-  return nullptr;
+    const blink::WebElement& fenced_frame) {
+  int proxy_routing_id = MSG_ROUTING_NONE;
+  blink::mojom::FrameReplicationStatePtr initial_replicated_state =
+      blink::mojom::FrameReplicationState::New();
+  blink::RemoteFrameToken frame_token;
+  base::UnguessableToken devtools_frame_token;
+
+  // TODO(crbug.com/1123606): Call mojom::FrameHost::CreateFencedFrame() once we
+  // introduce it in a subsequent CL.
+
+  RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyForPortalOrFencedFrame(
+      agent_scheduling_group_, this, proxy_routing_id, frame_token,
+      devtools_frame_token, fenced_frame);
+  proxy->SetReplicatedState(std::move(initial_replicated_state));
+  return proxy->web_frame();
 }
 
 blink::WebFrame* RenderFrameImpl::FindFrame(const blink::WebString& name) {
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 355db6f..5c8c75f 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -151,20 +151,21 @@
   return proxy.release();
 }
 
-RenderFrameProxy* RenderFrameProxy::CreateProxyForPortal(
+RenderFrameProxy* RenderFrameProxy::CreateProxyForPortalOrFencedFrame(
     AgentSchedulingGroup& agent_scheduling_group,
     RenderFrameImpl* parent,
     int proxy_routing_id,
     const blink::RemoteFrameToken& frame_token,
     const base::UnguessableToken& devtools_frame_token,
-    const blink::WebElement& portal_element) {
+    const blink::WebElement& frame_owner) {
   auto proxy = base::WrapUnique(
       new RenderFrameProxy(agent_scheduling_group, proxy_routing_id));
-  blink::WebRemoteFrame* web_frame = blink::WebRemoteFrame::CreateForPortal(
-      blink::mojom::TreeScopeType::kDocument, proxy.get(),
-      proxy->blink_interface_registry_.get(),
-      proxy->GetRemoteAssociatedInterfaces(), frame_token, devtools_frame_token,
-      portal_element);
+  blink::WebRemoteFrame* web_frame =
+      blink::WebRemoteFrame::CreateForPortalOrFencedFrame(
+          blink::mojom::TreeScopeType::kDocument, proxy.get(),
+          proxy->blink_interface_registry_.get(),
+          proxy->GetRemoteAssociatedInterfaces(), frame_token,
+          devtools_frame_token, frame_owner);
   proxy->Init(web_frame, parent->render_view());
   return proxy.release();
 }
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index 6f2ca3b5..ff54d72 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -97,15 +97,15 @@
       const base::UnguessableToken& devtools_frame_token,
       mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces);
 
-  // Creates a RenderFrameProxy to be used with a portal owned by |parent|.
-  // |routing_id| is the routing id of this new RenderFrameProxy.
-  static RenderFrameProxy* CreateProxyForPortal(
+  // Creates a RenderFrameProxy to be used with a portal or fenced frame owned
+  // by |parent|. |routing_id| is the routing id of this new RenderFrameProxy.
+  static RenderFrameProxy* CreateProxyForPortalOrFencedFrame(
       AgentSchedulingGroup& agent_scheduling_group,
       RenderFrameImpl* parent,
       int proxy_routing_id,
       const blink::RemoteFrameToken& frame_token,
       const base::UnguessableToken& devtools_frame_token,
-      const blink::WebElement& portal_element);
+      const blink::WebElement& frame_owner_element);
 
   // Returns the RenderFrameProxy for the given routing ID.
   static RenderFrameProxy* FromRoutingID(int routing_id);
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 3a5834f..0bc7245 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -631,9 +631,14 @@
 // but the system default is true.
 #if defined(OS_MAC)
   is_elastic_overscroll_enabled_ = true;
-#elif defined(OS_WIN) || defined(OS_ANDROID)
+#elif defined(OS_WIN)
   is_elastic_overscroll_enabled_ =
       base::FeatureList::IsEnabled(features::kElasticOverscroll);
+#elif defined(OS_ANDROID)
+  is_elastic_overscroll_enabled_ =
+      !base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableOverscrollEdgeEffect) &&
+      base::FeatureList::IsEnabled(features::kElasticOverscroll);
 #else
   is_elastic_overscroll_enabled_ = false;
 #endif
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 0274833..911a272 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -14,7 +14,6 @@
 #include "base/feature_list.h"
 #include "base/files/file.h"
 #include "base/files/file_util.h"
-#include "base/no_destructor.h"
 #include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -402,10 +401,9 @@
 ShellContentBrowserClient::RunSecondaryMediaService() {
   mojo::Remote<::media::mojom::MediaService> remote;
 #if BUILDFLAG(ENABLE_CAST_RENDERER)
-  static base::NoDestructor<
-      base::SequenceLocalStorageSlot<std::unique_ptr<::media::MediaService>>>
+  static base::SequenceLocalStorageSlot<std::unique_ptr<::media::MediaService>>
       service;
-  service->emplace(::media::CreateMediaServiceForTesting(
+  service.emplace(::media::CreateMediaServiceForTesting(
       remote.BindNewPipeAndPassReceiver()));
 #endif
   return remote;
diff --git a/content/test/data/accessibility/html/video-text-only-expected-blink.txt b/content/test/data/accessibility/html/video-text-only-expected-blink.txt
index c38319c..344d39f 100644
--- a/content/test/data/accessibility/html/video-text-only-expected-blink.txt
+++ b/content/test/data/accessibility/html/video-text-only-expected-blink.txt
@@ -3,45 +3,123 @@
 ++++genericContainer
 ++++++video name='Unable to play media.' restriction=disabled
 ++++++++genericContainer ignored
+++++++++++genericContainer ignored invisible
 ++++++++++genericContainer ignored
+++++++++++++button ignored invisible
 ++++++++++genericContainer ignored
+++++++++++++genericContainer ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++button ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored name='0:00'
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored name='/ 0:00'
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++slider ignored invisible
+++++++++++++++++++button ignored invisible
+++++++++++++++++button ignored invisible
+++++++++++++++++button ignored invisible
+++++++++++++++slider ignored invisible
 ++++++++++menu ignored invisible
 ++++++++++menu ignored invisible
 ++++++++++menu ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Play'
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Fullscreen'
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Download'
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Mute'
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Cast'
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Captions'
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Picture in picture'
 ++++++staticText name=' '
 ++++++++inlineTextBox name=' '
 ++++++video name='Unable to play media.' restriction=disabled
 ++++++++genericContainer ignored
+++++++++++genericContainer ignored invisible
 ++++++++++genericContainer ignored
+++++++++++++button ignored invisible
 ++++++++++genericContainer ignored
+++++++++++++genericContainer ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++button ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored name='0:00'
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored name='/ 0:00'
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++slider ignored invisible
+++++++++++++++++++button ignored invisible
+++++++++++++++++button ignored invisible
+++++++++++++++++button ignored invisible
+++++++++++++++slider ignored invisible
 ++++++++++menu ignored invisible
 ++++++++++menu ignored invisible
 ++++++++++menu ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
-++++++++++++button ignored invisible
-++++++++++++genericContainer ignored invisible
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Play'
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Fullscreen'
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Download'
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Mute'
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Cast'
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Captions'
+++++++++++++menuItem ignored invisible
+++++++++++++++button ignored invisible
+++++++++++++++genericContainer ignored invisible
+++++++++++++++++genericContainer ignored invisible
+++++++++++++++++++staticText ignored invisible name='Picture in picture'
diff --git a/content/test/data/android/scroll_element_offscreen.html b/content/test/data/android/scroll_element_offscreen.html
new file mode 100644
index 0000000..09943d00
--- /dev/null
+++ b/content/test/data/android/scroll_element_offscreen.html
@@ -0,0 +1,24 @@
+<!-- Simple test page to scroll an element partially off-screen and check bounding box -->
+<html>
+  <head>
+    <script>
+      function scrollUp() {
+        window.scrollBy(0,200);
+      }
+
+      function createText() {
+        var containingDiv = document.getElementById("scroll_view");
+        for (i = 0; i < 100; i++) {
+          var paragraph = document.createElement('p');
+          paragraph.innerHTML = "Example Text " + i;
+
+          containingDiv.appendChild(paragraph);
+        }
+      }
+    </script>
+  </head>
+  <body onload="createText()">
+    <div id="scroll_view">
+    </div>
+  </body>
+</html>
\ No newline at end of file
diff --git a/device/vr/android/arcore/arcore_anchor_manager.cc b/device/vr/android/arcore/arcore_anchor_manager.cc
index 4c4a466d..e82d31b 100644
--- a/device/vr/android/arcore/arcore_anchor_manager.cc
+++ b/device/vr/android/arcore/arcore_anchor_manager.cc
@@ -33,6 +33,13 @@
            << anchor_id_to_anchor_info_.size()
            << ", updated_anchor_ids_.size()=" << updated_anchor_ids_.size();
 
+#if DCHECK_IS_ON()
+  DCHECK(was_anchor_data_retrieved_in_current_frame_)
+      << "Update() must not be called twice in a row without a call to "
+         "GetAnchorsData() in between";
+  was_anchor_data_retrieved_in_current_frame_ = false;
+#endif
+
   std::vector<uint64_t> all_anchors_ids;
   all_anchors_ids.reserve(anchor_id_to_anchor_info_.size());
   for (const auto& anchor_id_and_object : anchor_id_to_anchor_info_) {
@@ -66,6 +73,10 @@
     }
   }
 
+#if DCHECK_IS_ON()
+  was_anchor_data_retrieved_in_current_frame_ = true;
+#endif
+
   return mojom::XRAnchorsData::New(std::move(all_anchors_ids),
                                    std::move(updated_anchors));
 }
diff --git a/device/vr/android/arcore/arcore_anchor_manager.h b/device/vr/android/arcore/arcore_anchor_manager.h
index 8bbcacc..449a20c 100644
--- a/device/vr/android/arcore/arcore_anchor_manager.h
+++ b/device/vr/android/arcore/arcore_anchor_manager.h
@@ -29,8 +29,7 @@
   ~ArCoreAnchorManager();
 
   // Updates anchor manager state - it should be called in every frame if the
-  // ARCore session supports anchors. Currently, all ARCore sessions support
-  // anchors.
+  // ARCore session has anchors feature enabled.
   void Update(ArFrame* ar_frame);
 
   mojom::XRAnchorsDataPtr GetAnchorsData() const;
@@ -87,6 +86,17 @@
   // Set containing IDs of anchors updated in the last frame. It should be
   // modified only during calls to |Update()|.
   std::set<AnchorId> updated_anchor_ids_;
+
+#if DCHECK_IS_ON()
+  // True if |GetAnchorsData()| was called after |Update()|. It is used to track
+  // if |Update()| was called twice in a row w/o a call to |GetAnchorsData()| in
+  // between. Initially true since we expect the call to |Update()| to happen
+  // next.
+  // TODO(https://crbug.com/1192844): remove the assumption that the calls to
+  // |Update()| will always be followed by at least one call to
+  // |GetAnchorsData()| before the next call to |Update()| happens.
+  mutable bool was_anchor_data_retrieved_in_current_frame_ = true;
+#endif
 };
 
 }  // namespace device
diff --git a/device/vr/android/arcore/arcore_impl.cc b/device/vr/android/arcore/arcore_impl.cc
index 0c27a88..54a5609 100644
--- a/device/vr/android/arcore/arcore_impl.cc
+++ b/device/vr/android/arcore/arcore_impl.cc
@@ -875,6 +875,8 @@
 }
 
 mojom::VRPosePtr ArCoreImpl::Update(bool* camera_updated) {
+  DVLOG(3) << __func__;
+
   TRACE_EVENT0("gpu", "ArCoreImpl Update");
 
   DCHECK(IsOnGlThread());
@@ -882,10 +884,9 @@
   DCHECK(arcore_frame_.is_valid());
   DCHECK(camera_updated);
 
-  ArStatus status;
-
   TRACE_EVENT_BEGIN0("gpu", "ArCore Update");
-  status = ArSession_update(arcore_session_.get(), arcore_frame_.get());
+  ArStatus status =
+      ArSession_update(arcore_session_.get(), arcore_frame_.get());
   TRACE_EVENT_END0("gpu", "ArCore Update");
 
   if (status != AR_SUCCESS) {
@@ -894,8 +895,15 @@
     return nullptr;
   }
 
-  // TODO(https://crbug.com/1192844): If the call was successful, we should
-  // Update() the ArCore entity managers.
+  if (plane_manager_) {
+    TRACE_EVENT0("gpu", "ArCorePlaneManager Update");
+    plane_manager_->Update(arcore_frame_.get());
+  }
+
+  if (anchor_manager_) {
+    TRACE_EVENT0("gpu", "ArCoreAnchorManager Update");
+    anchor_manager_->Update(arcore_frame_.get());
+  }
 
   // If we get here, assume we have a valid camera image, but we don't know yet
   // if tracking is working.
@@ -954,16 +962,6 @@
   auto mojo_from_viewer =
       GetMojomVRPoseFromArPose(arcore_session_.get(), arcore_pose.get());
 
-  if (plane_manager_) {
-    TRACE_EVENT0("gpu", "ArCorePlaneManager Update");
-    plane_manager_->Update(arcore_frame_.get());
-  }
-
-  if (anchor_manager_) {
-    TRACE_EVENT0("gpu", "ArCoreAnchorManager Update");
-    anchor_manager_->Update(arcore_frame_.get());
-  }
-
   return mojo_from_viewer;
 }
 
@@ -1188,6 +1186,10 @@
     case mojom::XRNativeOriginInformation::Tag::HAND_JOINT_SPACE_INFO:
       // Unsupported by ARCore:
       return absl::nullopt;
+    case mojom::XRNativeOriginInformation::Tag::IMAGE_INDEX:
+      // TODO(https://crbug.com/1143575): Add hit test support for tracked
+      // images.
+      return absl::nullopt;
     case mojom::XRNativeOriginInformation::Tag::ANCHOR_ID:
       // Validate that we know which anchor's space the hit test is interested
       // in tracking.
@@ -1427,6 +1429,10 @@
                              : false;
     case mojom::XRNativeOriginInformation::Tag::HAND_JOINT_SPACE_INFO:
       return false;
+    case mojom::XRNativeOriginInformation::Tag::IMAGE_INDEX:
+      // TODO(https://crbug.com/1143575): Needed for anchor creation relaitve to
+      // tracked images.
+      return false;
   }
 }
 
@@ -1472,6 +1478,11 @@
                              : absl::nullopt;
     case mojom::XRNativeOriginInformation::Tag::HAND_JOINT_SPACE_INFO:
       return absl::nullopt;
+
+    case mojom::XRNativeOriginInformation::Tag::IMAGE_INDEX:
+      // TODO(https://crbug.com/1143575): Needed for hit test and anchors
+      // support for tracked images.
+      return absl::nullopt;
   }
 }
 
diff --git a/device/vr/android/arcore/arcore_plane_manager.cc b/device/vr/android/arcore/arcore_plane_manager.cc
index d77ba65..23bc273 100644
--- a/device/vr/android/arcore/arcore_plane_manager.cc
+++ b/device/vr/android/arcore/arcore_plane_manager.cc
@@ -126,6 +126,13 @@
 }
 
 void ArCorePlaneManager::Update(ArFrame* ar_frame) {
+#if DCHECK_IS_ON()
+  DCHECK(was_plane_data_retrieved_in_current_frame_)
+      << "Update() must not be called twice in a row without a call to "
+         "GetDetectedPlanesData() in between";
+  was_plane_data_retrieved_in_current_frame_ = false;
+#endif
+
   ArTrackableType plane_tracked_type = AR_TRACKABLE_PLANE;
 
   // First, ask ARCore about all Plane trackables updated in the current frame.
@@ -196,12 +203,17 @@
         return !base::Contains(new_plane_id_to_plane_info,
                                plane_address_and_id.second);
       });
+
   plane_id_to_plane_info_.swap(new_plane_id_to_plane_info);
   updated_plane_ids_.swap(updated_plane_ids);
 }
 
 mojom::XRPlaneDetectionDataPtr ArCorePlaneManager::GetDetectedPlanesData()
     const {
+  DVLOG(3) << __func__ << ": plane_id_to_plane_info_.size()="
+           << plane_id_to_plane_info_.size()
+           << ", updated_plane_ids_.size()=" << updated_plane_ids_.size();
+
   std::vector<uint64_t> all_plane_ids;
   all_plane_ids.reserve(plane_id_to_plane_info_.size());
   for (const auto& plane_id_and_object : plane_id_to_plane_info_) {
@@ -261,6 +273,10 @@
     }
   }
 
+#if DCHECK_IS_ON()
+  was_plane_data_retrieved_in_current_frame_ = true;
+#endif
+
   return mojom::XRPlaneDetectionData::New(std::move(all_plane_ids),
                                           std::move(updated_planes));
 }
diff --git a/device/vr/android/arcore/arcore_plane_manager.h b/device/vr/android/arcore/arcore_plane_manager.h
index 771f532..4c0e0ba 100644
--- a/device/vr/android/arcore/arcore_plane_manager.h
+++ b/device/vr/android/arcore/arcore_plane_manager.h
@@ -103,6 +103,17 @@
   // Set containing IDs of planes updated in the last frame. It should be
   // modified only during calls to |Update()|.
   std::set<PlaneId> updated_plane_ids_;
+
+#if DCHECK_IS_ON()
+  // True if |GetDetectedPlanesData()| was called after |Update()|. It is used
+  // to track if |Update()| was called twice in a row w/o a call to
+  // |GetDetectedPlanesData()| in between. Initially true since we expect the
+  // call to |Update()| to happen next.
+  // TODO(https://crbug.com/1192844): remove the assumption that the calls to
+  // |Update()| will always be followed by at least one call to
+  // |GetDetectedPlanesData()| before the next call to |Update()| happens.
+  mutable bool was_plane_data_retrieved_in_current_frame_ = true;
+#endif
 };
 
 }  // namespace device
diff --git a/device/vr/openxr/openxr_anchor_manager.cc b/device/vr/openxr/openxr_anchor_manager.cc
index 111bf9d2..03cb72c 100644
--- a/device/vr/openxr/openxr_anchor_manager.cc
+++ b/device/vr/openxr/openxr_anchor_manager.cc
@@ -199,6 +199,7 @@
                                              native_origin_from_anchor);
     case mojom::XRNativeOriginInformation::Tag::PLANE_ID:
     case mojom::XRNativeOriginInformation::Tag::HAND_JOINT_SPACE_INFO:
+    case mojom::XRNativeOriginInformation::Tag::IMAGE_INDEX:
       // Unsupported for now
       return absl::nullopt;
     case mojom::XRNativeOriginInformation::Tag::ANCHOR_ID:
diff --git a/device/vr/openxr/openxr_scene_understanding_manager.cc b/device/vr/openxr/openxr_scene_understanding_manager.cc
index 259556a5..1860309 100644
--- a/device/vr/openxr/openxr_scene_understanding_manager.cc
+++ b/device/vr/openxr/openxr_scene_understanding_manager.cc
@@ -378,6 +378,8 @@
       return absl::nullopt;
     case mojom::XRNativeOriginInformation::Tag::HAND_JOINT_SPACE_INFO:
       return absl::nullopt;
+    case mojom::XRNativeOriginInformation::Tag::IMAGE_INDEX:
+      return absl::nullopt;
   }
 }
 
diff --git a/device/vr/public/mojom/vr_service.mojom b/device/vr/public/mojom/vr_service.mojom
index 6427f99c..5a72941 100644
--- a/device/vr/public/mojom/vr_service.mojom
+++ b/device/vr/public/mojom/vr_service.mojom
@@ -514,14 +514,15 @@
 // XRReferenceSpaceType, XRBoundedReferenceSpace) or returns an XRSpace (for
 // example XRAnchor, XRPlane, XRInputSource). Native origin can be identified,
 // depending on its type, by the id of the entity (this is the case for planes
-// and anchors), by reference space type, handedness & joint, or by input source
-// id & input source space type.
+// and anchors), index (for tracked images), by reference space type, handedness
+// & joint, or by input source id & input source space type.
 union XRNativeOriginInformation {
   XRInputSourceSpaceInfo input_source_space_info;
   uint64 plane_id;
   uint64 anchor_id;
   XRReferenceSpaceType reference_space_type;
   XRHandJointSpaceInfo hand_joint_space_info;
+  uint32 image_index;
 };
 
 // Input sources have 2 kinds of native origins, each of them backs a different
diff --git a/extensions/browser/extension_action.cc b/extensions/browser/extension_action.cc
index 1e931fba..66d4ecf 100644
--- a/extensions/browser/extension_action.cc
+++ b/extensions/browser/extension_action.cc
@@ -61,7 +61,7 @@
   // SetIcon function arguments.
   const char* size_string;
   // Scale factor for which the represantion should be used.
-  ui::ScaleFactor scale;
+  ui::ResourceScaleFactor scale;
 };
 
 template <class T>
diff --git a/extensions/browser/extension_function.cc b/extensions/browser/extension_function.cc
index 69ab9ce..d4793b28 100644
--- a/extensions/browser/extension_function.cc
+++ b/extensions/browser/extension_function.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
+#include "base/no_destructor.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
 #include "base/trace_event/memory_allocator_dump.h"
diff --git a/extensions/browser/extension_icon_image_unittest.cc b/extensions/browser/extension_icon_image_unittest.cc
index e132e1d..8fc50b0 100644
--- a/extensions/browser/extension_icon_image_unittest.cc
+++ b/extensions/browser/extension_icon_image_unittest.cc
@@ -29,9 +29,10 @@
 namespace extensions {
 namespace {
 
-SkBitmap CreateBlankBitmapForScale(int size_dip, ui::ScaleFactor scale_factor) {
+SkBitmap CreateBlankBitmapForScale(int size_dip,
+                                   ui::ResourceScaleFactor scale_factor) {
   SkBitmap bitmap;
-  const float scale = ui::GetScaleForScaleFactor(scale_factor);
+  const float scale = ui::GetScaleForResourceScaleFactor(scale_factor);
   bitmap.allocN32Pixels(static_cast<int>(size_dip * scale),
                         static_cast<int>(size_dip * scale));
   bitmap.eraseColor(SkColorSetARGB(0, 0, 0, 0));
@@ -134,10 +135,11 @@
 }  // namespace
 
 TEST_F(ExtensionIconImageTest, Basic) {
-  std::vector<ui::ScaleFactor> supported_factors;
+  std::vector<ui::ResourceScaleFactor> supported_factors;
   supported_factors.push_back(ui::SCALE_FACTOR_100P);
   supported_factors.push_back(ui::SCALE_FACTOR_200P);
-  ui::test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors);
+  ui::test::ScopedSetSupportedResourceScaleFactors scoped_supported(
+      supported_factors);
   scoped_refptr<Extension> extension(CreateExtension(
       "extension_icon_image", ManifestLocation::kInvalidLocation));
   ASSERT_TRUE(extension.get() != nullptr);
@@ -208,10 +210,11 @@
 // There is no resource with either exact or bigger size, but there is a smaller
 // resource.
 TEST_F(ExtensionIconImageTest, FallbackToSmallerWhenNoBigger) {
-  std::vector<ui::ScaleFactor> supported_factors;
+  std::vector<ui::ResourceScaleFactor> supported_factors;
   supported_factors.push_back(ui::SCALE_FACTOR_100P);
   supported_factors.push_back(ui::SCALE_FACTOR_200P);
-  ui::test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors);
+  ui::test::ScopedSetSupportedResourceScaleFactors scoped_supported(
+      supported_factors);
   scoped_refptr<Extension> extension(CreateExtension(
       "extension_icon_image", ManifestLocation::kInvalidLocation));
   ASSERT_TRUE(extension.get() != nullptr);
diff --git a/extensions/browser/image_loader.cc b/extensions/browser/image_loader.cc
index ec061ddc..c738684c 100644
--- a/extensions/browser/image_loader.cc
+++ b/extensions/browser/image_loader.cc
@@ -239,8 +239,8 @@
   std::vector<ImageRepresentation> info_list;
 
   std::set<float> scales;
-  for (auto scale : ui::GetSupportedScaleFactors())
-    scales.insert(ui::GetScaleForScaleFactor(scale));
+  for (auto scale : ui::GetSupportedResourceScaleFactors())
+    scales.insert(ui::GetScaleForResourceScaleFactor(scale));
 
   // There may not be a screen in unit tests.
   auto* screen = display::Screen::GetScreen();
diff --git a/extensions/renderer/script_context.cc b/extensions/renderer/script_context.cc
index d44ac6a..6ffc155 100644
--- a/extensions/renderer/script_context.cc
+++ b/extensions/renderer/script_context.cc
@@ -8,6 +8,7 @@
 #include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
 #include "base/logging.h"
+#include "base/no_destructor.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
diff --git a/extensions/shell/common/shell_content_client.cc b/extensions/shell/common/shell_content_client.cc
index 422734cf..18deacb1 100644
--- a/extensions/shell/common/shell_content_client.cc
+++ b/extensions/shell/common/shell_content_client.cc
@@ -88,7 +88,7 @@
 
 base::StringPiece ShellContentClient::GetDataResource(
     int resource_id,
-    ui::ScaleFactor scale_factor) {
+    ui::ResourceScaleFactor scale_factor) {
   return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
       resource_id, scale_factor);
 }
diff --git a/extensions/shell/common/shell_content_client.h b/extensions/shell/common/shell_content_client.h
index 06db44e..7ea87ce 100644
--- a/extensions/shell/common/shell_content_client.h
+++ b/extensions/shell/common/shell_content_client.h
@@ -21,8 +21,9 @@
       std::vector<content::PepperPluginInfo>* plugins) override;
   void AddAdditionalSchemes(Schemes* schemes) override;
   std::u16string GetLocalizedString(int message_id) override;
-  base::StringPiece GetDataResource(int resource_id,
-                                    ui::ScaleFactor scale_factor) override;
+  base::StringPiece GetDataResource(
+      int resource_id,
+      ui::ResourceScaleFactor scale_factor) override;
   base::RefCountedMemory* GetDataResourceBytes(int resource_id) override;
   gfx::Image& GetNativeImageNamed(int resource_id) override;
 
diff --git a/fuchsia/engine/DEPS b/fuchsia/engine/DEPS
index c9c4265..c4accdd 100644
--- a/fuchsia/engine/DEPS
+++ b/fuchsia/engine/DEPS
@@ -19,6 +19,7 @@
   "+third_party/blink/public/common/switches.h",
   "+third_party/widevine/cdm/widevine_cdm_common.h",
   "+ui/base",
+  "+ui/display/display_switches.h",
   "+ui/gfx",
   "+ui/gl/gl_switches.h",
   "+ui/ozone/public",
diff --git a/fuchsia/engine/browser/accessibility_bridge.cc b/fuchsia/engine/browser/accessibility_bridge.cc
index 7bc6fbf..3179b236 100644
--- a/fuchsia/engine/browser/accessibility_bridge.cc
+++ b/fuchsia/engine/browser/accessibility_bridge.cc
@@ -26,7 +26,7 @@
 constexpr size_t kMaxNodesPerUpdate = 16;
 
 constexpr size_t kMaxNodesPerDelete =
-    fuchsia::accessibility::semantics::MAX_NODES_PER_UPDATE - 1;
+    fuchsia::accessibility::semantics::MAX_NODES_PER_UPDATE;
 
 // Error allowed for each edge when converting from gfx::RectF to gfx::Rect.
 constexpr float kRectConversionError = 0.5;
@@ -47,29 +47,6 @@
   return offset_container_id;
 }
 
-// Applies |op| to elements of |items| in batches. Maximum batch size is set
-// by |batch_size|.
-template <typename T>
-inline void BatchApply(std::vector<T> items,
-                       size_t batch_size,
-                       base::RepeatingCallback<void(std::vector<T>)> op) {
-  const size_t total_items = items.size();
-  if (total_items == 0) {
-    return;
-  }
-
-  std::vector<T> batch;
-  batch.reserve(batch_size);
-  for (size_t current = 0; current < total_items; current += batch_size) {
-    const size_t next_batch_size = std::min(total_items - current, batch_size);
-    std::move(items.begin() + current,
-              items.begin() + current + next_batch_size,
-              std::back_inserter(batch));
-    op.Run(std::move(batch));
-    batch.clear();
-  }
-}
-
 }  // namespace
 
 AccessibilityBridge::AccessibilityBridge(
@@ -159,28 +136,25 @@
 
 void AccessibilityBridge::TryCommit() {
   if (commit_inflight_ || (to_delete_.empty() && to_update_.empty()) ||
-      ShouldHoldCommit())
+      ShouldHoldCommit()) {
     return;
+  }
 
   // Deletions come before updates because first the nodes are deleted, and
   // then we update the parents to no longer point at them.
-  BatchApply(
-      std::move(to_delete_), kMaxNodesPerDelete,
-      base::BindRepeating(
-          &fuchsia::accessibility::semantics::SemanticTree::DeleteSemanticNodes,
-          base::Unretained(semantic_tree_.get())));
+  for (auto& batch : to_delete_) {
+    semantic_tree_->DeleteSemanticNodes(std::move(batch));
+  }
+  to_delete_.clear();
 
-  BatchApply(
-      std::move(to_update_), kMaxNodesPerUpdate,
-      base::BindRepeating(
-          &fuchsia::accessibility::semantics::SemanticTree::UpdateSemanticNodes,
-          base::Unretained(semantic_tree_.get())));
+  for (auto& batch : to_update_) {
+    semantic_tree_->UpdateSemanticNodes(std::move(batch));
+  }
+  to_update_.clear();
 
   semantic_tree_->CommitUpdates(
       fit::bind_member(this, &AccessibilityBridge::OnCommitComplete));
   commit_inflight_ = true;
-  to_delete_.clear();
-  to_update_.clear();
 }
 
 void AccessibilityBridge::OnCommitComplete() {
@@ -380,7 +354,7 @@
 void AccessibilityBridge::OnNodeDeleted(ui::AXTree* tree, int32_t node_id) {
   DCHECK(tree);
 
-  to_delete_.push_back(
+  AppendToDeleteList(
       id_mapper_->ToFuchsiaNodeID(tree->GetAXTreeID(), node_id, false));
 }
 
@@ -429,8 +403,8 @@
     // that OnAtomicUpdateFinished() is called, offset_container_children_ will
     // be correct, so we can simply overwrite the existing update.
     auto* fuchsia_node =
-        GetUpdatedNode(tree->GetAXTreeID(), child_node->data().id,
-                       /*replace_existing=*/true);
+        EnsureAndGetUpdatedNode(tree->GetAXTreeID(), child_node->data().id,
+                                /*replace_existing=*/true);
     DCHECK(fuchsia_node);
   }
 }
@@ -461,8 +435,8 @@
     // there's an existing update for this node from OnNodeDataChanged(). This
     // update may not have the correct offset container and/or transform, so we
     // should replace it.
-    auto* fuchsia_node = GetUpdatedNode(tree->GetAXTreeID(), node.id,
-                                        /*replace_existing=*/true);
+    auto* fuchsia_node = EnsureAndGetUpdatedNode(tree->GetAXTreeID(), node.id,
+                                                 /*replace_existing=*/true);
     DCHECK(fuchsia_node);
 
     if (node.HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId)) {
@@ -547,8 +521,9 @@
     if (kv.second.is_connected)
       continue;  // No work to do, trees connected and present.
 
-    auto* fuchsia_node = GetUpdatedNode(parent_ax_tree_id, ax_node->id(),
-                                        /*replace_existing=*/false);
+    auto* fuchsia_node =
+        EnsureAndGetUpdatedNode(parent_ax_tree_id, ax_node->id(),
+                                /*replace_existing=*/false);
     DCHECK(fuchsia_node);
     // Now, the connection really happens:
     // This node, from the parent tree, will have a child that points to the
@@ -577,21 +552,22 @@
     // data changed. This makes sure that it contains the focus information. If
     // it is not part of the current update, no need to send this information,
     // as it is redundant.
-    auto* node =
-        focus_changed
-            ? GetUpdatedNode(new_focused_node->first, new_focused_node->second,
-                             /*replace_existing=*/false)
-            : GetNodeIfChangingInUpdate(new_focused_node->first,
-                                        new_focused_node->second);
+    auto* node = focus_changed
+                     ? EnsureAndGetUpdatedNode(new_focused_node->first,
+                                               new_focused_node->second,
+                                               /*replace_existing=*/false)
+                     : GetNodeIfChangingInUpdate(new_focused_node->first,
+                                                 new_focused_node->second);
     if (node)
       node->mutable_states()->set_has_input_focus(true);
   }
 
   if (last_focused_node_id_) {
-    auto* node = focus_changed ? GetUpdatedNode(last_focused_node_id_->first,
-                                                last_focused_node_id_->second,
-                                                /*replace_existing=*/false)
-                               : nullptr /*already updated above*/;
+    auto* node = focus_changed
+                     ? EnsureAndGetUpdatedNode(last_focused_node_id_->first,
+                                               last_focused_node_id_->second,
+                                               /*replace_existing=*/false)
+                     : nullptr /*already updated above*/;
     if (node)
       node->mutable_states()->set_has_input_focus(false);
   }
@@ -738,25 +714,44 @@
   return std::make_pair(tree->GetAXTreeID(), node->id());
 }
 
+void AccessibilityBridge::AppendToDeleteList(uint32_t node_id) {
+  if (to_delete_.empty() || to_delete_.back().size() == kMaxNodesPerDelete) {
+    to_delete_.emplace_back();
+  }
+  to_delete_.back().push_back(std::move(node_id));
+}
+
+void AccessibilityBridge::AppendToUpdateList(
+    fuchsia::accessibility::semantics::Node node) {
+  if (to_update_.empty() || to_update_.back().size() == kMaxNodesPerUpdate) {
+    to_update_.emplace_back();
+  }
+  to_update_.back().push_back(std::move(node));
+}
+
 fuchsia::accessibility::semantics::Node*
 AccessibilityBridge::GetNodeIfChangingInUpdate(const ui::AXTreeID& tree_id,
                                                ui::AXNodeID node_id) {
   auto fuchsia_node_id = id_mapper_->ToFuchsiaNodeID(tree_id, node_id, false);
-  auto result = std::find_if(
-      to_update_.rbegin(), to_update_.rend(),
-      [&fuchsia_node_id](const fuchsia::accessibility::semantics::Node& node) {
-        return node.node_id() == fuchsia_node_id;
-      });
-  if (result == to_update_.rend())
-    return nullptr;
 
-  return &(*result);
+  for (auto& update_batch : to_update_) {
+    auto result =
+        std::find_if(update_batch.rbegin(), update_batch.rend(),
+                     [&fuchsia_node_id](
+                         const fuchsia::accessibility::semantics::Node& node) {
+                       return node.node_id() == fuchsia_node_id;
+                     });
+    if (result != update_batch.rend())
+      return &(*result);
+  }
+
+  return nullptr;
 }
 
-fuchsia::accessibility::semantics::Node* AccessibilityBridge::GetUpdatedNode(
-    const ui::AXTreeID& tree_id,
-    ui::AXNodeID node_id,
-    bool replace_existing) {
+fuchsia::accessibility::semantics::Node*
+AccessibilityBridge::EnsureAndGetUpdatedNode(const ui::AXTreeID& tree_id,
+                                             ui::AXNodeID node_id,
+                                             bool replace_existing) {
   auto* fuchsia_node = GetNodeIfChangingInUpdate(tree_id, node_id);
   if (fuchsia_node && !replace_existing)
     return fuchsia_node;
@@ -785,6 +780,6 @@
     return fuchsia_node;
   }
 
-  to_update_.push_back(std::move(new_fuchsia_node));
-  return &to_update_.back();
+  AppendToUpdateList(std::move(new_fuchsia_node));
+  return &to_update_.back().back();
 }
diff --git a/fuchsia/engine/browser/accessibility_bridge.h b/fuchsia/engine/browser/accessibility_bridge.h
index 48287f0..2b77843 100644
--- a/fuchsia/engine/browser/accessibility_bridge.h
+++ b/fuchsia/engine/browser/accessibility_bridge.h
@@ -161,7 +161,7 @@
   // existing update for the node (if one exists).
   //
   // Returns nullptr if the node does not exist.
-  fuchsia::accessibility::semantics::Node* GetUpdatedNode(
+  fuchsia::accessibility::semantics::Node* EnsureAndGetUpdatedNode(
       const ui::AXTreeID& tree_id,
       ui::AXNodeID node_id,
       bool replace_existing);
@@ -171,6 +171,12 @@
   absl::optional<AXNodeID> GetFocusFromThisOrDescendantFrame(
       const ui::AXSerializableTree* tree) const;
 
+  // Enqueues |node_id| for deletion in the subsequent tree update.
+  void AppendToDeleteList(uint32_t node_id);
+
+  // Enqueues changes to |node| in the subsequent tree update.
+  void AppendToUpdateList(fuchsia::accessibility::semantics::Node node);
+
   // content::WebContentsObserver implementation.
   void AccessibilityEventReceived(
       const content::AXEventNotificationDetails& details) override;
@@ -225,9 +231,9 @@
   // Whether semantic updates are enabled.
   bool enable_semantic_updates_ = false;
 
-  // Cache for pending data to be sent to the Semantic Tree between commits.
-  std::vector<uint32_t> to_delete_;
-  std::vector<fuchsia::accessibility::semantics::Node> to_update_;
+  // Buffer for pending data to be sent to the Semantic Tree between commits.
+  std::vector<std::vector<uint32_t>> to_delete_;
+  std::vector<std::vector<fuchsia::accessibility::semantics::Node>> to_update_;
   bool commit_inflight_ = false;
 
   // Maintain a map from AXNode IDs to a list of the AXNode IDs of descendant
diff --git a/fuchsia/engine/browser/frame_impl.cc b/fuchsia/engine/browser/frame_impl.cc
index 34a11c7..533c51e 100644
--- a/fuchsia/engine/browser/frame_impl.cc
+++ b/fuchsia/engine/browser/frame_impl.cc
@@ -176,8 +176,8 @@
     base::small_map<std::map<content::WebContents*, FrameImpl*>>;
 
 FrameImplMap& WebContentsToFrameImplMap() {
-  static base::NoDestructor<FrameImplMap> frame_impl_map;
-  return *frame_impl_map;
+  static FrameImplMap frame_impl_map;
+  return frame_impl_map;
 }
 
 content::PermissionType FidlPermissionTypeToContentPermissionType(
diff --git a/fuchsia/engine/web_instance_host/web_instance_host.cc b/fuchsia/engine/web_instance_host/web_instance_host.cc
index 1b2e53d5..adbc3763c 100644
--- a/fuchsia/engine/web_instance_host/web_instance_host.cc
+++ b/fuchsia/engine/web_instance_host/web_instance_host.cc
@@ -61,6 +61,7 @@
 #include "services/network/public/cpp/network_switches.h"
 #include "third_party/blink/public/common/switches.h"
 #include "third_party/widevine/cdm/widevine_cdm_common.h"
+#include "ui/display/display_switches.h"
 #include "ui/gfx/switches.h"
 #include "ui/gl/gl_switches.h"
 #include "ui/ozone/public/ozone_switches.h"
@@ -331,6 +332,7 @@
       switches::kEnableCastStreamingReceiver,
       switches::kEnableFeatures,
       switches::kEnableLowEndDeviceMode,
+      switches::kForceDeviceScaleFactor,
       switches::kForceGpuMemAvailableMb,
       switches::kForceGpuMemDiscardableLimitMb,
       switches::kForceMaxTextureSize,
diff --git a/google_apis/BUILD.gn b/google_apis/BUILD.gn
index fddf591..a0ec864 100644
--- a/google_apis/BUILD.gn
+++ b/google_apis/BUILD.gn
@@ -243,6 +243,7 @@
     "//base/test:test_support",
     "//build:branding_buildflags",
     "//build:chromeos_buildflags",
+    "//google_apis/calendar:calendar_unittests",
     "//testing/gmock",
     "//testing/gtest",
   ]
@@ -253,6 +254,11 @@
     data = [ "test/" ]
   }
 
+  if (is_fuchsia) {
+    additional_manifest_fragments =
+        [ "//build/config/fuchsia/test/network_capabilities.test-cmx" ]
+  }
+
   if (enable_extensions) {
     deps += [ "//google_apis/drive:drive_unittests" ]
   }
@@ -267,6 +273,8 @@
 bundle_data("google_apis_unittest_bundle_data") {
   testonly = true
   sources = [
+    "test/data/calendar/events.json",
+    "test/data/calendar/invalid_events.json",
     "test/data/gaia/all_base_urls.json",
     "test/data/gaia/all_urls.json",
     "test/data/gaia/api_keys.json",
diff --git a/google_apis/calendar/BUILD.gn b/google_apis/calendar/BUILD.gn
new file mode 100644
index 0000000..5fb7f58
--- /dev/null
+++ b/google_apis/calendar/BUILD.gn
@@ -0,0 +1,64 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+import("//testing/test.gni")
+
+static_library("calendar") {
+  sources = [
+    "calendar_api_requests.cc",
+    "calendar_api_requests.h",
+    "calendar_api_response_types.cc",
+    "calendar_api_response_types.h",
+    "calendar_api_url_generator.cc",
+    "calendar_api_url_generator.h",
+  ]
+
+  deps = [
+    "//base",
+    "//base/third_party/dynamic_annotations",
+    "//components/signin/public/identity_manager",
+    "//crypto",
+    "//google_apis:google_apis",
+    "//google_apis/drive:drive",
+    "//net",
+    "//services/network/public/cpp",
+  ]
+}
+
+source_set("test_support") {
+  testonly = true
+
+  sources = []
+
+  public_deps = [
+    ":calendar",
+    "//base",
+    "//base/test:test_support",
+    "//google_apis:test_support",
+    "//google_apis/drive:drive",
+    "//google_apis/drive:test_support",
+    "//net:test_support",
+  ]
+}
+
+source_set("calendar_unittests") {
+  testonly = true
+
+  sources = [
+    "calendar_api_requests_unittest.cc",
+    "calendar_api_response_types_unittest.cc",
+    "calendar_api_url_generator_unittest.cc",
+  ]
+
+  deps = [
+    ":calendar",
+    ":test_support",
+    "//base",
+    "//base/test:test_support",
+    "//services/network:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/google_apis/calendar/DEPS b/google_apis/calendar/DEPS
new file mode 100644
index 0000000..c30fdc3
--- /dev/null
+++ b/google_apis/calendar/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+third_party/googletest/src/googletest/include/gtest/gtest.h",
+]
\ No newline at end of file
diff --git a/google_apis/calendar/calendar_api_requests.cc b/google_apis/calendar/calendar_api_requests.cc
new file mode 100644
index 0000000..a85931d
--- /dev/null
+++ b/google_apis/calendar/calendar_api_requests.cc
@@ -0,0 +1,98 @@
+// 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 "google_apis/calendar/calendar_api_requests.h"
+
+#include <stddef.h>
+
+#include "base/task_runner_util.h"
+#include "net/base/url_util.h"
+
+namespace google_apis {
+
+namespace calendar {
+
+const char kCalendarEventListFields[] =
+    "timeZone,etag,kind,items(id,summary,colorId, "
+    "status,start(dateTime),end(dateTime),htmlLink)";
+
+CalendarApiGetRequest::CalendarApiGetRequest(RequestSender* sender,
+                                             const std::string& fields)
+    : UrlFetchRequestBase(sender, ProgressCallback(), ProgressCallback()),
+      fields_(fields) {}
+
+CalendarApiGetRequest::~CalendarApiGetRequest() = default;
+
+GURL CalendarApiGetRequest::GetURL() const {
+  GURL url = GetURLInternal();
+  if (!fields_.empty())
+    url = net::AppendOrReplaceQueryParameter(url, "fields", fields_);
+  return url;
+}
+
+CalendarApiEventsRequest::CalendarApiEventsRequest(
+    RequestSender* sender,
+    const CalendarApiUrlGenerator& url_generator,
+    CalendarEventListCallback callback,
+    const base::Time& start_time,
+    const base::Time& end_time)
+    : CalendarApiGetRequest(sender, kCalendarEventListFields),
+      callback_(std::move(callback)),
+      url_generator_(url_generator),
+      start_time_(start_time),
+      end_time_(end_time) {
+  DCHECK(!callback_.is_null());
+}
+
+CalendarApiEventsRequest::~CalendarApiEventsRequest() = default;
+
+GURL CalendarApiEventsRequest::GetURLInternal() const {
+  return url_generator_.GetCalendarEventListUrl(start_time_, end_time_);
+}
+
+void CalendarApiEventsRequest::ProcessURLFetchResults(
+    const network::mojom::URLResponseHead* response_head,
+    base::FilePath response_file,
+    std::string response_body) {
+  // TODO(https://crbug.com/1222483): use common error code.
+  DriveApiErrorCode error = GetErrorCode();
+  switch (error) {
+    case HTTP_SUCCESS:
+      base::PostTaskAndReplyWithResult(
+          blocking_task_runner(), FROM_HERE,
+          base::BindOnce(&CalendarApiEventsRequest::Parse,
+                         std::move(response_body)),
+          base::BindOnce(&CalendarApiEventsRequest::OnDataParsed,
+                         weak_ptr_factory_.GetWeakPtr(), error));
+      break;
+    default:
+      RunCallbackOnPrematureFailure(error);
+      OnProcessURLFetchResultsComplete();
+      break;
+  }
+}
+
+void CalendarApiEventsRequest::OnDataParsed(DriveApiErrorCode error,
+                                            std::unique_ptr<EventList> events) {
+  // TODO(https://crbug.com/1222483): use common error code.
+  if (!events)
+    error = DRIVE_PARSE_ERROR;
+  std::move(callback_).Run(error, std::move(events));
+  OnProcessURLFetchResultsComplete();
+}
+
+void CalendarApiEventsRequest::RunCallbackOnPrematureFailure(
+    DriveApiErrorCode error) {
+  std::move(callback_).Run(error, std::unique_ptr<EventList>());
+}
+
+// static
+std::unique_ptr<EventList> CalendarApiEventsRequest::Parse(std::string json) {
+  std::unique_ptr<base::Value> value = ParseJson(json);
+
+  return value ? EventList::CreateFrom(*value) : nullptr;
+}
+
+}  // namespace calendar
+}  // namespace google_apis
diff --git a/google_apis/calendar/calendar_api_requests.h b/google_apis/calendar/calendar_api_requests.h
new file mode 100644
index 0000000..232bdc5
--- /dev/null
+++ b/google_apis/calendar/calendar_api_requests.h
@@ -0,0 +1,96 @@
+// 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 GOOGLE_APIS_CALENDAR_CALENDAR_API_REQUESTS_H_
+#define GOOGLE_APIS_CALENDAR_CALENDAR_API_REQUESTS_H_
+
+#include "google_apis/calendar/calendar_api_response_types.h"
+#include "google_apis/calendar/calendar_api_url_generator.h"
+#include "google_apis/drive/base_requests.h"
+#include "google_apis/drive/drive_api_error_codes.h"
+
+namespace google_apis {
+
+namespace calendar {
+
+// Callback used for requests that the server returns Events data
+// formatted into JSON value.
+// TODO(https://crbug.com/1222483): use common error code.
+using CalendarEventListCallback =
+    base::OnceCallback<void(DriveApiErrorCode error,
+                            std::unique_ptr<EventList> events)>;
+
+// This is base class of the Calendar API related requests.
+class CalendarApiGetRequest : public UrlFetchRequestBase {
+ public:
+  // `fields` is an optional request parameter that allows you to specify the
+  // fields you want returned in the response data. Documentation:
+  // https://developers.google.com/calendar/api/guides/performance?hl=en#partial-response
+  CalendarApiGetRequest(RequestSender* sender, const std::string& fields);
+
+  CalendarApiGetRequest(const CalendarApiGetRequest&) = delete;
+  CalendarApiGetRequest& operator=(const CalendarApiGetRequest&) = delete;
+  ~CalendarApiGetRequest() override;
+
+ protected:
+  // UrlFetchRequestBase:
+  GURL GetURL() const override;
+
+  // Derived classes should override GetURLInternal instead of GetURL()
+  // directly since fields are appended in the GetURL() method.
+  virtual GURL GetURLInternal() const = 0;
+
+ private:
+  // Optional parameter in the request.
+  std::string fields_;
+};
+
+// Request to fetch calendar events.
+class CalendarApiEventsRequest : public CalendarApiGetRequest {
+ public:
+  CalendarApiEventsRequest(RequestSender* sender,
+                           const CalendarApiUrlGenerator& url_generator,
+                           CalendarEventListCallback callback,
+                           const base::Time& start_time,
+                           const base::Time& end_time);
+  CalendarApiEventsRequest(const CalendarApiEventsRequest&) = delete;
+  CalendarApiEventsRequest& operator=(const CalendarApiEventsRequest&) = delete;
+  ~CalendarApiEventsRequest() override;
+
+ protected:
+  // CalendarApiGetRequest:
+  GURL GetURLInternal() const override;
+
+  // UrlFetchRequestBase:
+  void ProcessURLFetchResults(
+      const network::mojom::URLResponseHead* response_head,
+      const base::FilePath response_file,
+      std::string response_body) override;
+
+  // TODO(https://crbug.com/1222483): use common error code.
+  void RunCallbackOnPrematureFailure(DriveApiErrorCode code) override;
+
+ private:
+  // Parses the |json| string to EventList.
+  static std::unique_ptr<EventList> Parse(std::string json);
+
+  // Receives the parsed result and invokes the callback.
+  // TODO(https://crbug.com/1222483): use common error code.
+  void OnDataParsed(DriveApiErrorCode error, std::unique_ptr<EventList> events);
+
+  CalendarEventListCallback callback_;
+  const CalendarApiUrlGenerator url_generator_;
+
+  const base::Time start_time_;
+  const base::Time end_time_;
+
+  // Note: This should remain the last member so it'll be destroyed and
+  // invalidate its weak pointers before any other members are destroyed.
+  base::WeakPtrFactory<CalendarApiEventsRequest> weak_ptr_factory_{this};
+};
+
+}  // namespace calendar
+}  // namespace google_apis
+
+#endif  // GOOGLE_APIS_CALENDAR_CALENDAR_API_REQUESTS_H_
diff --git a/google_apis/calendar/calendar_api_requests_unittest.cc b/google_apis/calendar/calendar_api_requests_unittest.cc
new file mode 100644
index 0000000..36b692c43
--- /dev/null
+++ b/google_apis/calendar/calendar_api_requests_unittest.cc
@@ -0,0 +1,121 @@
+// 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 "google_apis/calendar/calendar_api_requests.h"
+
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/task_environment.h"
+#include "google_apis/calendar/calendar_api_response_types.h"
+#include "google_apis/drive/dummy_auth_service.h"
+#include "google_apis/drive/request_sender.h"
+#include "google_apis/drive/test_util.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/test/test_shared_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace google_apis {
+
+namespace calendar {
+
+namespace {
+const char kTestUserAgent[] = "test-user-agent";
+}
+
+class CalendarApiRequestsTest : public testing::Test {
+ public:
+  CalendarApiRequestsTest()
+      : test_shared_loader_factory_(
+            base::MakeRefCounted<network::TestSharedURLLoaderFactory>(
+                nullptr /* network_service */,
+                true /* is_trusted */)) {}
+
+  void SetUp() override {
+    request_sender_ = std::make_unique<RequestSender>(
+        std::make_unique<DummyAuthService>(), test_shared_loader_factory_,
+        task_environment_.GetMainThreadTaskRunner(), kTestUserAgent,
+        TRAFFIC_ANNOTATION_FOR_TESTS);
+
+    test_server_.RegisterRequestHandler(
+        base::BindRepeating(&CalendarApiRequestsTest::HandleDataFileRequest,
+                            base::Unretained(this)));
+    ASSERT_TRUE(test_server_.Start());
+    url_generator_ = std::make_unique<CalendarApiUrlGenerator>();
+    url_generator_->SetBaseUrlForTesting(test_server_.base_url().spec());
+  }
+
+  void TearDown() override {
+    // Deleting the sender here will delete all request objects.
+    request_sender_.reset();
+    // Wait for any DeleteSoon tasks to run.
+    task_environment_.RunUntilIdle();
+  }
+
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::MainThreadType::IO};
+  net::EmbeddedTestServer test_server_;
+  std::unique_ptr<RequestSender> request_sender_;
+  std::unique_ptr<CalendarApiUrlGenerator> url_generator_;
+  scoped_refptr<network::TestSharedURLLoaderFactory>
+      test_shared_loader_factory_;
+  net::test_server::HttpRequest http_request_;
+
+  // Returns the mock calendar event list.
+  std::unique_ptr<net::test_server::HttpResponse> HandleDataFileRequest(
+      const net::test_server::HttpRequest& request) {
+    http_request_ = request;
+
+    // Return the response from the event json file.
+    return test_util::CreateHttpResponseFromFile(
+        test_util::GetTestFilePath("calendar/events.json"));
+  }
+};
+
+// Tests the CalendarApiRequestsTest can generate the correct url and get the
+// correct response.
+TEST_F(CalendarApiRequestsTest, GetEventListRequest) {
+  DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+  std::unique_ptr<EventList> events;
+  base::Time start;
+  base::Time end;
+
+  EXPECT_TRUE(base::Time::FromString("13 Jun 2021 10:00 GMT", &start));
+  EXPECT_TRUE(base::Time::FromString("16 Jun 2021 10:00 GMT", &end));
+
+  {
+    base::RunLoop run_loop;
+    auto request = std::make_unique<CalendarApiEventsRequest>(
+        request_sender_.get(), *url_generator_,
+        test_util::CreateQuitCallback(
+            &run_loop, test_util::CreateCopyResultCallback(&error, &events)),
+        start, end);
+
+    request_sender_->StartRequestWithAuthRetry(std::move(request));
+    run_loop.Run();
+  }
+
+  EXPECT_EQ(HTTP_SUCCESS, error);
+  EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method);
+  EXPECT_EQ(
+      "/calendar/v3/calendars/primary/"
+      "events?timeMin=2021-06-13T10%3A00%3A00.000Z"
+      "&timeMax=2021-06-16T10%3A00%3A00.000Z"
+      "&fields=timeZone%2Cetag%2Ckind%2C"
+      "items(id%2Csummary%2CcolorId%2C+status%2C"
+      "start(dateTime)%2Cend(dateTime)%2ChtmlLink)",
+      http_request_.relative_url);
+
+  ASSERT_TRUE(events.get());
+
+  EXPECT_EQ(events->time_zone(), "America/Los_Angeles");
+  base::Time::Exploded exploded;
+  events->items()[0]->start_time().date_time().LocalExplode(&exploded);
+  EXPECT_EQ(exploded.month, 11);
+}
+
+}  // namespace calendar
+}  // namespace google_apis
diff --git a/google_apis/calendar/calendar_api_response_types.cc b/google_apis/calendar/calendar_api_response_types.cc
new file mode 100644
index 0000000..3e57cac
--- /dev/null
+++ b/google_apis/calendar/calendar_api_response_types.cc
@@ -0,0 +1,139 @@
+// 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 "google_apis/calendar/calendar_api_response_types.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/json/json_value_converter.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "google_apis/drive/time_util.h"
+
+namespace google_apis {
+
+namespace calendar {
+
+namespace {
+
+// EventList
+const char kKind[] = "kind";
+const char kTimeZone[] = "timeZone";
+const char kETag[] = "etag";
+const char kItems[] = "items";
+const char kCalendarEventListKind[] = "calendar#events";
+
+// DateTime
+const char kDateTime[] = "dateTime";
+
+// CalendarEvent
+const char kId[] = "id";
+const char kSummary[] = "summary";
+const char kStart[] = "start";
+const char kEnd[] = "end";
+const char kColorId[] = "colorId";
+const char kStatus[] = "status";
+const char kHtmlLink[] = "htmlLink";
+const char kCalendarEventKind[] = "calendar#event";
+
+// Checks if the JSON is expected kind.
+// TODO(https://crbug.com/1222483): move this to common and share with drive api
+// parsers.
+bool IsResourceKindExpected(const base::Value& value,
+                            const std::string& expected_kind) {
+  const std::string* kind = value.FindStringKey(kKind);
+  return kind && *kind == expected_kind;
+}
+}  // namespace
+
+DateTime::DateTime() = default;
+
+DateTime::DateTime(const DateTime& src) = default;
+
+DateTime& DateTime::operator=(const DateTime& src) = default;
+
+DateTime::~DateTime() = default;
+
+// static
+void DateTime::RegisterJSONConverter(
+    base::JSONValueConverter<DateTime>* converter) {
+  converter->RegisterCustomField<base::Time>(kDateTime, &DateTime::date_time_,
+                                             &util::GetTimeFromString);
+}
+
+// static
+bool DateTime::CreateDateTimeFromValue(const base::Value* value,
+                                       DateTime* time) {
+  base::JSONValueConverter<DateTime> converter;
+  if (!converter.Convert(*value, time)) {
+    DVLOG(1) << "Unable to create: Invalid DateTime JSON!";
+    return false;
+  }
+  return true;
+}
+
+CalendarEvent::CalendarEvent() = default;
+
+CalendarEvent::~CalendarEvent() = default;
+
+// static
+void CalendarEvent::RegisterJSONConverter(
+    base::JSONValueConverter<CalendarEvent>* converter) {
+  converter->RegisterStringField(kId, &CalendarEvent::id_);
+  converter->RegisterStringField(kSummary, &CalendarEvent::summary_);
+  converter->RegisterStringField(kHtmlLink, &CalendarEvent::html_link_);
+  converter->RegisterStringField(kColorId, &CalendarEvent::color_id_);
+  converter->RegisterStringField(kStatus, &CalendarEvent::status_);
+  converter->RegisterCustomValueField(kStart, &CalendarEvent::start_time_,
+                                      &DateTime::CreateDateTimeFromValue);
+  converter->RegisterCustomValueField(kEnd, &CalendarEvent::end_time_,
+                                      &DateTime::CreateDateTimeFromValue);
+}
+
+// static
+std::unique_ptr<CalendarEvent> CalendarEvent::CreateFrom(
+    const base::Value& value) {
+  auto event = std::make_unique<CalendarEvent>();
+  base::JSONValueConverter<CalendarEvent> converter;
+  if (!IsResourceKindExpected(value, kCalendarEventKind) ||
+      !converter.Convert(value, event.get())) {
+    DVLOG(1) << "Unable to create: Invalid CalendarEvent JSON!";
+    return nullptr;
+  }
+
+  return event;
+}
+
+EventList::EventList() = default;
+
+EventList::~EventList() = default;
+
+// static
+void EventList::RegisterJSONConverter(
+    base::JSONValueConverter<EventList>* converter) {
+  converter->RegisterStringField(kTimeZone, &EventList::time_zone_);
+  converter->RegisterStringField(kETag, &EventList::etag_);
+  converter->RegisterStringField(kKind, &EventList::kind_);
+  converter->RegisterRepeatedMessage<CalendarEvent>(kItems, &EventList::items_);
+}
+
+// static
+std::unique_ptr<EventList> EventList::CreateFrom(const base::Value& value) {
+  auto events = std::make_unique<EventList>();
+  base::JSONValueConverter<EventList> converter;
+  if (!IsResourceKindExpected(value, kCalendarEventListKind) ||
+      !converter.Convert(value, events.get())) {
+    DVLOG(1) << "Unable to create: Invalid EventList JSON!";
+    return nullptr;
+  }
+  return events;
+}
+
+}  // namespace calendar
+}  // namespace google_apis
diff --git a/google_apis/calendar/calendar_api_response_types.h b/google_apis/calendar/calendar_api_response_types.h
new file mode 100644
index 0000000..603eaa9
--- /dev/null
+++ b/google_apis/calendar/calendar_api_response_types.h
@@ -0,0 +1,156 @@
+// 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 GOOGLE_APIS_CALENDAR_CALENDAR_API_RESPONSE_TYPES_H_
+#define GOOGLE_APIS_CALENDAR_CALENDAR_API_RESPONSE_TYPES_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "url/gurl.h"
+
+namespace base {
+class Value;
+template <class StructType>
+class JSONValueConverter;
+}  // namespace base
+
+namespace google_apis {
+
+namespace calendar {
+
+// Parses the time filed in the calendar Events.list response.
+class DateTime {
+ public:
+  DateTime();
+  DateTime(const DateTime&);
+  DateTime& operator=(const DateTime& src);
+  ~DateTime();
+
+  // Registers the mapping between JSON field names and the members in this
+  // class.
+  static void RegisterJSONConverter(
+      base::JSONValueConverter<DateTime>* converter);
+
+  // Creates DateTime from parsed JSON.
+  static bool CreateDateTimeFromValue(const base::Value* value, DateTime* time);
+
+  const base::Time& date_time() const { return date_time_; }
+  void set_date_time(const base::Time& date_time) { date_time_ = date_time; }
+
+ private:
+  base::Time date_time_;
+};
+
+// Parses the event item from the response. Not every field is parsed. If you
+// find the field you want to use is not parsed here, you will need to add it.
+class CalendarEvent {
+ public:
+  CalendarEvent();
+  CalendarEvent(const CalendarEvent&) = delete;
+  CalendarEvent& operator=(const CalendarEvent&) = delete;
+  ~CalendarEvent();
+
+  // Registers the mapping between JSON field names and the members in this
+  // class.
+  static void RegisterJSONConverter(
+      base::JSONValueConverter<CalendarEvent>* converter);
+
+  // Creates CalendarEvent from parsed JSON.
+  static std::unique_ptr<CalendarEvent> CreateFrom(const base::Value& value);
+
+  // The ID of this Calendar Event.
+  const std::string& id() const { return id_; }
+  void set_id(const std::string& id) { id_ = id; }
+
+  // The title of the event (meeting's name).
+  const std::string& summary() const { return summary_; }
+  void set_summary(const std::string& summary) { summary_ = summary; }
+
+  // An absolute link to this event in the Google Calendar Web UI.
+  const std::string& html_link() const { return html_link_; }
+  void set_html_link(const std::string& link) { html_link_ = link; }
+
+  // The color id of the event.
+  const std::string& color_id() const { return color_id_; }
+  void set_color_id(const std::string& color_id) { color_id_ = color_id; }
+
+  // The status of the event.
+  const std::string& status() const { return status_; }
+  void set_status(const std::string& status) { status_ = status; }
+
+  const DateTime& start_time() const { return start_time_; }
+  void set_start_time(const DateTime& start_time) { start_time_ = start_time; }
+
+  const DateTime& end_time() const { return end_time_; }
+  void set_end_time(const DateTime& end_time) { end_time_ = end_time; }
+
+ private:
+  std::string id_;
+  std::string summary_;
+  std::string html_link_;
+  std::string color_id_;
+  std::string status_;
+  DateTime start_time_;
+  DateTime end_time_;
+};
+
+// Parses a list of calendar events.
+class EventList {
+ public:
+  EventList();
+  EventList(const EventList&) = delete;
+  EventList& operator=(const EventList&) = delete;
+  ~EventList();
+
+  // Registers the mapping between JSON field names and the members in this
+  // class.
+  static void RegisterJSONConverter(
+      base::JSONValueConverter<EventList>* converter);
+
+  // Creates EventList from parsed JSON.
+  static std::unique_ptr<EventList> CreateFrom(const base::Value& value);
+
+  // Returns time zone.
+  const std::string& time_zone() const { return time_zone_; }
+
+  // Returns ETag for this calendar.
+  const std::string& etag() const { return etag_; }
+
+  // Returns the kind.
+  const std::string& kind() const { return kind_; }
+
+  void set_time_zone(const std::string& time_zone) { time_zone_ = time_zone; }
+  void set_etag(const std::string& etag) { etag_ = etag; }
+  void set_kind(const std::string& kind) { kind_ = kind; }
+
+  // Returns a set of events in this calendar.
+  const std::vector<std::unique_ptr<CalendarEvent>>& items() const {
+    return items_;
+  }
+  std::vector<std::unique_ptr<CalendarEvent>>* mutable_items() {
+    return &items_;
+  }
+
+ private:
+  std::string time_zone_;
+  std::string etag_;
+  std::string kind_;
+
+  std::vector<std::unique_ptr<CalendarEvent>> items_;
+};
+
+}  // namespace calendar
+}  // namespace google_apis
+
+#endif  // GOOGLE_APIS_CALENDAR_CALENDAR_API_RESPONSE_TYPES_H_
diff --git a/google_apis/calendar/calendar_api_response_types_unittest.cc b/google_apis/calendar/calendar_api_response_types_unittest.cc
new file mode 100644
index 0000000..92dddb7
--- /dev/null
+++ b/google_apis/calendar/calendar_api_response_types_unittest.cc
@@ -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.
+
+#include "google_apis/calendar/calendar_api_response_types.h"
+
+#include "base/time/time.h"
+#include "base/values.h"
+#include "google_apis/drive/test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace google_apis {
+
+namespace calendar {
+
+TEST(CalendarAPIResponseTypesTest, ParseEventList) {
+  std::unique_ptr<base::Value> events =
+      test_util::LoadJSONFile("calendar/events.json");
+  ASSERT_TRUE(events.get());
+
+  ASSERT_EQ(base::Value::Type::DICTIONARY, events->type());
+  auto event_list = EventList::CreateFrom(*events);
+
+  EXPECT_EQ("America/Los_Angeles", event_list->time_zone());
+  EXPECT_EQ("calendar#events", event_list->kind());
+  EXPECT_EQ("\"p32ofplf5q6gf20g\"", event_list->etag());
+  EXPECT_EQ(3U, event_list->items().size());
+
+  const CalendarEvent& event = *event_list->items()[0];
+  base::Time start_time;
+  ASSERT_TRUE(base::Time::FromUTCExploded(
+      {2020, 11 /* November */, 1 /* Monday */, 2, 18, 0, 0, 0}, &start_time));
+  EXPECT_EQ(start_time, event.start_time().date_time());
+
+  base::Time end_time;
+  ASSERT_TRUE(base::Time::FromUTCExploded(
+      {2020, 11 /* November */, 1 /* Monday */, 2, 18, 30, 0, 0}, &end_time));
+  EXPECT_EQ(end_time, event.end_time().date_time());
+  EXPECT_EQ(event.summary(), "Mobile weekly team meeting ");
+  EXPECT_EQ(event.id(), "or8221sirt4ogftest");
+  EXPECT_EQ(
+      event.html_link(),
+      "https://www.google.com/calendar/event?eid=b3I4MjIxc2lydDRvZ2Ztest");
+  EXPECT_EQ(event.color_id(), "3");
+  EXPECT_EQ(event.status(), "confirmed");
+}
+
+TEST(CalendarAPIResponseTypesTest, ParseFailed) {
+  std::unique_ptr<base::Value> events =
+      test_util::LoadJSONFile("calendar/invalid_events.json");
+  ASSERT_TRUE(events.get());
+
+  ASSERT_EQ(base::Value::Type::DICTIONARY, events->type());
+  auto event_list = EventList::CreateFrom(*events);
+  ASSERT_EQ(event_list, nullptr);
+}
+}  // namespace calendar
+}  // namespace google_apis
diff --git a/google_apis/calendar/calendar_api_url_generator.cc b/google_apis/calendar/calendar_api_url_generator.cc
new file mode 100644
index 0000000..f6e1b2a6
--- /dev/null
+++ b/google_apis/calendar/calendar_api_url_generator.cc
@@ -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.
+
+#include "google_apis/calendar/calendar_api_url_generator.h"
+
+#include "google_apis/drive/time_util.h"
+#include "net/base/url_util.h"
+
+namespace google_apis {
+
+namespace calendar {
+
+namespace {
+
+// Hard coded URLs for communication with a google calendar server.
+const char kCalendarV3EventsUrl[] = "calendar/v3/calendars/primary/events";
+const char kCalendarV3ColorUrl[] = "calendar/v3/colors";
+const char kTimeMaxParameterName[] = "timeMax";
+const char kTimeMinParameterName[] = "timeMin";
+
+}  // namespace
+
+CalendarApiUrlGenerator::CalendarApiUrlGenerator() = default;
+
+CalendarApiUrlGenerator::CalendarApiUrlGenerator(
+    const CalendarApiUrlGenerator& src) = default;
+
+CalendarApiUrlGenerator& CalendarApiUrlGenerator::operator=(
+    const CalendarApiUrlGenerator& src) = default;
+
+CalendarApiUrlGenerator::~CalendarApiUrlGenerator() = default;
+
+// TODO(https://crbug.com/1222483): get this from GaiaUrls class instead.
+// The same for the DriveApiUrlGenerator.
+const char CalendarApiUrlGenerator::kBaseUrlForProduction[] =
+    "https://www.googleapis.com";
+
+GURL CalendarApiUrlGenerator::GetCalendarEventListUrl(
+    const base::Time& start_time,
+    const base::Time& end_time) const {
+  GURL url = base_url_.Resolve(kCalendarV3EventsUrl);
+  std::string start_time_string = util::FormatTimeAsString(start_time);
+  std::string end_time_string = util::FormatTimeAsString(end_time);
+  url = net::AppendOrReplaceQueryParameter(url, kTimeMinParameterName,
+                                           start_time_string);
+  url = net::AppendOrReplaceQueryParameter(url, kTimeMaxParameterName,
+                                           end_time_string);
+  return url;
+}
+
+GURL CalendarApiUrlGenerator::GetCalendarColorListUrl() const {
+  GURL url = base_url_.Resolve(kCalendarV3ColorUrl);
+  return url;
+}
+
+}  // namespace calendar
+}  // namespace google_apis
diff --git a/google_apis/calendar/calendar_api_url_generator.h b/google_apis/calendar/calendar_api_url_generator.h
new file mode 100644
index 0000000..3ac0c1c
--- /dev/null
+++ b/google_apis/calendar/calendar_api_url_generator.h
@@ -0,0 +1,46 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GOOGLE_APIS_CALENDAR_CALENDAR_API_URL_GENERATOR_H_
+#define GOOGLE_APIS_CALENDAR_CALENDAR_API_URL_GENERATOR_H_
+
+#include <string>
+
+#include "base/time/time.h"
+#include "url/gurl.h"
+
+namespace google_apis {
+
+namespace calendar {
+
+// This class is used to generate URLs for communicating with calendar api
+// servers for production, and a local server for testing.
+class CalendarApiUrlGenerator {
+ public:
+  CalendarApiUrlGenerator();
+  CalendarApiUrlGenerator(const CalendarApiUrlGenerator& src);
+  CalendarApiUrlGenerator& operator=(const CalendarApiUrlGenerator& src);
+  ~CalendarApiUrlGenerator();
+
+  // The base URL for communicating with the production calendar api server.
+  static const char kBaseUrlForProduction[];
+
+  // Returns a URL to fetch a list of calendar events.
+  GURL GetCalendarEventListUrl(const base::Time& start_time,
+                               const base::Time& end_time) const;
+
+  // Returns a URL to fetch a map of calendar color id to color code.
+  GURL GetCalendarColorListUrl() const;
+
+  // The base url can be set here. It defaults to the production base url.
+  void SetBaseUrlForTesting(const std::string& url) { base_url_ = GURL(url); }
+
+ private:
+  GURL base_url_{CalendarApiUrlGenerator::kBaseUrlForProduction};
+};
+
+}  // namespace calendar
+}  // namespace google_apis
+
+#endif  // GOOGLE_APIS_CALENDAR_CALENDAR_API_URL_GENERATOR_H_
diff --git a/google_apis/calendar/calendar_api_url_generator_unittest.cc b/google_apis/calendar/calendar_api_url_generator_unittest.cc
new file mode 100644
index 0000000..3176571f2
--- /dev/null
+++ b/google_apis/calendar/calendar_api_url_generator_unittest.cc
@@ -0,0 +1,34 @@
+// 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 "google_apis/calendar/calendar_api_url_generator.h"
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
+
+namespace google_apis {
+
+namespace calendar {
+
+// Make sure the hard-coded urls are returned.
+TEST(CalendarApiUrlGeneratorTest, GetColorListUrl) {
+  CalendarApiUrlGenerator url_generator_;
+  EXPECT_EQ("https://www.googleapis.com/calendar/v3/colors",
+            url_generator_.GetCalendarColorListUrl().spec());
+}
+
+TEST(CalendarApiUrlGeneratorTest, GetEventListUrl) {
+  CalendarApiUrlGenerator url_generator_;
+  base::Time start;
+  EXPECT_TRUE(base::Time::FromString("13 Jun 2021 10:00 PST", &start));
+  base::Time end;
+  EXPECT_TRUE(base::Time::FromString("16 Jun 2021 10:00 PST", &end));
+  EXPECT_EQ(
+      "https://www.googleapis.com/calendar/v3/calendars/primary/"
+      "events?timeMin=2021-06-13T18%3A00%3A00.000Z"
+      "&timeMax=2021-06-16T18%3A00%3A00.000Z",
+      url_generator_.GetCalendarEventListUrl(start, end).spec());
+}
+
+}  // namespace calendar
+}  // namespace google_apis
diff --git a/google_apis/test/data/calendar/events.json b/google_apis/test/data/calendar/events.json
new file mode 100644
index 0000000..f54619b
--- /dev/null
+++ b/google_apis/test/data/calendar/events.json
@@ -0,0 +1,270 @@
+{
+    "kind": "calendar#events",
+    "etag": "\"p32ofplf5q6gf20g\"",
+    "summary": "test1@google.com",
+    "updated": "2021-06-18T07:17:10.718Z",
+    "timeZone": "America/Los_Angeles",
+    "accessRole": "owner",
+    "defaultReminders": [
+     {
+      "method": "popup",
+      "minutes": 10
+     }
+    ],
+    "nextSyncToken": "CLD81eXRoPECtest",
+    "items": [
+     {
+      "kind": "calendar#event",
+      "etag": "\"3231955547274000\"",
+      "id": "or8221sirt4ogftest",
+      "status": "confirmed",
+      "htmlLink": "https://www.google.com/calendar/event?eid=b3I4MjIxc2lydDRvZ2Ztest",
+      "created": "2018-05-14T18:55:59.000Z",
+      "updated": "2021-03-17T10:42:53.637Z",
+      "summary": "Mobile weekly team meeting ",
+      "description": "\u003ca href=\"https://docs.google.com/document/d/1BSA5pn6dpPQOIBuvtest/edit\"\u003eMeeting agenda and notes\u003c/a\u003e",
+      "colorId": "3",
+      "creator": {
+       "email": "test2@google.com",
+       "displayName": "test2 testlatname2"
+      },
+      "organizer": {
+       "email": "test3@google.com"
+      },
+      "start": {
+       "dateTime": "2020-11-02T10:00:00-08:00",
+       "timeZone": "America/Los_Angeles"
+      },
+      "end": {
+       "dateTime": "2020-11-02T10:30:00-08:00",
+       "timeZone": "America/Los_Angeles"
+      },
+      "recurrence": [
+       "RRULE:FREQ=WEEKLY;UNTIL=20201109T075959Z;BYDAY=MO"
+      ],
+      "iCalUID": "or8221test@google.com",
+      "sequence": 6,
+      "attendees": [
+       {
+        "email": "test4@google.com",
+        "responseStatus": "needsAction"
+       },
+       {
+        "email": "google.com_726f6f6d5f75735f6d7test@resource.calendar.google.com",
+        "displayName": "MTV-1055-2-Pellucid (3) [GVC, Jamboard, Phone]",
+        "resource": true,
+        "responseStatus": "declined"
+       },
+       {
+        "email": "test1@google.com",
+        "displayName": "test1 testlatname1",
+        "self": true,
+        "responseStatus": "needsAction"
+       },
+       {
+        "email": "test2@google.com",
+        "displayName": "test2 testlatname2",
+        "responseStatus": "accepted"
+       }
+      ],
+      "hangoutLink": "https://meet.google.com/jbe-test",
+      "conferenceData": {
+       "entryPoints": [
+        {
+         "entryPointType": "video",
+         "uri": "https://meet.google.com/jbe-test",
+         "label": "meet.google.com/jbe-test"
+        },
+        {
+         "entryPointType": "more",
+         "uri": "https://tel.meet/jbe-test?pin=6390031303227",
+         "pin": "6390031303227"
+        },
+        {
+         "regionCode": "US",
+         "entryPointType": "phone",
+         "uri": "tel:+1-469-305-0860",
+         "label": "+1 469-305-0860",
+         "pin": "383483"
+        }
+       ],
+       "conferenceSolution": {
+        "key": {
+         "type": "hangoutsMeet"
+        },
+        "name": "Google Meet",
+        "iconUri": "https://fonts.gstatic.com/s/i/productlogos/meet_2020q4/v6/web-512dp/logo_meet_2020q4_color_2x_web_512dp.png"
+       },
+       "conferenceId": "jbe-test",
+       "signature": "ACn9hYFMw8sU85VncET68xXqnRrE"
+      },
+      "guestsCanModify": true,
+      "reminders": {
+       "useDefault": true
+      },
+      "eventType": "default"
+     },
+     {
+      "kind": "calendar#event",
+      "etag": "\"3246207098130000\"",
+      "id": "7749obefodf44k4kt7r8lr0l8b",
+      "status": "confirmed",
+      "htmlLink": "https://www.google.com/calendar/event?eid=Nzc0OW9iZWZvZGY0NGs0a3Q3cjtest",
+      "created": "2021-05-24T18:52:31.000Z",
+      "updated": "2021-06-07T22:05:49.065Z",
+      "summary": "Calendar view weekly meeting",
+      "description": "meeting notes: \u003ca href=\"https://docs.google.com/document/d/1UikvkL__BYOtest/edit?resourcekey=0-e1MW5R3-74eHVaj4Fad_vw#\"\u003ehttps://docs.google.com/document/d/1UikvkL__BYOtest/edit?resourcekey=0-e1MW5R3-74eHVaj4Fad_vw#\u003c/a\u003e",
+      "colorId": "3",
+      "creator": {
+       "email": "test1@google.com",
+       "displayName": "test1 testlatname1",
+       "self": true
+      },
+      "organizer": {
+       "email": "test1@google.com",
+       "displayName": "test1 testlatname1",
+       "self": true
+      },
+      "start": {
+       "dateTime": "2021-06-07T15:00:00-07:00"
+      },
+      "end": {
+       "dateTime": "2021-06-07T15:30:00-07:00"
+      },
+      "iCalUID": "7749obefodf44k4kt7r8lr0l8b@google.com",
+      "sequence": 1,
+      "attendees": [
+       {
+        "email": "test4@google.com",
+        "displayName": "test test4",
+        "responseStatus": "accepted"
+       },
+       {
+        "email": "test6@google.com",
+        "displayName": "test test6",
+        "responseStatus": "accepted"
+       },
+       {
+        "email": "test1@google.com",
+        "displayName": "test1 testlatname1",
+        "organizer": true,
+        "self": true,
+        "responseStatus": "accepted"
+       }
+      ],
+      "hangoutLink": "https://meet.google.com/dbp-qnpu-dtr",
+      "conferenceData": {
+       "entryPoints": [
+        {
+         "entryPointType": "video",
+         "uri": "https://meet.google.com/dbp-qnpu-dtr",
+         "label": "meet.google.com/dbp-qnpu-dtr"
+        },
+        {
+         "entryPointType": "more",
+         "uri": "https://tel.meet/dbp-qnpu-dtr?pin=9643865380295",
+         "pin": "9643865380295"
+        },
+        {
+         "regionCode": "US",
+         "entryPointType": "phone",
+         "uri": "tel:+1-505-445-7759",
+         "label": "+1 505-445-7759",
+         "pin": "537999715"
+        }
+       ],
+       "conferenceSolution": {
+        "key": {
+         "type": "hangoutsMeet"
+        },
+        "name": "Google Meet",
+        "iconUri": "https://fonts.gstatic.com/s/i/productlogos/meet_2020q4/v6/web-512dp/logo_meet_2020q4_color_2x_web_512dp.png"
+       },
+       "conferenceId": "dbp-qnpu-dtr",
+       "signature": "ACn9hYEoGEiIFbVraLJKk6nZMT8W"
+      },
+      "reminders": {
+       "useDefault": true
+      },
+      "eventType": "default"
+     },
+     {
+      "kind": "calendar#event",
+      "etag": "\"3246695702310000\"",
+      "id": "1a5n209tijjtrd1mhn2lkgdaq9",
+      "status": "confirmed",
+      "htmlLink": "https://www.google.com/calendar/event?eid=MWE1bjIwOXRpamp0cmQxbWhuMmxrZ2RhcTlfMjAyMTA1MTBUMTgzMDAwWiBqaWFtaW5nY0Bnb29nbGUuY29t",
+      "created": "2021-05-07T18:17:01.000Z",
+      "updated": "2021-06-10T17:57:31.155Z",
+      "summary": "System UI sync",
+      "creator": {
+       "email": "test5@google.com",
+       "displayName": "test test5"
+      },
+      "organizer": {
+       "email": "test5@google.com",
+       "displayName": "test test5"
+      },
+      "start": {
+       "dateTime": "2021-05-10T11:30:00-07:00",
+       "timeZone": "America/Los_Angeles"
+      },
+      "end": {
+       "dateTime": "2021-05-10T12:00:00-07:00",
+       "timeZone": "America/Los_Angeles"
+      },
+      "recurrence": [
+       "RRULE:FREQ=WEEKLY;UNTIL=20210614T065959Z;BYDAY=MO"
+      ],
+      "iCalUID": "1a5n209tijjtrd1mhn2lkgdaq9@google.com",
+      "sequence": 0,
+      "attendees": [
+       {
+        "email": "test4@google.com",
+        "responseStatus": "needsAction"
+       },
+       {
+        "email": "test1@google.com",
+        "displayName": "test1 testlatname1",
+        "self": true,
+        "responseStatus": "accepted"
+       }
+      ],
+      "hangoutLink": "https://meet.google.com/kid-test",
+      "conferenceData": {
+       "entryPoints": [
+        {
+         "entryPointType": "video",
+         "uri": "https://meet.google.com/kid-test",
+         "label": "meet.google.com/kid-test"
+        },
+        {
+         "entryPointType": "more",
+         "uri": "https://tel.meet/kid-test?pin=2281762872233",
+         "pin": "2281762872233"
+        },
+        {
+         "regionCode": "US",
+         "entryPointType": "phone",
+         "uri": "tel:+1-631-621-7845",
+         "label": "+1 631-621-7845",
+         "pin": "757210"
+        }
+       ],
+       "conferenceSolution": {
+        "key": {
+         "type": "hangoutsMeet"
+        },
+        "name": "Google Meet",
+        "iconUri": "https://fonts.gstatic.com/s/i/productlogos/meet_2020q4/v6/web-512dp/logo_meet_2020q4_color_2x_web_512dp.png"
+       },
+       "conferenceId": "kid-test",
+       "signature": "ACn9hYECv1wqAa2nRcODwiTjDSb9"
+      },
+      "reminders": {
+       "useDefault": true
+      },
+      "eventType": "default"
+     }
+    ]
+   }
\ No newline at end of file
diff --git a/google_apis/test/data/calendar/invalid_events.json b/google_apis/test/data/calendar/invalid_events.json
new file mode 100644
index 0000000..229968d
--- /dev/null
+++ b/google_apis/test/data/calendar/invalid_events.json
@@ -0,0 +1,8 @@
+{
+    "kind": [],
+    "etag": "\"p32ofplf5q6gf20g\"",
+    "summary": "test1@google.com",
+    "updated": "2021-06-18T07:17:10.718Z",
+    "timeZone": [],
+    "accessRole": "owner"
+}
\ No newline at end of file
diff --git a/gpu/command_buffer/service/external_vk_image_factory_unittest.cc b/gpu/command_buffer/service/external_vk_image_factory_unittest.cc
index e759d18a..422eee8 100644
--- a/gpu/command_buffer/service/external_vk_image_factory_unittest.cc
+++ b/gpu/command_buffer/service/external_vk_image_factory_unittest.cc
@@ -355,7 +355,6 @@
       dst_copy_view.buffer = dst_buffer;
       dst_copy_view.layout.bytesPerRow = 256;
       dst_copy_view.layout.offset = 0;
-      dst_copy_view.layout.rowsPerImage = 0;
 
       wgpu::Extent3D copy_extent = {static_cast<uint32_t>(size.width()),
                                     static_cast<uint32_t>(size.height()), 1};
diff --git a/gpu/command_buffer/tests/webgpu_mailbox_unittest.cc b/gpu/command_buffer/tests/webgpu_mailbox_unittest.cc
index a442e19..63a1033 100644
--- a/gpu/command_buffer/tests/webgpu_mailbox_unittest.cc
+++ b/gpu/command_buffer/tests/webgpu_mailbox_unittest.cc
@@ -308,7 +308,7 @@
 
     // Clear the texture using a render pass.
     wgpu::RenderPassColorAttachmentDescriptor color_desc = {};
-    color_desc.attachment = texture.CreateView();
+    color_desc.view = texture.CreateView();
     color_desc.loadOp = wgpu::LoadOp::Clear;
     color_desc.storeOp = wgpu::StoreOp::Store;
     color_desc.clearColor = {0, 255, 0, 255};
@@ -355,7 +355,6 @@
     copy_dst.buffer = readback_buffer;
     copy_dst.layout.offset = 0;
     copy_dst.layout.bytesPerRow = 256;
-    copy_dst.layout.rowsPerImage = 0;
 
     wgpu::Extent3D copy_size = {1, 1, 1};
 
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
index c8f3648..af982016 100644
--- a/infra/config/generated/commit-queue.cfg
+++ b/infra/config/generated/commit-queue.cfg
@@ -1689,6 +1689,14 @@
         includable_only: true
       }
       builders {
+        name: "chromium/try/win10-rel-compilator"
+        includable_only: true
+      }
+      builders {
+        name: "chromium/try/win10-rel-orchestrator"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/win10.20h2-blink-rel"
         includable_only: true
       }
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index ea0f04a..4e9fd617 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -61912,6 +61912,163 @@
       }
     }
     builders {
+      name: "win10-rel-compilator"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builder:win10-rel-compilator"
+      dimensions: "cores:16"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows-10"
+      dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:1"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        cmd: "luciexe"
+      }
+      properties: "{\"$build/goma\":{\"enable_ats\":false,\"jobs\":300,\"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\"},\"$recipe_engine/resultdb/test_presentation\":{\"column_keys\":[],\"grouping_keys\":[\"status\",\"v.test_suite\"]},\"builder_group\":\"tryserver.chromium.win\",\"orchestrator\":{\"builder_group\":\"tryserver.chromium.win\",\"builder_name\":\"win10-orchestrator\"},\"recipe\":\"chromium/compilator\"}"
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      caches {
+        name: "win_toolchain"
+        path: "win_toolchain"
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink"
+        value: 100
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink.junit_tests"
+        value: 100
+      }
+      experiments {
+        key: "luci.use_realms"
+        value: 100
+      }
+      experiments {
+        key: "use_rbe_cas"
+        value: 5
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
+      name: "win10-rel-orchestrator"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builder:win10-rel-orchestrator"
+      dimensions: "cores:2"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.try"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        cmd: "luciexe"
+      }
+      properties: "{\"$build/code_coverage\":{\"coverage_test_types\":[\"unit\",\"overall\"],\"use_clang_coverage\":true},\"$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\"},\"$recipe_engine/resultdb/test_presentation\":{\"column_keys\":[],\"grouping_keys\":[\"status\",\"v.test_suite\"]},\"builder_group\":\"tryserver.chromium.linux\",\"compilator\":\"win10-compilator\",\"recipe\":\"chromium/orchestrator\"}"
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      caches {
+        name: "win_toolchain"
+        path: "win_toolchain"
+      }
+      build_numbers: YES
+      service_account: "chromium-orchestrator@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink"
+        value: 100
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink.junit_tests"
+        value: 100
+      }
+      experiments {
+        key: "luci.use_realms"
+        value: 100
+      }
+      experiments {
+        key: "use_rbe_cas"
+        value: 5
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "win10.20h2-blink-rel"
       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 e5fa87f..db4d53eb 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -14341,6 +14341,12 @@
     name: "buildbucket/luci.chromium.try/win10-blink-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/win10-rel-compilator"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win10-rel-orchestrator"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/win10.20h2-blink-rel"
   }
   builders {
@@ -15134,6 +15140,9 @@
   builders {
     name: "buildbucket/luci.chromium.try/tricium-simple"
   }
+  builders {
+    name: "buildbucket/luci.chromium.try/win10-rel-orchestrator"
+  }
   builder_view_only: true
 }
 consoles {
@@ -15403,6 +15412,9 @@
     name: "buildbucket/luci.chromium.try/win-libfuzzer-asan-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/win10-rel-compilator"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/win10_chromium_inverse_fieldtrials_x64_fyi_rel_ng"
   }
   builders {
diff --git a/infra/config/generated/realms.cfg b/infra/config/generated/realms.cfg
index 25f4c5b..91c8ffe 100644
--- a/infra/config/generated/realms.cfg
+++ b/infra/config/generated/realms.cfg
@@ -252,6 +252,7 @@
     role: "role/buildbucket.builderServiceAccount"
     principals: "user:chromium-cipd-try-builder@chops-service-accounts.iam.gserviceaccount.com"
     principals: "user:chromium-mini-orchestrator@chops-service-accounts.iam.gserviceaccount.com"
+    principals: "user:chromium-orchestrator@chops-service-accounts.iam.gserviceaccount.com"
     principals: "user:chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
     principals: "user:chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
   }
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index 50cff18..ce06af13 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -1853,6 +1853,37 @@
     tryjob = try_.job(),
 )
 
+try_.chromium_linux_builder(
+    name = "win10-rel-orchestrator",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = False,
+    cores = 2,
+    executable = "recipe:chromium/orchestrator",
+    use_clang_coverage = True,
+    coverage_test_types = ["unit", "overall"],
+    properties = {
+        "compilator": "win10-compilator",
+    },
+    service_account = "chromium-orchestrator@chops-service-accounts.iam.gserviceaccount.com",
+)
+
+try_.chromium_win_builder(
+    name = "win10-rel-compilator",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = False,
+    cores = 16,
+    os = os.WINDOWS_10,
+    ssd = True,
+    goma_jobs = goma.jobs.J300,
+    executable = "recipe:chromium/compilator",
+    properties = {
+        "orchestrator": {
+            "builder_name": "win10-orchestrator",
+            "builder_group": "tryserver.chromium.win",
+        },
+    },
+)
+
 try_.chromium_win_builder(
     name = "win10_chromium_x64_rel_ng_exp",
     builderless = False,
diff --git a/ios/chrome/browser/favicon/favicon_client_impl.mm b/ios/chrome/browser/favicon/favicon_client_impl.mm
index 83031f7..7dc60c9 100644
--- a/ios/chrome/browser/favicon/favicon_client_impl.mm
+++ b/ios/chrome/browser/favicon/favicon_client_impl.mm
@@ -43,13 +43,14 @@
   if (resource_id == -1)
     return;
 
-  // Use ui::GetSupportedScaleFactors() because native URL favicon comes from
-  // resources.
-  std::vector<ui::ScaleFactor> scale_factors = ui::GetSupportedScaleFactors();
+  // Use ui::GetSupportedResourceScaleFactors() because native URL favicon comes
+  // from resources.
+  std::vector<ui::ResourceScaleFactor> scale_factors =
+      ui::GetSupportedResourceScaleFactors();
 
   std::vector<gfx::Size> candidate_sizes;
-  for (ui::ScaleFactor scale_factor : scale_factors) {
-    float scale = ui::GetScaleForScaleFactor(scale_factor);
+  for (ui::ResourceScaleFactor scale_factor : scale_factors) {
+    float scale = ui::GetScaleForResourceScaleFactor(scale_factor);
     int candidate_size = static_cast<int>(gfx::kFaviconSize * scale + 0.5f);
     candidate_sizes.push_back(gfx::Size(candidate_size, candidate_size));
   }
@@ -59,7 +60,7 @@
                             &selected_indices, nullptr);
 
   for (size_t selected_index : selected_indices) {
-    ui::ScaleFactor scale_factor = scale_factors[selected_index];
+    ui::ResourceScaleFactor scale_factor = scale_factors[selected_index];
     favicon_base::FaviconRawBitmapResult favicon_bitmap;
     favicon_bitmap.icon_type = favicon_base::IconType::kFavicon;
     favicon_bitmap.pixel_size = candidate_sizes[selected_index];
diff --git a/ios/chrome/browser/ntp_tiles/ios_most_visited_sites_factory.cc b/ios/chrome/browser/ntp_tiles/ios_most_visited_sites_factory.cc
index 46f89893..92443d1e 100644
--- a/ios/chrome/browser/ntp_tiles/ios_most_visited_sites_factory.cc
+++ b/ios/chrome/browser/ntp_tiles/ios_most_visited_sites_factory.cc
@@ -16,7 +16,6 @@
 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
 #include "ios/chrome/browser/history/top_sites_factory.h"
 #include "ios/chrome/browser/ntp_tiles/ios_popular_sites_factory.h"
-#include "ios/chrome/browser/suggestions/suggestions_service_factory.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 std::unique_ptr<ntp_tiles::MostVisitedSites>
@@ -25,7 +24,6 @@
   return std::make_unique<ntp_tiles::MostVisitedSites>(
       browser_state->GetPrefs(),
       ios::TopSitesFactory::GetForBrowserState(browser_state),
-      suggestions::SuggestionsServiceFactory::GetForBrowserState(browser_state),
       IOSPopularSitesFactory::NewForBrowserState(browser_state),
       /*custom_links=*/nullptr,
       std::make_unique<ntp_tiles::IconCacherImpl>(
diff --git a/ios/chrome/browser/policy/BUILD.gn b/ios/chrome/browser/policy/BUILD.gn
index 0e5d29d..6f4f0adc 100644
--- a/ios/chrome/browser/policy/BUILD.gn
+++ b/ios/chrome/browser/policy/BUILD.gn
@@ -279,6 +279,9 @@
     "//components/policy/core/browser",
     "//components/policy/core/common",
     "//ios/chrome/browser",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/policy_url_blocking",
+    "//ios/chrome/test/app:test_support",
   ]
   frameworks = [ "Foundation.framework" ]
 }
diff --git a/ios/chrome/browser/policy/policy_app_interface.h b/ios/chrome/browser/policy/policy_app_interface.h
index 400d6b9..28a3d46a 100644
--- a/ios/chrome/browser/policy/policy_app_interface.h
+++ b/ios/chrome/browser/policy/policy_app_interface.h
@@ -21,6 +21,10 @@
 // Clear all policy values.
 + (void)clearPolicies;
 
+// Returns YES if the given |URL| is blocked by the URLBlocklist and
+// URLAllowlist policies.
++ (BOOL)isURLBlocked:(NSString*)URL;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_POLICY_POLICY_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/policy/policy_app_interface.mm b/ios/chrome/browser/policy/policy_app_interface.mm
index 352af2c..6a78104 100644
--- a/ios/chrome/browser/policy/policy_app_interface.mm
+++ b/ios/chrome/browser/policy/policy_app_interface.mm
@@ -10,6 +10,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/values.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/browser/url_blocklist_manager.h"
 #include "components/policy/core/common/configuration_policy_provider.h"
 #include "components/policy/core/common/policy_bundle.h"
 #include "components/policy/core/common/policy_map.h"
@@ -17,8 +18,11 @@
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/policy_constants.h"
 #include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/policy/browser_policy_connector_ios.h"
 #include "ios/chrome/browser/policy/test_platform_policy_provider.h"
+#import "ios/chrome/browser/policy_url_blocking/policy_url_blocking_service.h"
+#import "ios/chrome/test/app/chrome_test_util.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -99,4 +103,13 @@
   GetTestPlatformPolicyProvider()->UpdateChromePolicy(values);
 }
 
++ (BOOL)isURLBlocked:(NSString*)URL {
+  GURL gurl = GURL(base::SysNSStringToUTF8(URL));
+  PolicyBlocklistService* service =
+      PolicyBlocklistServiceFactory::GetForBrowserState(
+          chrome_test_util::GetOriginalBrowserState());
+  return service->GetURLBlocklistState(gurl) ==
+         policy::URLBlocklist::URLBlocklistState::URL_IN_BLOCKLIST;
+}
+
 @end
diff --git a/ios/chrome/browser/policy_url_blocking/policy_url_blocking_egtest.mm b/ios/chrome/browser/policy_url_blocking/policy_url_blocking_egtest.mm
index 0de4a84..6f5594b 100644
--- a/ios/chrome/browser/policy_url_blocking/policy_url_blocking_egtest.mm
+++ b/ios/chrome/browser/policy_url_blocking/policy_url_blocking_egtest.mm
@@ -5,6 +5,7 @@
 #include <string>
 
 #include "base/strings/sys_string_conversions.h"
+#import "base/test/ios/wait_util.h"
 #include "components/policy/policy_constants.h"
 #import "ios/chrome/browser/chrome_switches.h"
 #import "ios/chrome/browser/policy/policy_app_interface.h"
@@ -21,6 +22,22 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+
+// Waits until |url| has the expected blocked state.
+void WaitForURLBlockedStatus(const GURL& url, bool blocked) {
+  NSString* nsurl = base::SysUTF8ToNSString(url.spec());
+  GREYAssertTrue(base::test::ios::WaitUntilConditionOrTimeout(
+                     base::test::ios::kWaitForActionTimeout,
+                     ^{
+                       return
+                           [PolicyAppInterface isURLBlocked:nsurl] == blocked;
+                     }),
+                 @"Waiting for policy url blocklist to update.");
+}
+
+}
+
 // Tests the URLBlocklist and URLWhitelist enterprise policies.
 @interface PolicyURLBlockingTestCase : ChromeTestCase
 @end
@@ -45,6 +62,15 @@
 - (void)setUp {
   [super setUp];
   GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
+
+  // Check that the policy blocklist is reset.
+  WaitForURLBlockedStatus(self.testServer->GetURL("/echo"), false);
+  WaitForURLBlockedStatus(self.testServer->GetURL("/testpage"), false);
+}
+
+- (void)tearDown {
+  [PolicyAppInterface clearPolicies];
+  [super tearDown];
 }
 
 // Tests that pages are not blocked when the blocklist exists, but is empty.
@@ -64,6 +90,7 @@
   [PolicyAppInterface
       setPolicyValue:@"[\"*\"]"
               forKey:base::SysUTF8ToNSString(policy::key::kURLBlocklist)];
+  WaitForURLBlockedStatus(self.testServer->GetURL("/echo"), true);
 
   [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo")];
 
@@ -77,6 +104,7 @@
   [PolicyAppInterface
       setPolicyValue:@"[\"*\"]"
               forKey:base::SysUTF8ToNSString(policy::key::kURLBlocklist)];
+  WaitForURLBlockedStatus(self.testServer->GetURL("/echo"), true);
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
       assertWithMatcher:grey_sufficientlyVisible()];
@@ -88,6 +116,7 @@
   [PolicyAppInterface
       setPolicyValue:@"[\"*/echo\"]"
               forKey:base::SysUTF8ToNSString(policy::key::kURLBlocklist)];
+  WaitForURLBlockedStatus(self.testServer->GetURL("/echo"), true);
 
   [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo")];
 
@@ -98,12 +127,18 @@
 
 // Tests that pages are loaded when explicitly listed in the URLAllowlist.
 - (void)testAllowlist {
+  // The URLBlocklistPolicyHandler will discard policy updates that occur while
+  // it is already computing a new blocklist, so wait between calls to set new
+  // policy values.
   [PolicyAppInterface
       setPolicyValue:@"[\"*\"]"
               forKey:base::SysUTF8ToNSString(policy::key::kURLBlocklist)];
+  WaitForURLBlockedStatus(self.testServer->GetURL("/testpage"), true);
+
   [PolicyAppInterface
       setPolicyValue:@"[\"*/echo\"]"
               forKey:base::SysUTF8ToNSString(policy::key::kURLAllowlist)];
+  WaitForURLBlockedStatus(self.testServer->GetURL("/echo"), false);
 
   [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo")];
 
diff --git a/ios/chrome/browser/sync/ios_trusted_vault_client.mm b/ios/chrome/browser/sync/ios_trusted_vault_client.mm
index e055719..ac97a02b 100644
--- a/ios/chrome/browser/sync/ios_trusted_vault_client.mm
+++ b/ios/chrome/browser/sync/ios_trusted_vault_client.mm
@@ -50,10 +50,8 @@
 
   ios::ChromeBrowserProvider* browser_provider =
       ios::GetChromeBrowserProvider();
-  ios::ChromeTrustedVaultService* trusted_vault_service =
-      browser_provider->GetChromeTrustedVaultService();
-  DCHECK(trusted_vault_service);
-  trusted_vault_service->FetchKeys(identity, std::move(callback));
+  browser_provider->GetChromeTrustedVaultService()->FetchKeys(
+      identity, std::move(callback));
 }
 
 void IOSTrustedVaultClient::StoreKeys(
@@ -67,8 +65,13 @@
 void IOSTrustedVaultClient::MarkKeysAsStale(
     const CoreAccountInfo& account_info,
     base::OnceCallback<void(bool)> callback) {
-  // TODO(crbug.com/1100278): Needs implementation.
-  std::move(callback).Run(false);
+  ChromeIdentity* identity =
+      account_manager_service_->GetIdentityWithGaiaID(account_info.gaia);
+
+  ios::ChromeBrowserProvider* browser_provider =
+      ios::GetChromeBrowserProvider();
+  browser_provider->GetChromeTrustedVaultService()->MarkLocalKeysAsStale(
+      identity, std::move(callback));
 }
 
 void IOSTrustedVaultClient::GetIsRecoverabilityDegraded(
@@ -87,6 +90,6 @@
     const std::vector<uint8_t>& public_key,
     int method_type_hint,
     base::OnceClosure callback) {
-  // TODO(crbug.com/1100278): Needs implementation.
-  std::move(callback).Run();
+  // Not used on iOS.
+  NOTREACHED();
 }
diff --git a/ios/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc b/ios/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
index 5207b8a4..c8d22f9 100644
--- a/ios/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
+++ b/ios/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
@@ -68,7 +68,6 @@
     ntp_tiles::TileSource source) {
   switch (source) {
     case ntp_tiles::TileSource::TOP_SITES:
-    case ntp_tiles::TileSource::SUGGESTIONS_SERVICE:
     case ntp_tiles::TileSource::POPULAR:
     case ntp_tiles::TileSource::POPULAR_BAKED_IN:
     case ntp_tiles::TileSource::HOMEPAGE:
diff --git a/ios/chrome/browser/web/chrome_web_client.h b/ios/chrome/browser/web/chrome_web_client.h
index 57816f7..0ea7cc19 100644
--- a/ios/chrome/browser/web/chrome_web_client.h
+++ b/ios/chrome/browser/web/chrome_web_client.h
@@ -29,7 +29,7 @@
   std::u16string GetLocalizedString(int message_id) const override;
   base::StringPiece GetDataResource(
       int resource_id,
-      ui::ScaleFactor scale_factor) const override;
+      ui::ResourceScaleFactor scale_factor) const override;
   base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override;
   void GetAdditionalWebUISchemes(
       std::vector<std::string>* additional_schemes) override;
diff --git a/ios/chrome/browser/web/chrome_web_client.mm b/ios/chrome/browser/web/chrome_web_client.mm
index 379705c5..70b8ce0962 100644
--- a/ios/chrome/browser/web/chrome_web_client.mm
+++ b/ios/chrome/browser/web/chrome_web_client.mm
@@ -267,7 +267,7 @@
 
 base::StringPiece ChromeWebClient::GetDataResource(
     int resource_id,
-    ui::ScaleFactor scale_factor) const {
+    ui::ResourceScaleFactor scale_factor) const {
   return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
       resource_id, scale_factor);
 }
diff --git a/ios/chrome/browser/web/error_page_util.mm b/ios/chrome/browser/web/error_page_util.mm
index b4015e9..9ea56c5c 100644
--- a/ios/chrome/browser/web/error_page_util.mm
+++ b/ios/chrome/browser/web/error_page_util.mm
@@ -59,7 +59,7 @@
           GetApplicationContext()->GetApplicationLocale(),
           /*is_blocked_by_extension=*/false);
 
-  ui::ScaleFactor scale_factor =
+  ui::ResourceScaleFactor scale_factor =
       ui::ResourceBundle::GetSharedInstance().GetMaxScaleFactor();
 
   std::string extracted_string =
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index 17e663c..35ecfcf9 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-7ed61a58f40f945dbb74c00a79bf03faaf7af52b
\ No newline at end of file
+1ca9c38a2c875423f580a9682e305b4109fa392d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index 5e2563c..3d643642 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-5823d62ba8ba4ef6a4060bbea629e5ab61720547
\ No newline at end of file
+68b5257e494bd125f3150e9a572ff98f53f77b54
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
index 921cff6..462de5b 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-a5b6c422bb7aed25f522081fd7d9bd07698b4f59
\ No newline at end of file
+0699d15b2a47348f55728db8cac322749fb64511
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
index 0991f09..994f711 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-8f38d3b36675af5ef20a5007abbf31218808d0d9
\ No newline at end of file
+25059d05998ec8fc50c67c0062419ecd9dfb6ca6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index 42e8b37..d4f15f9 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-3679a77fb019998b103932756f12af49ed0f3554
\ No newline at end of file
+7b98f81cda420d754cee55bbec6695610d1693b4
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index 57b54a16..581b62b 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-a31816740e900542b0d038e0dae2e3e83293d935
\ No newline at end of file
+bc5b701a9bd198c0ea9ae73589e9d38c0293bc51
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index 9399f65b..1b52fd95 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-62c91bb98f7a96a08ed6d07de64a8d520e67431c
\ No newline at end of file
+0108cad53273467da40ac6a64c03b8386cd9889a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index 5efda37..32987f0 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-1c57f20e633c98d14efa6bbb773c1e753d192d87
\ No newline at end of file
+6ce178db7d7ed99625da2fcfc1957d719f556f86
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index ae2edf6..df6fdbf 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-d922439466cccfa2be595a8f17c6cf27770c3374
\ No newline at end of file
+8a6cc02d8cb925a2492749e9aa7e79dd237389ff
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index 126e59e9..b4f51085 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-78751e21d0d5586c383e75a4f6fa6b755a4b9200
\ No newline at end of file
+866ae9097d2c05236a24272b4d4450c8d4c7e493
\ No newline at end of file
diff --git a/ios/public/provider/chrome/browser/signin/chrome_trusted_vault_service.h b/ios/public/provider/chrome/browser/signin/chrome_trusted_vault_service.h
index 85a9611..ef806f1 100644
--- a/ios/public/provider/chrome/browser/signin/chrome_trusted_vault_service.h
+++ b/ios/public/provider/chrome/browser/signin/chrome_trusted_vault_service.h
@@ -41,6 +41,18 @@
       ChromeIdentity* chrome_identity,
       base::OnceCallback<void(const TrustedVaultSharedKeyList&)> callback) = 0;
 
+  // Invoked when the result of FetchKeys() contains keys that are not
+  // up-to-date. |cb| is run upon completion and returns false if the call did
+  // not make any difference (e.g. the operation is unsupported) or true if
+  // some change may have occurred (which indicates a second FetchKeys() attempt
+  // is worth). During the execution, before |cb| is invoked, the behavior is
+  // unspecified if FetchKeys() is invoked, that is, FetchKeys() may or may not
+  // treat existing keys as stale (only guaranteed upon completion of
+  // MarkLocalKeysAsStale()).
+  // TODO(crbug.com/1100278): Make pure virtual.
+  virtual void MarkLocalKeysAsStale(ChromeIdentity* chrome_identity,
+                                    base::OnceCallback<void(bool)> callback);
+
   // Returns whether recoverability of the keys is degraded and user action is
   // required to add a new method.
   virtual void GetDegradedRecoverabilityStatus(
diff --git a/ios/public/provider/chrome/browser/signin/chrome_trusted_vault_service.mm b/ios/public/provider/chrome/browser/signin/chrome_trusted_vault_service.mm
index 38a7004..d60436e 100644
--- a/ios/public/provider/chrome/browser/signin/chrome_trusted_vault_service.mm
+++ b/ios/public/provider/chrome/browser/signin/chrome_trusted_vault_service.mm
@@ -22,6 +22,12 @@
   observer_list_.RemoveObserver(observer);
 }
 
+void ChromeTrustedVaultService::MarkLocalKeysAsStale(
+    ChromeIdentity* chrome_identity,
+    base::OnceCallback<void(bool)> callback) {
+  std::move(callback).Run(false);
+}
+
 void ChromeTrustedVaultService::Reauthentication(
     ChromeIdentity* chrome_identity,
     UIViewController* presentingViewController,
diff --git a/ios/third_party/fishhook/fishhook.c b/ios/third_party/fishhook/fishhook.c
index 22cd1e3..3ae6355 100644
--- a/ios/third_party/fishhook/fishhook.c
+++ b/ios/third_party/fishhook/fishhook.c
@@ -26,6 +26,7 @@
 #include <dlfcn.h>
 #include <stdbool.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 #include <sys/mman.h>
 #include <sys/types.h>
@@ -126,7 +127,17 @@
     trunc_size =(vm_size_t)indirect_symbol_bindings -trunc_address;
     pthread_mutex_lock(&mutex);
     oldProtection = get_protection((void *)trunc_address);
-    mprotect((void *)trunc_address, section->size+trunc_size, PROT_READ | PROT_WRITE);
+
+    // Use vm_protect to also set VM_PROT_COPY.
+    kern_return_t err = vm_protect(mach_task_self(),
+                                  (uintptr_t)trunc_address,
+                                   section->size+trunc_size,
+                                  0,
+                                  VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY);
+    if (err != KERN_SUCCESS) {
+      fprintf(stderr, "perform_rebinding_with_section vm_protect failed.\n");
+      return;
+    }
   }
   for (uint i = 0; i < section->size / sizeof(void *); i++) {
     uint32_t symtab_index = indirect_symbol_indices[i];
diff --git a/ios/web/public/web_client.h b/ios/web/public/web_client.h
index f8148ee7f..b6545328 100644
--- a/ios/web/public/web_client.h
+++ b/ios/web/public/web_client.h
@@ -94,8 +94,9 @@
   virtual std::u16string GetLocalizedString(int message_id) const;
 
   // Returns the contents of a resource in a StringPiece given the resource id.
-  virtual base::StringPiece GetDataResource(int resource_id,
-                                            ui::ScaleFactor scale_factor) const;
+  virtual base::StringPiece GetDataResource(
+      int resource_id,
+      ui::ResourceScaleFactor scale_factor) const;
 
   // Returns the raw bytes of a scale independent data resource.
   virtual base::RefCountedMemory* GetDataResourceBytes(int resource_id) const;
diff --git a/ios/web/shell/shell_web_client.h b/ios/web/shell/shell_web_client.h
index 21d60f2..994f7c06 100644
--- a/ios/web/shell/shell_web_client.h
+++ b/ios/web/shell/shell_web_client.h
@@ -25,7 +25,7 @@
   std::string GetUserAgent(UserAgentType type) const override;
   base::StringPiece GetDataResource(
       int resource_id,
-      ui::ScaleFactor scale_factor) const override;
+      ui::ResourceScaleFactor scale_factor) const override;
   base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override;
   void BindInterfaceReceiverFromMainFrame(
       WebState* web_state,
diff --git a/ios/web/shell/shell_web_client.mm b/ios/web/shell/shell_web_client.mm
index bdd4ce13..f4a0271e 100644
--- a/ios/web/shell/shell_web_client.mm
+++ b/ios/web/shell/shell_web_client.mm
@@ -63,7 +63,7 @@
 
 base::StringPiece ShellWebClient::GetDataResource(
     int resource_id,
-    ui::ScaleFactor scale_factor) const {
+    ui::ResourceScaleFactor scale_factor) const {
   return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
       resource_id, scale_factor);
 }
diff --git a/ios/web/web_client.mm b/ios/web/web_client.mm
index 1d3d655..ad9e40b 100644
--- a/ios/web/web_client.mm
+++ b/ios/web/web_client.mm
@@ -58,7 +58,7 @@
 
 base::StringPiece WebClient::GetDataResource(
     int resource_id,
-    ui::ScaleFactor scale_factor) const {
+    ui::ResourceScaleFactor scale_factor) const {
   return base::StringPiece();
 }
 
diff --git a/ios/web_view/internal/web_view_web_client.h b/ios/web_view/internal/web_view_web_client.h
index 8ed0669..652530a0 100644
--- a/ios/web_view/internal/web_view_web_client.h
+++ b/ios/web_view/internal/web_view_web_client.h
@@ -25,7 +25,7 @@
   std::string GetUserAgent(web::UserAgentType type) const override;
   base::StringPiece GetDataResource(
       int resource_id,
-      ui::ScaleFactor scale_factor) const override;
+      ui::ResourceScaleFactor scale_factor) const override;
   base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override;
   std::vector<web::JavaScriptFeature*> GetJavaScriptFeatures(
       web::BrowserState* browser_state) const override;
diff --git a/ios/web_view/internal/web_view_web_client.mm b/ios/web_view/internal/web_view_web_client.mm
index d33754e..1f12a8e6 100644
--- a/ios/web_view/internal/web_view_web_client.mm
+++ b/ios/web_view/internal/web_view_web_client.mm
@@ -90,7 +90,7 @@
 
 base::StringPiece WebViewWebClient::GetDataResource(
     int resource_id,
-    ui::ScaleFactor scale_factor) const {
+    ui::ResourceScaleFactor scale_factor) const {
   return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
       resource_id, scale_factor);
 }
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index e5012b7..b72315cb 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -1479,18 +1479,17 @@
 bool VaapiWrapper::IsDecodingSupportedForInternalFormat(
     VAProfile va_profile,
     unsigned int rt_format) {
-  static const base::NoDestructor<VaapiWrapper::InternalFormats>
-      supported_internal_formats(
-          VaapiWrapper::GetDecodeSupportedInternalFormats(va_profile));
+  static const VaapiWrapper::InternalFormats supported_internal_formats(
+      VaapiWrapper::GetDecodeSupportedInternalFormats(va_profile));
   switch (rt_format) {
     case VA_RT_FORMAT_YUV420:
-      return supported_internal_formats->yuv420;
+      return supported_internal_formats.yuv420;
     case VA_RT_FORMAT_YUV420_10:
-      return supported_internal_formats->yuv420_10;
+      return supported_internal_formats.yuv420_10;
     case VA_RT_FORMAT_YUV422:
-      return supported_internal_formats->yuv422;
+      return supported_internal_formats.yuv422;
     case VA_RT_FORMAT_YUV444:
-      return supported_internal_formats->yuv444;
+      return supported_internal_formats.yuv444;
   }
   return false;
 }
diff --git a/media/mojo/clients/mojo_video_decoder.cc b/media/mojo/clients/mojo_video_decoder.cc
index 0112cb3..2b79916 100644
--- a/media/mojo/clients/mojo_video_decoder.cc
+++ b/media/mojo/clients/mojo_video_decoder.cc
@@ -14,7 +14,6 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/no_destructor.h"
 #include "base/sequenced_task_runner.h"
 #include "base/unguessable_token.h"
 #include "build/build_config.h"
@@ -39,8 +38,8 @@
 namespace {
 // Number of functional instances of MojoVideoDecoder in the current process.
 std::atomic<int>& get_mojo_instance_counter() {
-  static base::NoDestructor<std::atomic<int>> gInstanceCounter(0);
-  return *gInstanceCounter;
+  static std::atomic<int> instance_counter(0);
+  return instance_counter;
 }
 }  // namespace
 
diff --git a/media/video/fake_gpu_memory_buffer.cc b/media/video/fake_gpu_memory_buffer.cc
index 8722397..fbaae57 100644
--- a/media/video/fake_gpu_memory_buffer.cc
+++ b/media/video/fake_gpu_memory_buffer.cc
@@ -75,8 +75,8 @@
 
   handle_.type = gfx::NATIVE_PIXMAP;
 
-  static base::NoDestructor<base::AtomicSequenceNumber> buffer_id_generator;
-  handle_.id = gfx::GpuMemoryBufferId(buffer_id_generator->GetNext());
+  static base::AtomicSequenceNumber buffer_id_generator;
+  handle_.id = gfx::GpuMemoryBufferId(buffer_id_generator.GetNext());
 
 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
   for (size_t i = 0; i < VideoFrame::NumPlanes(video_pixel_format_); i++) {
diff --git a/mojo/core/mojo_core.cc b/mojo/core/mojo_core.cc
index 5f22533..bcbe1210 100644
--- a/mojo/core/mojo_core.cc
+++ b/mojo/core/mojo_core.cc
@@ -13,7 +13,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/message_loop/message_pump_type.h"
-#include "base/no_destructor.h"
 #include "base/rand_util.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
@@ -146,9 +145,9 @@
     argv = options->argv;
   }
 
-  static base::NoDestructor<GlobalStateInitializer> global_state_initializer;
+  static GlobalStateInitializer global_state_initializer;
   const bool was_global_state_already_initialized =
-      !global_state_initializer->Initialize(argc, argv);
+      !global_state_initializer.Initialize(argc, argv);
 
   if (!should_initialize_ipc_support) {
     if (was_global_state_already_initialized)
diff --git a/mojo/core/port_event_fuzzer.cc b/mojo/core/port_event_fuzzer.cc
index 8c03adaf..6d91eea 100644
--- a/mojo/core/port_event_fuzzer.cc
+++ b/mojo/core/port_event_fuzzer.cc
@@ -5,7 +5,6 @@
 #include <stdint.h>
 
 #include "base/containers/span.h"
-#include "base/no_destructor.h"
 #include "mojo/core/entrypoints.h"
 #include "mojo/core/node_controller.h"
 
@@ -16,7 +15,7 @@
 };
 
 extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
-  static base::NoDestructor<Environment> environment;
+  static Environment environment;
 
   // Try using the fuzz as the full contents of a port event.
   mojo::core::NodeController::DeserializeRawBytesAsEventForFuzzer(
diff --git a/mojo/core/user_message_fuzzer.cc b/mojo/core/user_message_fuzzer.cc
index 6b6091d..7365d2d 100644
--- a/mojo/core/user_message_fuzzer.cc
+++ b/mojo/core/user_message_fuzzer.cc
@@ -5,7 +5,6 @@
 #include <stdint.h>
 
 #include "base/containers/span.h"
-#include "base/no_destructor.h"
 #include "mojo/core/entrypoints.h"
 #include "mojo/core/node_controller.h"
 #include "mojo/core/user_message_impl.h"
@@ -17,7 +16,7 @@
 };
 
 extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
-  static base::NoDestructor<Environment> environment;
+  static Environment environment;
 
   // Try using our fuzz input as the payload of an otherwise well-formed user
   // message event.
diff --git a/mojo/public/c/system/thunks.cc b/mojo/public/c/system/thunks.cc
index eb2cd668..d7dc6e3 100644
--- a/mojo/public/c/system/thunks.cc
+++ b/mojo/public/c/system/thunks.cc
@@ -142,7 +142,9 @@
 extern "C" {
 
 MojoResult MojoInitialize(const struct MojoInitializeOptions* options) {
-  static base::NoDestructor<mojo::CoreLibraryInitializer> initializer;
+  static base::NoDestructor<mojo::CoreLibraryInitializer,
+                            base::AllowForTriviallyDestructibleType>
+      initializer;
 
   base::StringPiece library_path_utf8;
   if (options) {
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
index e2eafb9..92a9756 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -16,7 +16,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/no_destructor.h"
 #include "base/rand_util.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
@@ -105,10 +104,9 @@
     // The NestingObserver for each thread. Note that this is always a
     // Connector::RunLoopNestingObserver; we use the base type here because that
     // subclass is private to Connector.
-    static base::NoDestructor<
-        base::SequenceLocalStorageSlot<RunLoopNestingObserver>>
+    static base::SequenceLocalStorageSlot<RunLoopNestingObserver>
         sls_nesting_observer;
-    return &sls_nesting_observer->GetOrCreateValue();
+    return &sls_nesting_observer.GetOrCreateValue();
   }
 
  private:
diff --git a/mojo/public/cpp/bindings/lib/sequence_local_sync_event_watcher.cc b/mojo/public/cpp/bindings/lib/sequence_local_sync_event_watcher.cc
index 1d91787..03ff19e3 100644
--- a/mojo/public/cpp/bindings/lib/sequence_local_sync_event_watcher.cc
+++ b/mojo/public/cpp/bindings/lib/sequence_local_sync_event_watcher.cc
@@ -14,7 +14,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "base/no_destructor.h"
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/sequence_local_storage_slot.h"
@@ -173,8 +172,8 @@
  private:
   using StorageSlotType = base::SequenceLocalStorageSlot<SequenceLocalState>;
   static StorageSlotType& GetStorageSlot() {
-    static base::NoDestructor<StorageSlotType> storage;
-    return *storage;
+    static StorageSlotType storage;
+    return storage;
   }
 
   void OnEventSignaled();
diff --git a/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc b/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc
index b6cb0a7..45103af 100644
--- a/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc
+++ b/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc
@@ -47,8 +47,8 @@
 }
 
 size_t& GetSequenceLocalScopedAllowCount() {
-  static base::NoDestructor<base::SequenceLocalStorageSlot<size_t>> count;
-  return count->GetOrCreateValue();
+  static base::SequenceLocalStorageSlot<size_t> count;
+  return count.GetOrCreateValue();
 }
 
 // Sometimes sync calls need to be made while sequence-local storage is not
diff --git a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
index 512bece..ffb49ca7 100644
--- a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
+++ b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
@@ -35,8 +35,7 @@
 
 // static
 scoped_refptr<SyncHandleRegistry> SyncHandleRegistry::current() {
-  static base::NoDestructor<
-      base::SequenceLocalStorageSlot<scoped_refptr<SyncHandleRegistry>>>
+  static base::SequenceLocalStorageSlot<scoped_refptr<SyncHandleRegistry>>
       g_current_sync_handle_watcher;
 
   // SyncMessageFilter can be used on threads without sequence-local storage
@@ -46,12 +45,12 @@
         base::PassKey<SyncHandleRegistry>());
   }
 
-  if (!*g_current_sync_handle_watcher) {
-    g_current_sync_handle_watcher->emplace(
+  if (!g_current_sync_handle_watcher) {
+    g_current_sync_handle_watcher.emplace(
         base::MakeRefCounted<SyncHandleRegistry>(
             base::PassKey<SyncHandleRegistry>()));
   }
-  return *g_current_sync_handle_watcher->GetValuePointer();
+  return *g_current_sync_handle_watcher.GetValuePointer();
 }
 
 SyncHandleRegistry::SyncHandleRegistry(base::PassKey<SyncHandleRegistry>) {}
diff --git a/net/base/host_mapping_rules.cc b/net/base/host_mapping_rules.cc
index a10be8d..4ec3cc09 100644
--- a/net/base/host_mapping_rules.cc
+++ b/net/base/host_mapping_rules.cc
@@ -73,7 +73,7 @@
   return false;
 }
 
-bool HostMappingRules::RewriteUrl(GURL& url) const {
+HostMappingRules::RewriteResult HostMappingRules::RewriteUrl(GURL& url) const {
   // Must be a valid and standard URL. Otherwise, Chrome might not know how to
   // find/replace the contained host or port.
   DCHECK(url.is_valid());
@@ -82,17 +82,17 @@
 
   HostPortPair host_port_pair = HostPortPair::FromURL(url);
   if (!RewriteHost(&host_port_pair))
-    return false;
+    return RewriteResult::kNoMatchingRule;
 
   url::Replacements<char> replacements;
   std::string port_str = base::NumberToString(host_port_pair.port());
   replacements.SetPort(port_str.c_str(), url::Component(0, port_str.size()));
-  replacements.SetHost(host_port_pair.host().c_str(),
-                       url::Component(0, host_port_pair.host().size()));
+  std::string host_str = host_port_pair.HostForURL();
+  replacements.SetHost(host_str.c_str(), url::Component(0, host_str.size()));
   GURL new_url = url.ReplaceComponents(replacements);
 
   if (!new_url.is_valid())
-    return false;
+    return RewriteResult::kInvalidRewrite;
 
   DCHECK(new_url.IsStandard());
   DCHECK(new_url.has_host());
@@ -100,7 +100,7 @@
             new_url.EffectiveIntPort() == url::PORT_UNSPECIFIED);
 
   url = std::move(new_url);
-  return true;
+  return RewriteResult::kRewritten;
 }
 
 bool HostMappingRules::AddRuleFromString(base::StringPiece rule_string) {
diff --git a/net/base/host_mapping_rules.h b/net/base/host_mapping_rules.h
index 738219ef..a8d7f95 100644
--- a/net/base/host_mapping_rules.h
+++ b/net/base/host_mapping_rules.h
@@ -19,6 +19,12 @@
 
 class NET_EXPORT_PRIVATE HostMappingRules {
  public:
+  enum class RewriteResult {
+    kRewritten,
+    kNoMatchingRule,
+    kInvalidRewrite,
+  };
+
   HostMappingRules();
   HostMappingRules(const HostMappingRules& host_mapping_rules);
   ~HostMappingRules();
@@ -29,11 +35,15 @@
   // `*host_port` was modified, false otherwise.
   bool RewriteHost(HostPortPair* host_port) const;
 
-  // Modifies the host and port of `url` based on current rules. Returns true if
-  // `url` was modified, false otherwise. May only be called for URLs with a
-  // host and a scheme that is standard, and if the scheme does not allow ports,
-  // only the host will be rewritten.
-  bool RewriteUrl(GURL& url) const;
+  // Modifies the host and port of `url` based on current rules. May only be
+  // called for URLs with a host and a scheme that is standard, and if the
+  // scheme does not allow ports, only the host will be rewritten.
+  //
+  // If `url` is rewritten, returns `kRewritten`. If no matching rule is found,
+  // returns `kNoMatchingRule` and `url` is not modified. If a matching rule is
+  // found but it results in an invalid URL, e.g. if the rule maps to
+  // "~NOTFOUND", returns `kInvalidRewrite` and `url` is not modified.
+  RewriteResult RewriteUrl(GURL& url) const;
 
   // Adds a rule to this mapper. The format of the rule can be one of:
   //
diff --git a/net/base/host_mapping_rules_unittest.cc b/net/base/host_mapping_rules_unittest.cc
index 272a42b..8226858 100644
--- a/net/base/host_mapping_rules_unittest.cc
+++ b/net/base/host_mapping_rules_unittest.cc
@@ -92,16 +92,25 @@
   rules.AddRuleFromString("MAP initial.test replacement.test:1000");
 
   GURL url("http://initial.test:111");
-  EXPECT_TRUE(rules.RewriteUrl(url));
+  EXPECT_EQ(rules.RewriteUrl(url), HostMappingRules::RewriteResult::kRewritten);
   EXPECT_EQ(url, GURL("http://replacement.test:1000"));
 }
 
+TEST(HostMappingRulesTest, RewritesUrlToIpv6Literal) {
+  HostMappingRules rules;
+  rules.AddRuleFromString("MAP initial.test [2345:6789::0abc]:1112");
+
+  GURL url("http://initial.test:111");
+  EXPECT_EQ(rules.RewriteUrl(url), HostMappingRules::RewriteResult::kRewritten);
+  EXPECT_EQ(url, GURL("http://[2345:6789::0abc]:1112"));
+}
+
 TEST(HostMappingRulesTest, RewritesUrlPreservingScheme) {
   HostMappingRules rules;
   rules.AddRuleFromString("MAP initial.test replacement.test:1000");
 
   GURL url("wss://initial.test:222");
-  EXPECT_TRUE(rules.RewriteUrl(url));
+  EXPECT_EQ(rules.RewriteUrl(url), HostMappingRules::RewriteResult::kRewritten);
   EXPECT_EQ(url, GURL("wss://replacement.test:1000"));
 }
 
@@ -112,7 +121,7 @@
   // Expect replacement port to be ignored because file URLs do not use port.
   GURL url("file://initial.test/file.txt");
   ASSERT_EQ(url.EffectiveIntPort(), url::PORT_UNSPECIFIED);
-  EXPECT_TRUE(rules.RewriteUrl(url));
+  EXPECT_EQ(rules.RewriteUrl(url), HostMappingRules::RewriteResult::kRewritten);
   EXPECT_EQ(url, GURL("file://replacement.test/file.txt"));
   EXPECT_EQ(url.EffectiveIntPort(), url::PORT_UNSPECIFIED);
 }
@@ -127,7 +136,7 @@
   rules.AddRuleFromString("MAP initial.test replacement.test:1000");
 
   GURL url("foo://initial.test:100");
-  EXPECT_TRUE(rules.RewriteUrl(url));
+  EXPECT_EQ(rules.RewriteUrl(url), HostMappingRules::RewriteResult::kRewritten);
   EXPECT_EQ(url, GURL("foo://replacement.test:1000"));
 }
 
@@ -143,7 +152,7 @@
   // Expect replacement port to be ignored.
   GURL url("foo://initial.test");
   ASSERT_EQ(url.EffectiveIntPort(), url::PORT_UNSPECIFIED);
-  EXPECT_TRUE(rules.RewriteUrl(url));
+  EXPECT_EQ(rules.RewriteUrl(url), HostMappingRules::RewriteResult::kRewritten);
   EXPECT_EQ(url, GURL("foo://replacement.test"));
   EXPECT_EQ(url.EffectiveIntPort(), url::PORT_UNSPECIFIED);
 }
@@ -153,7 +162,8 @@
   rules.AddRuleFromString("MAP initial.test replacement.test:1000");
 
   GURL url("http://different.test:111");
-  EXPECT_FALSE(rules.RewriteUrl(url));
+  EXPECT_EQ(rules.RewriteUrl(url),
+            HostMappingRules::RewriteResult::kNoMatchingRule);
   EXPECT_EQ(url, GURL("http://different.test:111"));
 }
 
@@ -162,7 +172,20 @@
   rules.AddRuleFromString("MAP initial.test invalid/url");
 
   GURL url("http://initial.test");
-  EXPECT_FALSE(rules.RewriteUrl(url));
+  EXPECT_EQ(rules.RewriteUrl(url),
+            HostMappingRules::RewriteResult::kInvalidRewrite);
+  EXPECT_EQ(url, GURL("http://initial.test"));
+}
+
+// Remapping to "~NOTFOUND" is documented as a special case for
+// MappedHostResolver usage. Ensure that it is handled as invalid as expected.
+TEST(HostMappingRulesTest, NotFoundIgnoredAsInvalidUrl) {
+  HostMappingRules rules;
+  rules.AddRuleFromString("MAP initial.test ~NOTFOUND");
+
+  GURL url("http://initial.test");
+  EXPECT_EQ(rules.RewriteUrl(url),
+            HostMappingRules::RewriteResult::kInvalidRewrite);
   EXPECT_EQ(url, GURL("http://initial.test"));
 }
 
diff --git a/net/dns/context_host_resolver.cc b/net/dns/context_host_resolver.cc
index 915bc74..9c9c173 100644
--- a/net/dns/context_host_resolver.cc
+++ b/net/dns/context_host_resolver.cc
@@ -16,11 +16,15 @@
 #include "net/base/network_isolation_key.h"
 #include "net/dns/dns_config.h"
 #include "net/dns/host_cache.h"
+#include "net/dns/host_resolver.h"
 #include "net/dns/host_resolver_manager.h"
 #include "net/dns/host_resolver_proc.h"
 #include "net/dns/public/resolve_error_info.h"
 #include "net/dns/resolve_context.h"
+#include "net/log/net_log_with_source.h"
 #include "net/url_request/url_request_context.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/scheme_host_port.h"
 
 namespace net {
 
@@ -172,9 +176,8 @@
   const absl::optional<HostCache::EntryStaleness>& GetStaleInfo()
       const override {
     if (!inner_request_) {
-      static const base::NoDestructor<absl::optional<HostCache::EntryStaleness>>
-          nullopt_result;
-      return *nullopt_result;
+      static const absl::optional<HostCache::EntryStaleness> nullopt_result;
+      return nullopt_result;
     }
 
     return inner_request_->GetStaleInfo();
@@ -293,6 +296,29 @@
 
 std::unique_ptr<HostResolver::ResolveHostRequest>
 ContextHostResolver::CreateRequest(
+    url::SchemeHostPort host,
+    NetworkIsolationKey network_isolation_key,
+    NetLogWithSource source_net_log,
+    absl::optional<ResolveHostParameters> optional_parameters) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  std::unique_ptr<HostResolverManager::CancellableResolveHostRequest>
+      inner_request;
+  if (!shutting_down_) {
+    inner_request = manager_->CreateRequest(
+        std::move(host), std::move(network_isolation_key),
+        std::move(source_net_log), std::move(optional_parameters),
+        resolve_context_.get(), resolve_context_->host_cache());
+  }
+
+  auto request = std::make_unique<WrappedResolveHostRequest>(
+      std::move(inner_request), this, shutting_down_);
+  handed_out_requests_.insert(request.get());
+  return request;
+}
+
+std::unique_ptr<HostResolver::ResolveHostRequest>
+ContextHostResolver::CreateRequest(
     const HostPortPair& host,
     const NetworkIsolationKey& network_isolation_key,
     const NetLogWithSource& source_net_log,
diff --git a/net/dns/context_host_resolver.h b/net/dns/context_host_resolver.h
index 5faba8da..eed4048 100644
--- a/net/dns/context_host_resolver.h
+++ b/net/dns/context_host_resolver.h
@@ -12,7 +12,11 @@
 #include "base/macros.h"
 #include "base/sequence_checker.h"
 #include "net/base/net_export.h"
+#include "net/base/network_isolation_key.h"
 #include "net/dns/host_resolver.h"
+#include "net/log/net_log_with_source.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/scheme_host_port.h"
 
 namespace base {
 class TickClock;
@@ -45,6 +49,11 @@
   // HostResolver methods:
   void OnShutdown() override;
   std::unique_ptr<ResolveHostRequest> CreateRequest(
+      url::SchemeHostPort host,
+      NetworkIsolationKey network_isolation_key,
+      NetLogWithSource net_log,
+      absl::optional<ResolveHostParameters> optional_parameters) override;
+  std::unique_ptr<ResolveHostRequest> CreateRequest(
       const HostPortPair& host,
       const NetworkIsolationKey& network_isolation_key,
       const NetLogWithSource& net_log,
diff --git a/net/dns/context_host_resolver_unittest.cc b/net/dns/context_host_resolver_unittest.cc
index c4a51e5..39e71f1a 100644
--- a/net/dns/context_host_resolver_unittest.cc
+++ b/net/dns/context_host_resolver_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "net/dns/context_host_resolver.h"
 
+#include <memory>
 #include <utility>
 
 #include "base/bind.h"
@@ -17,12 +18,14 @@
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
+#include "net/base/network_isolation_key.h"
 #include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/dns/dns_config.h"
 #include "net/dns/dns_test_util.h"
 #include "net/dns/dns_util.h"
 #include "net/dns/host_cache.h"
+#include "net/dns/host_resolver.h"
 #include "net/dns/host_resolver_manager.h"
 #include "net/dns/host_resolver_source.h"
 #include "net/dns/mock_host_resolver.h"
@@ -37,6 +40,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
+#include "url/scheme_host_port.h"
 
 namespace net {
 
@@ -123,6 +127,59 @@
               testing::ElementsAre(kEndpoint));
 }
 
+TEST_F(ContextHostResolverTest, ResolveWithScheme) {
+  URLRequestContext context;
+
+  MockDnsClientRuleList rules;
+  rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
+                         "example.com", kEndpoint.address())),
+                     false /* delay */, &context);
+  rules.emplace_back("example.com", dns_protocol::kTypeAAAA, false /* secure */,
+                     MockDnsClientRule::Result(MockDnsClientRule::EMPTY),
+                     false /* delay */, &context);
+  SetMockDnsRules(std::move(rules));
+
+  auto resolve_context =
+      std::make_unique<ResolveContext>(&context, false /* enable_caching */);
+  auto resolver = std::make_unique<ContextHostResolver>(
+      manager_.get(), std::move(resolve_context));
+  std::unique_ptr<HostResolver::ResolveHostRequest> request =
+      resolver->CreateRequest(
+          url::SchemeHostPort(url::kHttpsScheme, "example.com", 100),
+          NetworkIsolationKey(), NetLogWithSource(), absl::nullopt);
+
+  TestCompletionCallback callback;
+  int rv = request->Start(callback.callback());
+  EXPECT_THAT(callback.GetResult(rv), test::IsOk());
+  EXPECT_THAT(request->GetResolveErrorInfo().error, test::IsError(net::OK));
+  EXPECT_THAT(request->GetAddressResults().value().endpoints(),
+              testing::ElementsAre(kEndpoint));
+}
+
+TEST_F(ContextHostResolverTest, ResolveWithSchemeAndIpLiteral) {
+  URLRequestContext context;
+
+  IPAddress expected_address;
+  ASSERT_TRUE(expected_address.AssignFromIPLiteral("1234::5678"));
+
+  auto resolve_context =
+      std::make_unique<ResolveContext>(&context, false /* enable_caching */);
+  auto resolver = std::make_unique<ContextHostResolver>(
+      manager_.get(), std::move(resolve_context));
+  std::unique_ptr<HostResolver::ResolveHostRequest> request =
+      resolver->CreateRequest(
+          url::SchemeHostPort(url::kHttpsScheme, "[1234::5678]", 100),
+          NetworkIsolationKey(), NetLogWithSource(), absl::nullopt);
+
+  TestCompletionCallback callback;
+  int rv = request->Start(callback.callback());
+  EXPECT_THAT(callback.GetResult(rv), test::IsOk());
+  EXPECT_THAT(request->GetResolveErrorInfo().error, test::IsError(net::OK));
+  EXPECT_THAT(request->GetAddressResults().value().endpoints(),
+              testing::ElementsAre(IPEndPoint(expected_address, 100)));
+}
+
 // Test that destroying a request silently cancels that request.
 TEST_F(ContextHostResolverTest, DestroyRequest) {
   // Set up delayed results for "example.com".
diff --git a/net/dns/dns_test_util.cc b/net/dns/dns_test_util.cc
index 4c86ee84..1193eff 100644
--- a/net/dns/dns_test_util.cc
+++ b/net/dns/dns_test_util.cc
@@ -339,7 +339,7 @@
       public base::SupportsWeakPtr<MockTransaction> {
  public:
   MockTransaction(const MockDnsClientRuleList& rules,
-                  const std::string& hostname,
+                  std::string hostname,
                   uint16_t qtype,
                   bool secure,
                   bool force_doh_server_available,
@@ -348,7 +348,7 @@
                   bool fast_timeout,
                   DnsTransactionFactory::CallbackType callback)
       : result_(MockDnsClientRule::FAIL),
-        hostname_(hostname),
+        hostname_(std::move(hostname)),
         qtype_(qtype),
         callback_(std::move(callback)),
         started_(false),
@@ -359,13 +359,13 @@
         resolve_context->NumAvailableDohServers(
             resolve_context->current_session_for_testing()) > 0) {
       // Find the relevant rule which matches |qtype|, |secure|, prefix of
-      // |hostname|, and |url_request_context| (iff the rule context is not
+      // |hostname_|, and |url_request_context| (iff the rule context is not
       // null).
       for (size_t i = 0; i < rules.size(); ++i) {
         const std::string& prefix = rules[i].prefix;
         if ((rules[i].qtype == qtype) && (rules[i].secure == secure) &&
-            (hostname.size() >= prefix.size()) &&
-            (hostname.compare(0, prefix.size(), prefix) == 0) &&
+            (hostname_.size() >= prefix.size()) &&
+            (hostname_.compare(0, prefix.size(), prefix) == 0) &&
             (!rules[i].context ||
              rules[i].context == resolve_context->url_request_context())) {
           const MockDnsClientRule::Result* result = &rules[i].result;
@@ -537,7 +537,7 @@
 MockDnsTransactionFactory::~MockDnsTransactionFactory() = default;
 
 std::unique_ptr<DnsTransaction> MockDnsTransactionFactory::CreateTransaction(
-    const std::string& hostname,
+    std::string hostname,
     uint16_t qtype,
     DnsTransactionFactory::CallbackType callback,
     const NetLogWithSource&,
@@ -546,9 +546,10 @@
     ResolveContext* resolve_context,
     bool fast_timeout) {
   std::unique_ptr<MockTransaction> transaction =
-      std::make_unique<MockTransaction>(
-          rules_, hostname, qtype, secure, force_doh_server_available_,
-          secure_dns_mode, resolve_context, fast_timeout, std::move(callback));
+      std::make_unique<MockTransaction>(rules_, std::move(hostname), qtype,
+                                        secure, force_doh_server_available_,
+                                        secure_dns_mode, resolve_context,
+                                        fast_timeout, std::move(callback));
   if (transaction->delayed())
     delayed_transactions_.push_back(transaction->AsWeakPtr());
   return transaction;
diff --git a/net/dns/dns_test_util.h b/net/dns/dns_test_util.h
index 5e2895b4..8c04281 100644
--- a/net/dns/dns_test_util.h
+++ b/net/dns/dns_test_util.h
@@ -330,7 +330,7 @@
   ~MockDnsTransactionFactory() override;
 
   std::unique_ptr<DnsTransaction> CreateTransaction(
-      const std::string& hostname,
+      std::string hostname,
       uint16_t qtype,
       DnsTransactionFactory::CallbackType callback,
       const NetLogWithSource&,
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc
index 57d5663..7be16510 100644
--- a/net/dns/dns_transaction.cc
+++ b/net/dns/dns_transaction.cc
@@ -1040,7 +1040,7 @@
                            public base::SupportsWeakPtr<DnsTransactionImpl> {
  public:
   DnsTransactionImpl(DnsSession* session,
-                     const std::string& hostname,
+                     std::string hostname,
                      uint16_t qtype,
                      DnsTransactionFactory::CallbackType callback,
                      const NetLogWithSource& net_log,
@@ -1050,7 +1050,7 @@
                      ResolveContext* resolve_context,
                      bool fast_timeout)
       : session_(session),
-        hostname_(hostname),
+        hostname_(std::move(hostname)),
         qtype_(qtype),
         opt_rdata_(opt_rdata),
         secure_(secure),
@@ -1648,7 +1648,7 @@
   }
 
   std::unique_ptr<DnsTransaction> CreateTransaction(
-      const std::string& hostname,
+      std::string hostname,
       uint16_t qtype,
       CallbackType callback,
       const NetLogWithSource& net_log,
@@ -1657,8 +1657,8 @@
       ResolveContext* resolve_context,
       bool fast_timeout) override {
     return std::make_unique<DnsTransactionImpl>(
-        session_.get(), hostname, qtype, std::move(callback), net_log,
-        opt_rdata_.get(), secure, secure_dns_mode, resolve_context,
+        session_.get(), std::move(hostname), qtype, std::move(callback),
+        net_log, opt_rdata_.get(), secure, secure_dns_mode, resolve_context,
         fast_timeout);
   }
 
diff --git a/net/dns/dns_transaction.h b/net/dns/dns_transaction.h
index c6e3471..3b47b8a 100644
--- a/net/dns/dns_transaction.h
+++ b/net/dns/dns_transaction.h
@@ -112,7 +112,7 @@
   // and it would be beneficial to move on to those options sooner on signals
   // that the transaction is potentially slow or problematic.
   virtual std::unique_ptr<DnsTransaction> CreateTransaction(
-      const std::string& hostname,
+      std::string hostname,
       uint16_t qtype,
       CallbackType callback,
       const NetLogWithSource& net_log,
diff --git a/net/dns/host_cache.cc b/net/dns/host_cache.cc
index 17bd553..9af27e2 100644
--- a/net/dns/host_cache.cc
+++ b/net/dns/host_cache.cc
@@ -112,12 +112,12 @@
   MAX_ERASE_REASON
 };
 
-HostCache::Key::Key(const std::string& hostname,
+HostCache::Key::Key(std::string hostname,
                     DnsQueryType dns_query_type,
                     HostResolverFlags host_resolver_flags,
                     HostResolverSource host_resolver_source,
                     const NetworkIsolationKey& network_isolation_key)
-    : hostname(hostname),
+    : hostname(std::move(hostname)),
       dns_query_type(dns_query_type),
       host_resolver_flags(host_resolver_flags),
       host_resolver_source(host_resolver_source),
diff --git a/net/dns/host_cache.h b/net/dns/host_cache.h
index 0aa5e37..2f83993 100644
--- a/net/dns/host_cache.h
+++ b/net/dns/host_cache.h
@@ -46,7 +46,7 @@
 class NET_EXPORT HostCache {
  public:
   struct NET_EXPORT Key {
-    Key(const std::string& hostname,
+    Key(std::string hostname,
         DnsQueryType dns_query_type,
         HostResolverFlags host_resolver_flags,
         HostResolverSource host_resolver_source,
diff --git a/net/dns/host_resolver.cc b/net/dns/host_resolver.cc
index 16c6589c..b13c671 100644
--- a/net/dns/host_resolver.cc
+++ b/net/dns/host_resolver.cc
@@ -72,9 +72,8 @@
 
   const absl::optional<HostCache::EntryStaleness>& GetStaleInfo()
       const override {
-    static const base::NoDestructor<absl::optional<HostCache::EntryStaleness>>
-        nullopt_result;
-    return *nullopt_result;
+    static const absl::optional<HostCache::EntryStaleness> nullopt_result;
+    return nullopt_result;
   }
 
  private:
diff --git a/net/dns/host_resolver.h b/net/dns/host_resolver.h
index 334527d..93676bb 100644
--- a/net/dns/host_resolver.h
+++ b/net/dns/host_resolver.h
@@ -17,6 +17,7 @@
 #include "net/base/address_family.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/host_port_pair.h"
+#include "net/base/network_isolation_key.h"
 #include "net/base/request_priority.h"
 #include "net/dns/host_cache.h"
 #include "net/dns/host_resolver_source.h"
@@ -24,7 +25,9 @@
 #include "net/dns/public/dns_query_type.h"
 #include "net/dns/public/resolve_error_info.h"
 #include "net/dns/public/secure_dns_policy.h"
+#include "net/log/net_log_with_source.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/scheme_host_port.h"
 
 namespace base {
 class Value;
@@ -38,7 +41,6 @@
 struct DnsConfigOverrides;
 class HostResolverManager;
 class NetLog;
-class NetLogWithSource;
 class URLRequestContext;
 
 // This class represents the task of resolving hostnames (or IP address
@@ -324,7 +326,15 @@
   // Profiling information for the request is saved to |net_log| if non-NULL.
   //
   // Additional parameters may be set using |optional_parameters|. Reasonable
-  // defaults will be used if passed |absl::nullopt|.
+  // defaults will be used if passed |nullptr|.
+  virtual std::unique_ptr<ResolveHostRequest> CreateRequest(
+      url::SchemeHostPort host,
+      NetworkIsolationKey network_isolation_key,
+      NetLogWithSource net_log,
+      absl::optional<ResolveHostParameters> optional_parameters) = 0;
+
+  // Create requests when scheme is unknown or non-standard.
+  // TODO(crbug.com/1206799): Rename to discourage use when scheme is known.
   virtual std::unique_ptr<ResolveHostRequest> CreateRequest(
       const HostPortPair& host,
       const NetworkIsolationKey& network_isolation_key,
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 5a901ad..3e1e676 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -99,6 +99,9 @@
 #include "net/log/net_log_with_source.h"
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/datagram_client_socket.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
+#include "url/scheme_host_port.h"
 #include "url/third_party/mozilla/url_parse.h"
 #include "url/url_canon_ip.h"
 
@@ -171,7 +174,7 @@
 }
 
 // True if |hostname| ends with either ".local" or ".local.".
-bool ResemblesMulticastDNSName(const std::string& hostname) {
+bool ResemblesMulticastDNSName(base::StringPiece hostname) {
   const char kSuffix[] = ".local.";
   const size_t kSuffixLen = sizeof(kSuffix) - 1;
   const size_t kSuffixLenTrimmed = kSuffixLen - 1;
@@ -324,7 +327,7 @@
 
 // Creates NetLog parameters for the creation of a HostResolverManager::Job.
 base::Value NetLogJobCreationParams(const NetLogSource& source,
-                                    const std::string& host) {
+                                    base::StringPiece host) {
   base::Value dict(base::Value::Type::DICTIONARY);
   source.AddToEventParameters(&dict);
   dict.SetStringKey("host", host);
@@ -464,6 +467,57 @@
   net_log.AddEntry(type, phase, [&] { return results.NetLogParams(); });
 }
 
+base::Value ToLogStringValue(
+    const absl::variant<url::SchemeHostPort, HostPortPair>& host) {
+  if (absl::holds_alternative<url::SchemeHostPort>(host))
+    return base::Value(absl::get<url::SchemeHostPort>(host).Serialize());
+
+  return base::Value(absl::get<HostPortPair>(host).ToString());
+}
+
+base::StringPiece GetHostname(
+    const absl::variant<url::SchemeHostPort, HostPortPair>& host) {
+  if (absl::holds_alternative<url::SchemeHostPort>(host)) {
+    base::StringPiece hostname = absl::get<url::SchemeHostPort>(host).host();
+    if (hostname.size() >= 2 && hostname.front() == '[' &&
+        hostname.back() == ']') {
+      hostname = hostname.substr(1, hostname.size() - 2);
+    }
+    return hostname;
+  }
+
+  return absl::get<HostPortPair>(host).host();
+}
+
+base::StringPiece GetHostname(
+    const absl::variant<url::SchemeHostPort, std::string>& host) {
+  if (absl::holds_alternative<url::SchemeHostPort>(host)) {
+    base::StringPiece hostname = absl::get<url::SchemeHostPort>(host).host();
+    if (hostname.size() >= 2 && hostname.front() == '[' &&
+        hostname.back() == ']') {
+      hostname = hostname.substr(1, hostname.size() - 2);
+    }
+    return hostname;
+  }
+
+  return absl::get<std::string>(host);
+}
+
+uint16_t GetPort(const absl::variant<url::SchemeHostPort, HostPortPair>& host) {
+  if (absl::holds_alternative<url::SchemeHostPort>(host)) {
+    return absl::get<url::SchemeHostPort>(host).port();
+  }
+
+  return absl::get<HostPortPair>(host).port();
+}
+
+// TODO(crbug.com/1206799): Make dependent on status of `kUseDnsHttpsSvcb`
+// Feature.
+absl::variant<url::SchemeHostPort, std::string> CreateHostForJobKey(
+    const absl::variant<url::SchemeHostPort, HostPortPair>& input) {
+  return std::string(GetHostname(input));
+}
+
 }  // namespace
 
 //-----------------------------------------------------------------------------
@@ -494,22 +548,22 @@
     : public CancellableResolveHostRequest,
       public base::LinkNode<HostResolverManager::RequestImpl> {
  public:
-  RequestImpl(const NetLogWithSource& source_net_log,
-              const HostPortPair& request_host,
-              const NetworkIsolationKey& network_isolation_key,
-              const absl::optional<ResolveHostParameters>& optional_parameters,
+  RequestImpl(NetLogWithSource source_net_log,
+              absl::variant<url::SchemeHostPort, HostPortPair> request_host,
+              NetworkIsolationKey network_isolation_key,
+              absl::optional<ResolveHostParameters> optional_parameters,
               ResolveContext* resolve_context,
               HostCache* host_cache,
               base::WeakPtr<HostResolverManager> resolver,
               const base::TickClock* tick_clock)
-      : source_net_log_(source_net_log),
-        request_host_(request_host),
+      : source_net_log_(std::move(source_net_log)),
+        request_host_(std::move(request_host)),
         network_isolation_key_(
             base::FeatureList::IsEnabled(
                 net::features::kSplitHostCacheByNetworkIsolationKey)
-                ? network_isolation_key
+                ? std::move(network_isolation_key)
                 : NetworkIsolationKey()),
-        parameters_(optional_parameters ? optional_parameters.value()
+        parameters_(optional_parameters ? std::move(optional_parameters).value()
                                         : ResolveHostParameters()),
         resolve_context_(resolve_context),
         host_cache_(host_cache),
@@ -671,7 +725,9 @@
   // NetLog for the source, passed in HostResolver::Resolve.
   const NetLogWithSource& source_net_log() { return source_net_log_; }
 
-  const HostPortPair& request_host() const { return request_host_; }
+  const absl::variant<url::SchemeHostPort, HostPortPair>& request_host() const {
+    return request_host_;
+  }
 
   const NetworkIsolationKey& network_isolation_key() const {
     return network_isolation_key_;
@@ -712,7 +768,7 @@
     source_net_log_.BeginEvent(
         NetLogEventType::HOST_RESOLVER_IMPL_REQUEST, [this] {
           base::Value dict(base::Value::Type::DICTIONARY);
-          dict.SetStringKey("host", request_host_.ToString());
+          dict.SetKey("host", ToLogStringValue(request_host_));
           dict.SetIntKey("dns_query_type",
                          static_cast<int>(parameters_.dns_query_type));
           dict.SetBoolKey("allow_cached_response",
@@ -731,9 +787,9 @@
     source_net_log_.EndEventWithNetErrorCode(
         NetLogEventType::HOST_RESOLVER_IMPL_REQUEST, net_error);
 
+    base::StringPiece hostname = GetHostname(request_host_);
     url::HostSafetyStatus host_safety_status = url::CheckHostnameSafety(
-        request_host_.host().c_str(),
-        url::Component(0, request_host_.host().size()));
+        hostname.data(), url::Component(0, hostname.size()));
     if (net_error == net::OK) {
       UMA_HISTOGRAM_ENUMERATION("Net.DNS.Request.Success.HostSafetyStatus",
                                 host_safety_status);
@@ -760,7 +816,7 @@
 
   const NetLogWithSource source_net_log_;
 
-  const HostPortPair request_host_;
+  const absl::variant<url::SchemeHostPort, HostPortPair> request_host_;
   const NetworkIsolationKey network_isolation_key_;
   ResolveHostParameters parameters_;
   // TODO(ericorth@chromium.org): Use base::UnownedPtr once available.
@@ -1117,7 +1173,7 @@
   };
 
   DnsTask(DnsClient* client,
-          base::StringPiece hostname,
+          absl::variant<url::SchemeHostPort, std::string> host,
           DnsQueryType query_type,
           ResolveContext* resolve_context,
           bool secure,
@@ -1127,7 +1183,7 @@
           const base::TickClock* tick_clock,
           bool fallback_available)
       : client_(client),
-        hostname_(hostname),
+        host_(std::move(host)),
         resolve_context_(resolve_context),
         secure_(secure),
         secure_dns_mode_(secure_dns_mode),
@@ -1151,9 +1207,9 @@
 
       // Queue up an INTEGRITY/HTTPS query if we are allowed to.
       const bool is_httpssvc_experiment_domain =
-          httpssvc_domain_cache_.IsExperimental(hostname);
+          httpssvc_domain_cache_.IsExperimental(GetHostname(host_));
       const bool is_httpssvc_control_domain =
-          httpssvc_domain_cache_.IsControl(hostname);
+          httpssvc_domain_cache_.IsControl(GetHostname(host_));
       const bool can_query_via_insecure =
           !secure_ && features::kDnsHttpssvcEnableQueryOverInsecure.Get() &&
           client_->CanQueryAdditionalTypesViaInsecureDns();
@@ -1215,7 +1271,8 @@
 
     std::unique_ptr<DnsTransaction> trans =
         client_->GetTransactionFactory()->CreateTransaction(
-            hostname_, DnsQueryTypeToQtype(dns_query_type),
+            std::string(GetHostname(host_)),
+            DnsQueryTypeToQtype(dns_query_type),
             base::BindOnce(&DnsTask::OnTransactionComplete,
                            base::Unretained(this), tick_clock_->NowTicks(),
                            dns_query_type),
@@ -1529,7 +1586,9 @@
   }
 
   DnsClient* client_;
-  std::string hostname_;
+
+  absl::variant<url::SchemeHostPort, std::string> host_;
+
   // TODO(ericorth@chromium.org): Use base::UnownedPtr once available.
   ResolveContext* const resolve_context_;
 
@@ -1574,14 +1633,13 @@
 struct HostResolverManager::JobKey {
   bool operator<(const JobKey& other) const {
     return std::forward_as_tuple(query_type, flags, source, secure_dns_mode,
-                                 resolve_context, hostname,
-                                 network_isolation_key) <
+                                 resolve_context, host, network_isolation_key) <
            std::forward_as_tuple(other.query_type, other.flags, other.source,
                                  other.secure_dns_mode, other.resolve_context,
-                                 other.hostname, other.network_isolation_key);
+                                 other.host, other.network_isolation_key);
   }
 
-  std::string hostname;
+  absl::variant<url::SchemeHostPort, std::string> host;
   NetworkIsolationKey network_isolation_key;
   DnsQueryType query_type;
   HostResolverFlags flags;
@@ -1591,8 +1649,9 @@
   ResolveContext* resolve_context;
 
   HostCache::Key ToCacheKey(bool secure) const {
-    HostCache::Key key(hostname, query_type, flags, source,
-                       network_isolation_key);
+    // TODO(crbug.com/1206799): Propagate scheme and port to cache key.
+    HostCache::Key key(std::string(GetHostname(host)), query_type, flags,
+                       source, network_isolation_key);
     key.secure = secure;
     return key;
   }
@@ -1634,7 +1693,8 @@
     source_net_log.AddEvent(NetLogEventType::HOST_RESOLVER_IMPL_CREATE_JOB);
 
     net_log_.BeginEvent(NetLogEventType::HOST_RESOLVER_IMPL_JOB, [&] {
-      return NetLogJobCreationParams(source_net_log.source(), key_.hostname);
+      return NetLogJobCreationParams(source_net_log.source(),
+                                     GetHostname(key_.host));
     });
   }
 
@@ -1688,7 +1748,9 @@
     // HostCache. Since the ResolveContext is part of the JobKey, any request
     // added to any existing Job should share the same HostCache.
     DCHECK_EQ(host_cache_, request->host_cache());
-    DCHECK_EQ(key_.hostname, request->request_host().host());
+    // TODO(crbug.com/1206799): Check equality of whole host once Jobs are
+    // separated by scheme/port.
+    DCHECK_EQ(GetHostname(key_.host), GetHostname(request->request_host()));
 
     request->AssignJob(this);
 
@@ -1712,7 +1774,9 @@
   }
 
   void ChangeRequestPriority(RequestImpl* req, RequestPriority priority) {
-    DCHECK_EQ(key_.hostname, req->request_host().host());
+    // TODO(crbug.com/1206799): Check equality of whole host once Jobs are
+    // separated by scheme/port.
+    DCHECK_EQ(GetHostname(key_.host), GetHostname(req->request_host()));
 
     priority_tracker_.Remove(req->priority());
     req->set_priority(priority);
@@ -1723,7 +1787,9 @@
   // Detach cancelled request. If it was the last active Request, also finishes
   // this Job.
   void CancelRequest(RequestImpl* request) {
-    DCHECK_EQ(key_.hostname, request->request_host().host());
+    // TODO(crbug.com/1206799): Check equality of whole host once Jobs are
+    // separated by scheme/port.
+    DCHECK_EQ(GetHostname(key_.host), GetHostname(request->request_host()));
     DCHECK(!requests_.empty());
 
     priority_tracker_.Remove(request->priority());
@@ -1814,7 +1880,7 @@
   bool ServeFromHosts() {
     DCHECK_GT(num_active_requests(), 0u);
     absl::optional<HostCache::Entry> results = resolver_->ServeFromHosts(
-        key_.hostname, key_.query_type,
+        GetHostname(key_.host), key_.query_type,
         key_.flags & HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6, tasks_);
     if (results) {
       // This will destroy the Job.
@@ -1986,7 +2052,7 @@
     DCHECK(IsAddressType(key_.query_type));
 
     proc_task_ = std::make_unique<ProcTask>(
-        key_.hostname,
+        std::string(GetHostname(key_.host)),
         HostResolver::DnsQueryTypeToAddressFamily(key_.query_type), key_.flags,
         resolver_->proc_params_,
         base::BindOnce(&Job::OnProcTaskComplete, base::Unretained(this),
@@ -2055,7 +2121,7 @@
     // Need to create the task even if we're going to post a failure instead of
     // running it, as a "started" job needs a task to be properly cleaned up.
     dns_task_ = std::make_unique<DnsTask>(
-        resolver_->dns_client_.get(), key_.hostname, key_.query_type,
+        resolver_->dns_client_.get(), key_.host, key_.query_type,
         key_.resolve_context, secure, key_.secure_dns_mode, this, net_log_,
         tick_clock_, !tasks_.empty() /* fallback_available */);
     dns_task_->StartNextTransaction();
@@ -2195,8 +2261,8 @@
 
     MDnsClient* client = nullptr;
     int rv = resolver_->GetOrCreateMdnsClient(&client);
-    mdns_task_ = std::make_unique<HostResolverMdnsTask>(client, key_.hostname,
-                                                        query_types);
+    mdns_task_ = std::make_unique<HostResolverMdnsTask>(
+        client, std::string(GetHostname(key_.host)), query_types);
 
     if (rv == OK) {
       mdns_task_->Start(
@@ -2360,7 +2426,7 @@
 
       if (results.error() == OK && !req->parameters().is_speculative) {
         req->set_results(
-            results.CopyWithDefaultPort(req->request_host().port()));
+            results.CopyWithDefaultPort(GetPort(req->request_host())));
 
         // TODO(cammie): Move the sanitization deeper, possibly in
         // HttpCache::Entry::SetResult(AddressList addresses), so that it
@@ -2553,10 +2619,10 @@
 
 std::unique_ptr<HostResolverManager::CancellableResolveHostRequest>
 HostResolverManager::CreateRequest(
-    const HostPortPair& host,
-    const NetworkIsolationKey& network_isolation_key,
-    const NetLogWithSource& net_log,
-    const absl::optional<ResolveHostParameters>& optional_parameters,
+    absl::variant<url::SchemeHostPort, HostPortPair> host,
+    NetworkIsolationKey network_isolation_key,
+    NetLogWithSource net_log,
+    absl::optional<ResolveHostParameters> optional_parameters,
     ResolveContext* resolve_context,
     HostCache* host_cache) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -2567,8 +2633,9 @@
   DCHECK(registered_contexts_.HasObserver(resolve_context));
 
   return std::make_unique<RequestImpl>(
-      net_log, host, network_isolation_key, optional_parameters,
-      resolve_context, host_cache, weak_ptr_factory_.GetWeakPtr(), tick_clock_);
+      std::move(net_log), std::move(host), std::move(network_isolation_key),
+      std::move(optional_parameters), resolve_context, host_cache,
+      weak_ptr_factory_.GetWeakPtr(), tick_clock_);
 }
 
 std::unique_ptr<HostResolverManager::CancellableProbeRequest>
@@ -2746,16 +2813,16 @@
 
   const auto& parameters = request->parameters();
   JobKey job_key;
-  job_key.hostname = request->request_host().host();
+  job_key.host = CreateHostForJobKey(request->request_host());
   job_key.network_isolation_key = request->network_isolation_key();
   job_key.source = parameters.source;
   job_key.resolve_context = request->resolve_context();
 
   IPAddress ip_address;
-  bool is_ip = ip_address.AssignFromIPLiteral(job_key.hostname);
+  bool is_ip = ip_address.AssignFromIPLiteral(GetHostname(job_key.host));
 
   GetEffectiveParametersForRequest(
-      job_key.hostname, parameters.dns_query_type,
+      GetHostname(job_key.host), parameters.dns_query_type,
       request->host_resolver_flags(), parameters.secure_dns_policy,
       parameters.cache_usage, is_ip, request->source_net_log(),
       &job_key.query_type, &job_key.flags, &job_key.secure_dns_mode);
@@ -2770,7 +2837,7 @@
       tasks.empty()) {
     if (results.error() == OK && !request->parameters().is_speculative) {
       request->set_results(
-          results.CopyWithDefaultPort(request->request_host().port()));
+          results.CopyWithDefaultPort(GetPort(request->request_host())));
 
       // TODO(cammie): Sanitize before adding to the cache instead.
       request->SanitizeDnsAliasResults();
@@ -2806,8 +2873,8 @@
     // than implicitly based on |source|.
     const bool is_valid_hostname =
         job_key.source == HostResolverSource::MULTICAST_DNS
-            ? IsValidUnrestrictedDNSDomain(job_key.hostname)
-            : IsValidDNSDomain(job_key.hostname);
+            ? IsValidUnrestrictedDNSDomain(GetHostname(job_key.host))
+            : IsValidDNSDomain(GetHostname(job_key.host));
     if (!is_valid_hostname) {
       return HostCache::Entry(ERR_NAME_NOT_RESOLVED,
                               HostCache::Entry::SOURCE_UNKNOWN);
@@ -2821,7 +2888,8 @@
   // The result of |getaddrinfo| for empty hosts is inconsistent across systems.
   // On Windows it gives the default interface's address, whereas on Linux it
   // gives an error. We will make it fail on all platforms for consistency.
-  if (job_key.hostname.empty() || job_key.hostname.size() > kMaxHostLength) {
+  if (GetHostname(job_key.host).empty() ||
+      GetHostname(job_key.host).size() > kMaxHostLength) {
     return HostCache::Entry(ERR_NAME_NOT_RESOLVED,
                             HostCache::Entry::SOURCE_UNKNOWN);
   }
@@ -2833,7 +2901,7 @@
 
   // Special-case localhost names, as per the recommendations in
   // https://tools.ietf.org/html/draft-west-let-localhost-be-localhost.
-  resolved = ServeLocalhost(job_key.hostname, job_key.query_type,
+  resolved = ServeLocalhost(GetHostname(job_key.host), job_key.query_type,
                             default_family_due_to_no_ipv6);
   if (resolved)
     return resolved.value();
@@ -2868,7 +2936,7 @@
 
   // TODO(szym): Do not do this if nsswitch.conf instructs not to.
   // http://crbug.com/117655
-  resolved = ServeFromHosts(job_key.hostname, job_key.query_type,
+  resolved = ServeFromHosts(GetHostname(job_key.host), job_key.query_type,
                             default_family_due_to_no_ipv6, *out_tasks);
   if (resolved) {
     NetLogHostCacheEntry(source_net_log,
@@ -3209,7 +3277,7 @@
       if ((job_key.flags & HOST_RESOLVER_CANONNAME) &&
           IsAddressType(job_key.query_type)) {
         out_tasks->push_back(TaskType::PROC);
-      } else if (!ResemblesMulticastDNSName(job_key.hostname)) {
+      } else if (!ResemblesMulticastDNSName(GetHostname(job_key.host))) {
         bool proc_task_allowed =
             IsAddressType(job_key.query_type) &&
             job_key.secure_dns_mode != SecureDnsMode::kSecure;
@@ -3258,7 +3326,7 @@
 }
 
 void HostResolverManager::GetEffectiveParametersForRequest(
-    const std::string& hostname,
+    base::StringPiece hostname,
     DnsQueryType dns_query_type,
     HostResolverFlags flags,
     SecureDnsPolicy secure_dns_policy,
diff --git a/net/dns/host_resolver_manager.h b/net/dns/host_resolver_manager.h
index ad14780..d44b51b 100644
--- a/net/dns/host_resolver_manager.h
+++ b/net/dns/host_resolver_manager.h
@@ -20,10 +20,13 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/strings/string_piece.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "net/base/completion_once_callback.h"
+#include "net/base/host_port_pair.h"
 #include "net/base/network_change_notifier.h"
+#include "net/base/network_isolation_key.h"
 #include "net/base/prioritized_dispatcher.h"
 #include "net/dns/dns_config.h"
 #include "net/dns/host_cache.h"
@@ -35,7 +38,11 @@
 #include "net/dns/public/secure_dns_policy.h"
 #include "net/dns/resolve_context.h"
 #include "net/dns/system_dns_config_change_notifier.h"
+#include "net/log/net_log_with_source.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 #include "url/gurl.h"
+#include "url/scheme_host_port.h"
 
 namespace base {
 class TickClock;
@@ -46,13 +53,10 @@
 class AddressList;
 class DnsClient;
 class DnsProbeRunner;
-class HostPortPair;
 class IPAddress;
 class MDnsClient;
 class MDnsSocketFactory;
 class NetLog;
-class NetLogWithSource;
-class NetworkIsolationKey;
 
 // Scheduler and controller of host resolution requests. Because of the global
 // nature of host resolutions, this class is generally expected to be singleton
@@ -149,10 +153,10 @@
   // TODO(crbug.com/1022059): Use the HostCache out of the ResolveContext
   // instead of passing it separately.
   std::unique_ptr<CancellableResolveHostRequest> CreateRequest(
-      const HostPortPair& host,
-      const NetworkIsolationKey& network_isolation_key,
-      const NetLogWithSource& net_log,
-      const absl::optional<ResolveHostParameters>& optional_parameters,
+      absl::variant<url::SchemeHostPort, HostPortPair> host,
+      NetworkIsolationKey network_isolation_key,
+      NetLogWithSource net_log,
+      absl::optional<ResolveHostParameters> optional_parameters,
       ResolveContext* resolve_context,
       HostCache* host_cache);
   // |resolve_context| is the context to use for the probes, and it is expected
@@ -359,7 +363,7 @@
   // Determines "effective" request parameters using manager properties and IPv6
   // reachability.
   void GetEffectiveParametersForRequest(
-      const std::string& hostname,
+      base::StringPiece hostname,
       DnsQueryType dns_query_type,
       HostResolverFlags flags,
       SecureDnsPolicy secure_dns_policy,
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index 3c50380..1d5f655 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -79,6 +79,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
+#include "url/scheme_host_port.h"
+#include "url/url_constants.h"
 
 #if BUILDFLAG(ENABLE_MDNS)
 #include "net/dns/mdns_client_impl.h"
@@ -663,6 +665,31 @@
   EXPECT_TRUE(cache_result);
 }
 
+// TODO(crbug.com/1206799): Confirm scheme behavior once it affects behavior.
+TEST_F(HostResolverManagerTest, AsynchronousLookupWithScheme) {
+  proc_->AddRuleForAllFamilies("host.test", "192.168.1.42");
+  proc_->SignalMultiple(1u);
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      url::SchemeHostPort(url::kHttpScheme, "host.test", 80),
+      NetworkIsolationKey(), NetLogWithSource(), absl::nullopt,
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_THAT(response.result_error(), IsOk());
+  EXPECT_THAT(response.top_level_result_error(), IsOk());
+  EXPECT_THAT(response.request()->GetAddressResults().value().endpoints(),
+              testing::ElementsAre(CreateExpected("192.168.1.42", 80)));
+  EXPECT_FALSE(response.request()->GetStaleInfo());
+
+  EXPECT_EQ("host.test", proc_->GetCaptureList()[0].hostname);
+
+  const std::pair<const HostCache::Key, HostCache::Entry>* cache_result =
+      GetCacheHit(HostCache::Key(
+          "host.test", DnsQueryType::UNSPECIFIED, 0 /* host_resolver_flags */,
+          HostResolverSource::ANY, NetworkIsolationKey()));
+  EXPECT_TRUE(cache_result);
+}
+
 TEST_F(HostResolverManagerTest, JobsClearedOnCompletion) {
   proc_->AddRuleForAllFamilies("just.testing", "192.168.1.42");
   proc_->SignalMultiple(1u);
@@ -686,7 +713,7 @@
       NetLogWithSource(), absl::nullopt, resolve_context_.get(),
       resolve_context_->host_cache()));
   ResolveHostResponseHelper response2(resolver_->CreateRequest(
-      HostPortPair("just.testing", 85), NetworkIsolationKey(),
+      HostPortPair("just.testing", 80), NetworkIsolationKey(),
       NetLogWithSource(), absl::nullopt, resolve_context_.get(),
       resolve_context_->host_cache()));
   EXPECT_EQ(1u, resolver_->num_jobs_for_testing());
@@ -903,6 +930,17 @@
               testing::ElementsAre(CreateExpected("127.1.2.3", 5555)));
 }
 
+TEST_F(HostResolverManagerTest, NumericIPv4AddressWithScheme) {
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      url::SchemeHostPort(url::kHttpsScheme, "127.1.2.3", 5555),
+      NetworkIsolationKey(), NetLogWithSource(), absl::nullopt,
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_THAT(response.result_error(), IsOk());
+  EXPECT_THAT(response.request()->GetAddressResults().value().endpoints(),
+              testing::ElementsAre(CreateExpected("127.1.2.3", 5555)));
+}
+
 TEST_F(HostResolverManagerTest, NumericIPv6Address) {
   // Resolve a plain IPv6 address.  Don't worry about [brackets], because
   // the caller should have removed them.
@@ -916,6 +954,17 @@
               testing::ElementsAre(CreateExpected("2001:db8::1", 5555)));
 }
 
+TEST_F(HostResolverManagerTest, NumericIPv6AddressWithScheme) {
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      url::SchemeHostPort(url::kFtpScheme, "[2001:db8::1]", 5555),
+      NetworkIsolationKey(), NetLogWithSource(), absl::nullopt,
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_THAT(response.result_error(), IsOk());
+  EXPECT_THAT(response.request()->GetAddressResults().value().endpoints(),
+              testing::ElementsAre(CreateExpected("2001:db8::1", 5555)));
+}
+
 TEST_F(HostResolverManagerTest, EmptyHost) {
   ResolveHostResponseHelper response(resolver_->CreateRequest(
       HostPortPair(std::string(), 5555), NetworkIsolationKey(),
@@ -964,6 +1013,49 @@
           resolve_context_->host_cache())));
   responses.emplace_back(
       std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
+          HostPortPair("b", 80), NetworkIsolationKey(), NetLogWithSource(),
+          absl::nullopt, resolve_context_.get(),
+          resolve_context_->host_cache())));
+  responses.emplace_back(
+      std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
+          HostPortPair("a", 80), NetworkIsolationKey(), NetLogWithSource(),
+          absl::nullopt, resolve_context_.get(),
+          resolve_context_->host_cache())));
+  responses.emplace_back(
+      std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
+          HostPortPair("b", 80), NetworkIsolationKey(), NetLogWithSource(),
+          absl::nullopt, resolve_context_.get(),
+          resolve_context_->host_cache())));
+
+  for (auto& response : responses) {
+    ASSERT_FALSE(response->complete());
+  }
+
+  proc_->SignalMultiple(2u);  // One for "a:80", one for "b:80".
+
+  for (auto& response : responses) {
+    EXPECT_THAT(response->result_error(), IsOk());
+  }
+}
+
+// TODO(crbug.com/1206799): Delete/adapt once requests with different ports are
+// not deduped.
+TEST_F(HostResolverManagerTest, DeDupeRequestsWithDifferentPorts) {
+  // Start 5 requests, duplicating hosts "a" and "b". Since the resolver_proc is
+  // blocked, these should all pile up until we signal it.
+  std::vector<std::unique_ptr<ResolveHostResponseHelper>> responses;
+  responses.emplace_back(
+      std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
+          HostPortPair("a", 80), NetworkIsolationKey(), NetLogWithSource(),
+          absl::nullopt, resolve_context_.get(),
+          resolve_context_->host_cache())));
+  responses.emplace_back(
+      std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
+          HostPortPair("b", 80), NetworkIsolationKey(), NetLogWithSource(),
+          absl::nullopt, resolve_context_.get(),
+          resolve_context_->host_cache())));
+  responses.emplace_back(
+      std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
           HostPortPair("b", 81), NetworkIsolationKey(), NetLogWithSource(),
           absl::nullopt, resolve_context_.get(),
           resolve_context_->host_cache())));
@@ -1003,17 +1095,17 @@
           resolve_context_->host_cache())));
   responses.emplace_back(
       std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
-          HostPortPair("b", 81), NetworkIsolationKey(), NetLogWithSource(),
+          HostPortPair("b", 80), NetworkIsolationKey(), NetLogWithSource(),
           absl::nullopt, resolve_context_.get(),
           resolve_context_->host_cache())));
   responses.emplace_back(
       std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
-          HostPortPair("a", 82), NetworkIsolationKey(), NetLogWithSource(),
+          HostPortPair("a", 80), NetworkIsolationKey(), NetLogWithSource(),
           absl::nullopt, resolve_context_.get(),
           resolve_context_->host_cache())));
   responses.emplace_back(
       std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
-          HostPortPair("b", 83), NetworkIsolationKey(), NetLogWithSource(),
+          HostPortPair("b", 80), NetworkIsolationKey(), NetLogWithSource(),
           absl::nullopt, resolve_context_.get(),
           resolve_context_->host_cache())));
 
@@ -1021,7 +1113,7 @@
     ASSERT_FALSE(response->complete());
   }
 
-  // Cancel everything except request for requests[3] ("a", 82).
+  // Cancel everything except request for requests[3] ("a", 80).
   responses[0]->CancelRequest();
   responses[1]->CancelRequest();
   responses[2]->CancelRequest();
@@ -1054,7 +1146,7 @@
 
     responses.emplace_back(
         std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
-            HostPortPair(hostname, 81), NetworkIsolationKey(),
+            HostPortPair(hostname, 80), NetworkIsolationKey(),
             NetLogWithSource(), absl::nullopt, resolve_context_.get(),
             resolve_context_->host_cache())));
     ASSERT_FALSE(responses.back()->complete());
@@ -1103,12 +1195,12 @@
 
   responses.emplace_back(
       std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
-          HostPortPair("a", 81), NetworkIsolationKey(), NetLogWithSource(),
+          HostPortPair("a", 80), NetworkIsolationKey(), NetLogWithSource(),
           absl::nullopt, resolve_context_.get(),
           resolve_context_->host_cache())));
   responses.emplace_back(
       std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
-          HostPortPair("a", 82), NetworkIsolationKey(), NetLogWithSource(),
+          HostPortPair("a", 80), NetworkIsolationKey(), NetLogWithSource(),
           absl::nullopt, resolve_context_.get(),
           resolve_context_->host_cache())));
 
@@ -1199,7 +1291,7 @@
 
   responses.emplace_back(
       std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
-          HostPortPair("a", 81), NetworkIsolationKey(), NetLogWithSource(),
+          HostPortPair("a", 80), NetworkIsolationKey(), NetLogWithSource(),
           absl::nullopt, resolve_context_.get(),
           resolve_context_->host_cache())));
   responses.emplace_back(
@@ -1209,7 +1301,7 @@
           resolve_context_->host_cache())));
   responses.emplace_back(
       std::make_unique<ResolveHostResponseHelper>(resolver_->CreateRequest(
-          HostPortPair("b", 83), NetworkIsolationKey(), NetLogWithSource(),
+          HostPortPair("b", 82), NetworkIsolationKey(), NetLogWithSource(),
           absl::nullopt, resolve_context_.get(),
           resolve_context_->host_cache())));
 
@@ -1351,7 +1443,7 @@
   auto custom_callback = base::BindLambdaForTesting(
       [&](CompletionOnceCallback completion_callback, int error) {
         new_response = std::make_unique<ResolveHostResponseHelper>(
-            resolver_->CreateRequest(HostPortPair("evictor", 70),
+            resolver_->CreateRequest(HostPortPair("evictor", 80),
                                      NetworkIsolationKey(), NetLogWithSource(),
                                      absl::nullopt, resolve_context_.get(),
                                      resolve_context_->host_cache()));
@@ -4302,6 +4394,21 @@
               testing::ElementsAre(CreateExpected("192.168.1.102", 80)));
 }
 
+TEST_F(HostResolverManagerDnsTest, DnsTaskWithScheme) {
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      url::SchemeHostPort(url::kWsScheme, "ok_fail", 80), NetworkIsolationKey(),
+      NetLogWithSource(), absl::nullopt, resolve_context_.get(),
+      resolve_context_->host_cache()));
+
+  // Resolved by MockDnsClient.
+  EXPECT_THAT(response.result_error(), IsOk());
+  EXPECT_THAT(response.request()->GetAddressResults().value().endpoints(),
+              testing::UnorderedElementsAre(CreateExpected("127.0.0.1", 80),
+                                            CreateExpected("::1", 80)));
+}
+
 // Test successful and failing resolutions in HostResolverManager::DnsTask when
 // fallback to ProcTask is disabled.
 TEST_F(HostResolverManagerDnsTest, NoFallbackToProcTask) {
diff --git a/net/dns/host_resolver_mdns_task.cc b/net/dns/host_resolver_mdns_task.cc
index 687b957..84cac53a 100644
--- a/net/dns/host_resolver_mdns_task.cc
+++ b/net/dns/host_resolver_mdns_task.cc
@@ -127,9 +127,9 @@
 
 HostResolverMdnsTask::HostResolverMdnsTask(
     MDnsClient* mdns_client,
-    const std::string& hostname,
+    std::string hostname,
     const std::vector<DnsQueryType>& query_types)
-    : mdns_client_(mdns_client), hostname_(hostname) {
+    : mdns_client_(mdns_client), hostname_(std::move(hostname)) {
   DCHECK(!query_types.empty());
   for (DnsQueryType query_type : query_types) {
     transactions_.emplace_back(query_type, this);
diff --git a/net/dns/host_resolver_mdns_task.h b/net/dns/host_resolver_mdns_task.h
index e0e408a..b91e77bd 100644
--- a/net/dns/host_resolver_mdns_task.h
+++ b/net/dns/host_resolver_mdns_task.h
@@ -30,7 +30,7 @@
  public:
   // |mdns_client| must outlive |this|.
   HostResolverMdnsTask(MDnsClient* mdns_client,
-                       const std::string& hostname,
+                       std::string hostname,
                        const std::vector<DnsQueryType>& query_types);
   ~HostResolverMdnsTask();
 
diff --git a/net/dns/mapped_host_resolver.cc b/net/dns/mapped_host_resolver.cc
index be723810..0faf128 100644
--- a/net/dns/mapped_host_resolver.cc
+++ b/net/dns/mapped_host_resolver.cc
@@ -11,6 +11,14 @@
 #include "base/values.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/net_errors.h"
+#include "net/base/network_isolation_key.h"
+#include "net/base/url_util.h"
+#include "net/dns/host_resolver.h"
+#include "net/log/net_log_with_source.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/gurl.h"
+#include "url/scheme_host_port.h"
+#include "url/url_canon.h"
 
 namespace net {
 
@@ -25,6 +33,33 @@
 
 std::unique_ptr<HostResolver::ResolveHostRequest>
 MappedHostResolver::CreateRequest(
+    url::SchemeHostPort host,
+    NetworkIsolationKey network_isolation_key,
+    NetLogWithSource source_net_log,
+    absl::optional<ResolveHostParameters> optional_parameters) {
+  GURL rewritten_url = host.GetURL();
+  HostMappingRules::RewriteResult result = rules_.RewriteUrl(rewritten_url);
+
+  switch (result) {
+    case HostMappingRules::RewriteResult::kRewritten:
+      DCHECK(rewritten_url.is_valid());
+      DCHECK_NE(rewritten_url.host_piece(), "~NOTFOUND");
+      return impl_->CreateRequest(
+          url::SchemeHostPort(rewritten_url), std::move(network_isolation_key),
+          std::move(source_net_log), std::move(optional_parameters));
+    case HostMappingRules::RewriteResult::kInvalidRewrite:
+      // Treat any invalid mapping as if it was "~NOTFOUND" (which should itself
+      // result in `kInvalidRewrite`).
+      return CreateFailingRequest(ERR_NAME_NOT_RESOLVED);
+    case HostMappingRules::RewriteResult::kNoMatchingRule:
+      return impl_->CreateRequest(
+          std::move(host), std::move(network_isolation_key),
+          std::move(source_net_log), std::move(optional_parameters));
+  }
+}
+
+std::unique_ptr<HostResolver::ResolveHostRequest>
+MappedHostResolver::CreateRequest(
     const HostPortPair& host,
     const NetworkIsolationKey& network_isolation_key,
     const NetLogWithSource& source_net_log,
diff --git a/net/dns/mapped_host_resolver.h b/net/dns/mapped_host_resolver.h
index f27bb71..477f5848 100644
--- a/net/dns/mapped_host_resolver.h
+++ b/net/dns/mapped_host_resolver.h
@@ -12,8 +12,12 @@
 #include "net/base/completion_once_callback.h"
 #include "net/base/host_mapping_rules.h"
 #include "net/base/net_export.h"
+#include "net/base/network_isolation_key.h"
 #include "net/dns/dns_config.h"
 #include "net/dns/host_resolver.h"
+#include "net/log/net_log_with_source.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/scheme_host_port.h"
 
 namespace net {
 
@@ -51,6 +55,11 @@
 
   // HostResolver methods:
   std::unique_ptr<ResolveHostRequest> CreateRequest(
+      url::SchemeHostPort host,
+      NetworkIsolationKey network_isolation_key,
+      NetLogWithSource net_log,
+      absl::optional<ResolveHostParameters> optional_parameters) override;
+  std::unique_ptr<ResolveHostRequest> CreateRequest(
       const HostPortPair& host,
       const NetworkIsolationKey& network_isolation_key,
       const NetLogWithSource& net_log,
diff --git a/net/dns/mapped_host_resolver_unittest.cc b/net/dns/mapped_host_resolver_unittest.cc
index 8fb3c71f..d5ca366f 100644
--- a/net/dns/mapped_host_resolver_unittest.cc
+++ b/net/dns/mapped_host_resolver_unittest.cc
@@ -4,17 +4,24 @@
 
 #include "net/dns/mapped_host_resolver.h"
 
+#include <memory>
 #include <utility>
 
 #include "base/test/task_environment.h"
 #include "net/base/address_list.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
+#include "net/base/network_isolation_key.h"
 #include "net/base/test_completion_callback.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/log/net_log_with_source.h"
 #include "net/test/gtest_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/scheme_host_port.h"
+#include "url/url_constants.h"
 
 using net::test::IsError;
 using net::test::IsOk;
@@ -100,6 +107,138 @@
             FirstAddress(request->GetAddressResults().value()));
 }
 
+TEST(MappedHostResolverTest, MapsHostWithScheme) {
+  base::test::TaskEnvironment task_environment;
+
+  // Create a mock host resolver, with specific hostname to IP mappings.
+  std::unique_ptr<MockHostResolver> resolver_impl(new MockHostResolver());
+  resolver_impl->rules()->AddRule("remapped.test", "192.168.1.22");
+
+  // Create a remapped resolver that uses `resolver_impl`.
+  std::unique_ptr<MappedHostResolver> resolver(
+      new MappedHostResolver(std::move(resolver_impl)));
+  ASSERT_TRUE(resolver->AddRuleFromString("MAP to.map.test remapped.test"));
+
+  std::unique_ptr<HostResolver::ResolveHostRequest> request =
+      resolver->CreateRequest(
+          url::SchemeHostPort(url::kHttpScheme, "to.map.test", 155),
+          NetworkIsolationKey(), NetLogWithSource(), absl::nullopt);
+
+  TestCompletionCallback callback;
+  int rv = request->Start(callback.callback());
+
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  EXPECT_THAT(
+      request->GetAddressResults().value().endpoints(),
+      testing::ElementsAre(IPEndPoint(IPAddress(192, 168, 1, 22), 155)));
+}
+
+TEST(MappedHostResolverTest, MapsHostWithSchemeToIpLiteral) {
+  base::test::TaskEnvironment task_environment;
+
+  // Create a mock host resolver, with specific hostname to IP mappings.
+  std::unique_ptr<MockHostResolver> resolver_impl(new MockHostResolver());
+  resolver_impl->rules()->AddRule("host.test", "192.168.1.22");
+
+  // Create a remapped resolver that uses `resolver_impl`.
+  std::unique_ptr<MappedHostResolver> resolver(
+      new MappedHostResolver(std::move(resolver_impl)));
+  ASSERT_TRUE(resolver->AddRuleFromString("MAP host.test [1234:5678::000A]"));
+
+  IPAddress expected_address;
+  ASSERT_TRUE(expected_address.AssignFromIPLiteral("1234:5678::000A"));
+
+  std::unique_ptr<HostResolver::ResolveHostRequest> request =
+      resolver->CreateRequest(
+          url::SchemeHostPort(url::kHttpScheme, "host.test", 156),
+          NetworkIsolationKey(), NetLogWithSource(), absl::nullopt);
+
+  TestCompletionCallback callback;
+  int rv = request->Start(callback.callback());
+
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  EXPECT_THAT(request->GetAddressResults().value().endpoints(),
+              testing::ElementsAre(IPEndPoint(expected_address, 156)));
+}
+
+// Tests that remapped URL gets canonicalized when passing scheme.
+TEST(MappedHostResolverTest, MapsHostWithSchemeToNonCanon) {
+  base::test::TaskEnvironment task_environment;
+
+  // Create a mock host resolver, with specific hostname to IP mappings.
+  std::unique_ptr<MockHostResolver> resolver_impl(new MockHostResolver());
+  resolver_impl->rules()->AddRule("remapped.test", "192.168.1.23");
+
+  // Create a remapped resolver that uses `resolver_impl`.
+  std::unique_ptr<MappedHostResolver> resolver(
+      new MappedHostResolver(std::move(resolver_impl)));
+  ASSERT_TRUE(resolver->AddRuleFromString("MAP host.test reMapped.TEST"));
+
+  std::unique_ptr<HostResolver::ResolveHostRequest> request =
+      resolver->CreateRequest(
+          url::SchemeHostPort(url::kHttpScheme, "host.test", 157),
+          NetworkIsolationKey(), NetLogWithSource(), absl::nullopt);
+
+  TestCompletionCallback callback;
+  int rv = request->Start(callback.callback());
+
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  EXPECT_THAT(
+      request->GetAddressResults().value().endpoints(),
+      testing::ElementsAre(IPEndPoint(IPAddress(192, 168, 1, 23), 157)));
+}
+
+TEST(MappedHostResolverTest, MapsHostWithSchemeToNameWithPort) {
+  base::test::TaskEnvironment task_environment;
+
+  // Create a mock host resolver, with specific hostname to IP mappings.
+  std::unique_ptr<MockHostResolver> resolver_impl(new MockHostResolver());
+  resolver_impl->rules()->AddRule("remapped.test", "192.168.1.24");
+
+  // Create a remapped resolver that uses `resolver_impl`.
+  std::unique_ptr<MappedHostResolver> resolver(
+      new MappedHostResolver(std::move(resolver_impl)));
+  ASSERT_TRUE(resolver->AddRuleFromString("MAP host.test remapped.test:258"));
+
+  std::unique_ptr<HostResolver::ResolveHostRequest> request =
+      resolver->CreateRequest(
+          url::SchemeHostPort(url::kHttpScheme, "host.test", 158),
+          NetworkIsolationKey(), NetLogWithSource(), absl::nullopt);
+
+  TestCompletionCallback callback;
+  int rv = request->Start(callback.callback());
+
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  EXPECT_THAT(
+      request->GetAddressResults().value().endpoints(),
+      testing::ElementsAre(IPEndPoint(IPAddress(192, 168, 1, 24), 258)));
+}
+
+TEST(MappedHostResolverTest, HandlesUnmappedHostWithScheme) {
+  base::test::TaskEnvironment task_environment;
+
+  // Create a mock host resolver, with specific hostname to IP mappings.
+  std::unique_ptr<MockHostResolver> resolver_impl(new MockHostResolver());
+  resolver_impl->rules()->AddRule("unmapped.test", "192.168.1.23");
+
+  // Create a remapped resolver that uses `resolver_impl`.
+  std::unique_ptr<MappedHostResolver> resolver(
+      new MappedHostResolver(std::move(resolver_impl)));
+
+  std::unique_ptr<HostResolver::ResolveHostRequest> request =
+      resolver->CreateRequest(
+          url::SchemeHostPort(url::kHttpsScheme, "unmapped.test", 155),
+          NetworkIsolationKey(), NetLogWithSource(), absl::nullopt);
+
+  TestCompletionCallback callback;
+  int rv = request->Start(callback.callback());
+
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  EXPECT_THAT(
+      request->GetAddressResults().value().endpoints(),
+      testing::ElementsAre(IPEndPoint(IPAddress(192, 168, 1, 23), 155)));
+}
+
 // Tests that exclusions are respected.
 TEST(MappedHostResolverTest, Exclusion) {
   base::test::TaskEnvironment task_environment;
@@ -242,6 +381,28 @@
             FirstAddress(request->GetAddressResults().value()));
 }
 
+TEST(MappedHostResolverTest, MapHostWithSchemeToError) {
+  base::test::TaskEnvironment task_environment;
+
+  // Create a mock host resolver, with specific hostname to IP mappings.
+  std::unique_ptr<MockHostResolver> resolver_impl(new MockHostResolver());
+  resolver_impl->rules()->AddRule("host.test", "192.168.1.25");
+
+  // Create a remapped resolver that uses `resolver_impl`.
+  std::unique_ptr<MappedHostResolver> resolver(
+      new MappedHostResolver(std::move(resolver_impl)));
+  ASSERT_TRUE(resolver->AddRuleFromString("MAP host.test ~NOTFOUND"));
+
+  std::unique_ptr<HostResolver::ResolveHostRequest> request =
+      resolver->CreateRequest(
+          url::SchemeHostPort(url::kWssScheme, "host.test", 155),
+          NetworkIsolationKey(), NetLogWithSource(), absl::nullopt);
+
+  TestCompletionCallback callback;
+  int rv = request->Start(callback.callback());
+  EXPECT_THAT(callback.GetResult(rv), IsError(ERR_NAME_NOT_RESOLVED));
+}
+
 }  // namespace
 
 }  // namespace net
diff --git a/net/dns/mock_host_resolver.cc b/net/dns/mock_host_resolver.cc
index 072373a..a6aae7f 100644
--- a/net/dns/mock_host_resolver.cc
+++ b/net/dns/mock_host_resolver.cc
@@ -29,12 +29,16 @@
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
+#include "net/base/network_isolation_key.h"
 #include "net/base/test_completion_callback.h"
 #include "net/dns/dns_alias_utility.h"
 #include "net/dns/host_cache.h"
 #include "net/dns/public/resolve_error_info.h"
 #include "net/dns/public/secure_dns_policy.h"
+#include "net/log/net_log_with_source.h"
 #include "net/url_request/url_request_context.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/scheme_host_port.h"
 
 #if defined(OS_WIN)
 #include "net/base/winsock_init.h"
@@ -372,6 +376,17 @@
 
 std::unique_ptr<HostResolver::ResolveHostRequest>
 MockHostResolverBase::CreateRequest(
+    url::SchemeHostPort host,
+    NetworkIsolationKey network_isolation_key,
+    NetLogWithSource net_log,
+    absl::optional<ResolveHostParameters> optional_parameters) {
+  // TODO(crbug.com/1206799): Propagate scheme and make affect behavior.
+  return CreateRequest(HostPortPair::FromSchemeHostPort(host),
+                       network_isolation_key, net_log, optional_parameters);
+}
+
+std::unique_ptr<HostResolver::ResolveHostRequest>
+MockHostResolverBase::CreateRequest(
     const HostPortPair& host,
     const NetworkIsolationKey& network_isolation_key,
     const NetLogWithSource& source_net_log,
@@ -1108,6 +1123,17 @@
 
 std::unique_ptr<HostResolver::ResolveHostRequest>
 HangingHostResolver::CreateRequest(
+    url::SchemeHostPort host,
+    NetworkIsolationKey network_isolation_key,
+    NetLogWithSource net_log,
+    absl::optional<ResolveHostParameters> optional_parameters) {
+  // TODO(crbug.com/1206799): Propagate scheme and make affect behavior.
+  return CreateRequest(HostPortPair::FromSchemeHostPort(host),
+                       network_isolation_key, net_log, optional_parameters);
+}
+
+std::unique_ptr<HostResolver::ResolveHostRequest>
+HangingHostResolver::CreateRequest(
     const HostPortPair& host,
     const NetworkIsolationKey& network_isolation_key,
     const NetLogWithSource& source_net_log,
diff --git a/net/dns/mock_host_resolver.h b/net/dns/mock_host_resolver.h
index 1b121465..a258446 100644
--- a/net/dns/mock_host_resolver.h
+++ b/net/dns/mock_host_resolver.h
@@ -27,6 +27,9 @@
 #include "net/dns/host_resolver_source.h"
 #include "net/dns/public/dns_query_type.h"
 #include "net/dns/public/secure_dns_policy.h"
+#include "net/log/net_log_with_source.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/scheme_host_port.h"
 
 namespace base {
 class TickClock;
@@ -122,6 +125,11 @@
   // HostResolver methods:
   void OnShutdown() override;
   std::unique_ptr<ResolveHostRequest> CreateRequest(
+      url::SchemeHostPort host,
+      NetworkIsolationKey network_isolation_key,
+      NetLogWithSource net_log,
+      absl::optional<ResolveHostParameters> optional_parameters) override;
+  std::unique_ptr<ResolveHostRequest> CreateRequest(
       const HostPortPair& host,
       const NetworkIsolationKey& network_isolation_key,
       const NetLogWithSource& net_log,
@@ -507,6 +515,11 @@
   ~HangingHostResolver() override;
   void OnShutdown() override;
   std::unique_ptr<ResolveHostRequest> CreateRequest(
+      url::SchemeHostPort host,
+      NetworkIsolationKey network_isolation_key,
+      NetLogWithSource net_log,
+      absl::optional<ResolveHostParameters> optional_parameters) override;
+  std::unique_ptr<ResolveHostRequest> CreateRequest(
       const HostPortPair& host,
       const NetworkIsolationKey& network_isolation_key,
       const NetLogWithSource& net_log,
diff --git a/net/http/http_proxy_connect_job.cc b/net/http/http_proxy_connect_job.cc
index 20017094..2cb7225 100644
--- a/net/http/http_proxy_connect_job.cc
+++ b/net/http/http_proxy_connect_job.cc
@@ -12,7 +12,6 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/no_destructor.h"
 #include "base/numerics/ranges.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -125,9 +124,8 @@
 };
 
 HttpProxyTimeoutExperiments* GetProxyTimeoutExperiments() {
-  static base::NoDestructor<HttpProxyTimeoutExperiments>
-      proxy_timeout_experiments;
-  return proxy_timeout_experiments.get();
+  static HttpProxyTimeoutExperiments proxy_timeout_experiments;
+  return &proxy_timeout_experiments;
 }
 
 }  // namespace
diff --git a/net/quic/platform/impl/DEPS b/net/quic/platform/impl/DEPS
index fbe3824..2c12fec5 100644
--- a/net/quic/platform/impl/DEPS
+++ b/net/quic/platform/impl/DEPS
@@ -1,8 +1,4 @@
 include_rules = [
-  # This is a temporary rule to simplify migrating QUICHE to using Abseil
-  # directly.
-  # TODO(b/166325009): remove this rule.
-  "+third_party/abseil-cpp/absl/base",
-  "+third_party/abseil-cpp/absl/container",
+  # Allow string_view.h since absl::string_view is widely used in QUICHE API.
   "+third_party/abseil-cpp/absl/strings",
 ]
diff --git a/net/quic/platform/impl/quic_flags_impl.h b/net/quic/platform/impl/quic_flags_impl.h
index 6c50b2c..5553084 100644
--- a/net/quic/platform/impl/quic_flags_impl.h
+++ b/net/quic/platform/impl/quic_flags_impl.h
@@ -11,7 +11,9 @@
 #include <string>
 #include <vector>
 
+#include "base/command_line.h"
 #include "base/export_template.h"
+#include "base/no_destructor.h"
 #include "net/third_party/quiche/src/common/platform/api/quiche_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -21,12 +23,6 @@
 #include "net/third_party/quiche/src/quic/core/quic_protocol_flags_list.h"
 #undef QUIC_PROTOCOL_FLAG
 
-namespace base {
-class CommandLine;
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 // Sets the flag named |flag_name| to the value of |value| after converting
 // it from a string to the appropriate type. If |value| is invalid or out of
 // range, the flag will be unchanged.
diff --git a/net/quiche/common/platform/impl/DEPS b/net/quiche/common/platform/impl/DEPS
index c65e978..2c12fec5 100644
--- a/net/quiche/common/platform/impl/DEPS
+++ b/net/quiche/common/platform/impl/DEPS
@@ -1,9 +1,4 @@
 include_rules = [
-  # This is a temporary rule to simplify migrating QUICHE to using Abseil
-  # directly.
-  # TODO(b/166325009): remove this rule.
-  "+third_party/abseil-cpp/absl/base/macros.h",
-  "+third_party/abseil-cpp/absl/container/node_hash_map.h",
-  "+third_party/abseil-cpp/absl/hash/hash.h",
+  # Allow string_view.h since absl::string_view is widely used in QUICHE API.
   "+third_party/abseil-cpp/absl/strings",
 ]
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index 8f94e48..9aa5e39 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -464,15 +464,15 @@
   PDFiumFormFiller::ScriptOption script_option =
       PDFiumFormFiller::DefaultScriptOption();
   bool has_edits = false;
-  const char* stream_url = nullptr;
+  const char* src_url = nullptr;
   const char* original_url = nullptr;
   const char* top_level_url = nullptr;
   const char* headers = nullptr;
   for (uint32_t i = 0; i < argc; ++i) {
-    if (strcmp(argn[i], "src") == 0) {
+    if (strcmp(argn[i], "original-url") == 0) {
       original_url = argv[i];
-    } else if (strcmp(argn[i], "stream-url") == 0) {
-      stream_url = argv[i];
+    } else if (strcmp(argn[i], "src") == 0) {
+      src_url = argv[i];
     } else if (strcmp(argn[i], "top-level-url") == 0) {
       top_level_url = argv[i];
     } else if (strcmp(argn[i], "headers") == 0) {
@@ -492,11 +492,11 @@
     }
   }
 
-  if (!original_url)
+  if (!src_url)
     return false;
 
-  if (!stream_url)
-    stream_url = original_url;
+  if (!original_url)
+    original_url = src_url;
 
   InitializeEngine(std::make_unique<PDFiumEngine>(this, script_option));
 
@@ -507,7 +507,7 @@
   if (IsPrintPreview())
     return true;
 
-  LoadUrl(stream_url, /*is_print_preview=*/false);
+  LoadUrl(src_url, /*is_print_preview=*/false);
   set_url(original_url);
 
   // Not all edits go through the PDF plugin's form filler. The plugin instance
diff --git a/pdf/parsed_params.cc b/pdf/parsed_params.cc
index 898777b..46cada1 100644
--- a/pdf/parsed_params.cc
+++ b/pdf/parsed_params.cc
@@ -22,10 +22,10 @@
     const blink::WebPluginParams& params) {
   ParsedParams result;
   for (size_t i = 0; i < params.attribute_names.size(); ++i) {
-    if (params.attribute_names[i] == "src") {
+    if (params.attribute_names[i] == "original-url") {
       result.original_url = params.attribute_values[i].Utf8();
-    } else if (params.attribute_names[i] == "stream-url") {
-      result.stream_url = params.attribute_values[i].Utf8();
+    } else if (params.attribute_names[i] == "src") {
+      result.src_url = params.attribute_values[i].Utf8();
     } else if (params.attribute_names[i] == "full-frame") {
       result.full_frame = true;
     } else if (params.attribute_names[i] == "background-color") {
@@ -38,11 +38,12 @@
     }
   }
 
-  if (result.original_url.empty())
+  if (result.src_url.empty())
     return absl::nullopt;
 
-  if (result.stream_url.empty())
-    result.stream_url = result.original_url;
+  if (result.original_url.empty()) {
+    result.original_url = result.src_url;
+  }
 
   return result;
 }
diff --git a/pdf/parsed_params.h b/pdf/parsed_params.h
index acc03655..cf08d03 100644
--- a/pdf/parsed_params.h
+++ b/pdf/parsed_params.h
@@ -21,11 +21,11 @@
   ParsedParams(const ParsedParams& other);
   ~ParsedParams();
 
-  // Document URL. Must not be empty.
+  // The document original URL. Must not be empty.
   std::string original_url;
 
-  // Document stream URL. Must not be empty.
-  std::string stream_url;
+  // The plugin source URL. Must not be empty.
+  std::string src_url;
 
   // The background color for the PDF viewer.
   absl::optional<SkColor> background_color;
diff --git a/pdf/parsed_params_unittest.cc b/pdf/parsed_params_unittest.cc
index e09a917..99f735b 100644
--- a/pdf/parsed_params_unittest.cc
+++ b/pdf/parsed_params_unittest.cc
@@ -18,7 +18,7 @@
 namespace {
 
 constexpr char kDummyOriginalUrl[] = "https://test.com/dummy.pdf";
-constexpr char kDummyStreamUrl[] = "chrome-extension://dummy-stream-url";
+constexpr char kDummySrcUrl[] = "chrome-extension://dummy-source-url";
 
 constexpr SkColor kNewBackgroundColor = SkColorSetARGB(0xFF, 0x52, 0x56, 0x59);
 
@@ -26,8 +26,8 @@
 constexpr char kNewBackgroundColorStr[] = "4283586137";
 
 // Creates a `blink::WebPluginParams` without any URL attributes, namely "src"
-// and "stream-url". The return value only contains valid "background-color" and
-// "full-frame" attributes.
+// and "original-url". The return value only contains valid "background-color"
+// and "full-frame" attributes.
 blink::WebPluginParams CreateWebPluginParamsWithoutUrl() {
   blink::WebPluginParams params;
   params.attribute_names.push_back(blink::WebString("background-color"));
@@ -38,13 +38,13 @@
 }
 
 // Creates a `blink::WebPluginParams` with only the URL attributes: "src" and
-// "stream-url".
+// "original-url".
 blink::WebPluginParams CreateWebPluginParamsWithUrls() {
   blink::WebPluginParams params;
-  params.attribute_names.push_back(blink::WebString("src"));
+  params.attribute_names.push_back(blink::WebString("original-url"));
   params.attribute_values.push_back(blink::WebString(kDummyOriginalUrl));
-  params.attribute_names.push_back(blink::WebString("stream-url"));
-  params.attribute_values.push_back(blink::WebString(kDummyStreamUrl));
+  params.attribute_names.push_back(blink::WebString("src"));
+  params.attribute_values.push_back(blink::WebString(kDummySrcUrl));
   return params;
 }
 
@@ -52,41 +52,41 @@
 
 TEST(ParsedParamsTest, ParseValidWebPluginParams) {
   blink::WebPluginParams params = CreateWebPluginParamsWithoutUrl();
-  params.attribute_names.push_back(blink::WebString("src"));
+  params.attribute_names.push_back(blink::WebString("original-url"));
   params.attribute_values.push_back(blink::WebString(kDummyOriginalUrl));
-  params.attribute_names.push_back(blink::WebString("stream-url"));
-  params.attribute_values.push_back(blink::WebString(kDummyStreamUrl));
+  params.attribute_names.push_back(blink::WebString("src"));
+  params.attribute_values.push_back(blink::WebString(kDummySrcUrl));
 
   absl::optional<ParsedParams> result = ParseWebPluginParams(params);
   ASSERT_TRUE(result.has_value());
   EXPECT_EQ(kDummyOriginalUrl, result->original_url);
-  EXPECT_EQ(kDummyStreamUrl, result->stream_url);
+  EXPECT_EQ(kDummySrcUrl, result->src_url);
   ASSERT_TRUE(result->background_color.has_value());
   EXPECT_EQ(kNewBackgroundColor, result->background_color.value());
   EXPECT_TRUE(result->full_frame);
 }
 
-TEST(ParsedParamsTest, ParseWebPluginParamsWithoutOriginalUrl) {
+TEST(ParsedParamsTest, ParseWebPluginParamsWithoutSourceUrl) {
   blink::WebPluginParams params = CreateWebPluginParamsWithoutUrl();
-  params.attribute_names.push_back(blink::WebString("stream-url"));
-  params.attribute_values.push_back(blink::WebString(kDummyStreamUrl));
+  params.attribute_names.push_back(blink::WebString("original-url"));
+  params.attribute_values.push_back(blink::WebString(kDummyOriginalUrl));
 
-  // Expect the `ParsedParams` to be invalid due to missing the original URL.
+  // Expect the `ParsedParams` to be invalid due to missing the source URL.
   absl::optional<ParsedParams> result = ParseWebPluginParams(params);
   EXPECT_FALSE(result.has_value());
 }
 
-TEST(ParseParsedParamsTest, ParseWebPluginParamsWithoutStreamUrl) {
+TEST(ParseParsedParamsTest, ParseWebPluginParamsWithoutOriginalUrl) {
   blink::WebPluginParams params = CreateWebPluginParamsWithoutUrl();
   params.attribute_names.push_back(blink::WebString("src"));
-  params.attribute_values.push_back(blink::WebString(kDummyOriginalUrl));
+  params.attribute_values.push_back(blink::WebString(kDummySrcUrl));
 
-  // Expect the `ParsedParams` to be valid and `stream_url` to be the same as
-  // `original_url`.
+  // Expect the `ParsedParams` to be valid and `original_url` to be the same as
+  // `src_url`.
   absl::optional<ParsedParams> result = ParseWebPluginParams(params);
   ASSERT_TRUE(result.has_value());
-  EXPECT_EQ(kDummyOriginalUrl, result->original_url);
-  EXPECT_EQ(kDummyOriginalUrl, result->stream_url);
+  EXPECT_EQ(kDummySrcUrl, result->original_url);
+  EXPECT_EQ(kDummySrcUrl, result->src_url);
   ASSERT_TRUE(result->background_color.has_value());
   EXPECT_EQ(kNewBackgroundColor, result->background_color.value());
   EXPECT_TRUE(result->full_frame);
@@ -100,7 +100,7 @@
   absl::optional<ParsedParams> result = ParseWebPluginParams(params);
   ASSERT_TRUE(result.has_value());
   EXPECT_EQ(kDummyOriginalUrl, result->original_url);
-  EXPECT_EQ(kDummyStreamUrl, result->stream_url);
+  EXPECT_EQ(kDummySrcUrl, result->src_url);
   EXPECT_FALSE(result->background_color.has_value());
   EXPECT_FALSE(result->full_frame);
 }
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc
index 2e54aae..60e43deb 100644
--- a/pdf/pdf_view_web_plugin.cc
+++ b/pdf/pdf_view_web_plugin.cc
@@ -87,7 +87,9 @@
 class PerProcessInitializer final {
  public:
   static PerProcessInitializer& GetInstance() {
-    static base::NoDestructor<PerProcessInitializer> instance;
+    static base::NoDestructor<PerProcessInitializer,
+                              base::AllowForTriviallyDestructibleType>
+        instance;
     return *instance;
   }
 
@@ -262,7 +264,7 @@
   PerProcessInitializer::GetInstance().Acquire();
   InitializeEngine(std::make_unique<PDFiumEngine>(
       this, PDFiumFormFiller::ScriptOption::kNoJavaScript));
-  LoadUrl(params->stream_url, /*is_print_preview=*/false);
+  LoadUrl(params->src_url, /*is_print_preview=*/false);
   set_url(params->original_url);
   post_message_sender_.set_container(Container());
   return true;
diff --git a/ppapi/native_client/src/untrusted/pnacl_irt_shim/BUILD.gn b/ppapi/native_client/src/untrusted/pnacl_irt_shim/BUILD.gn
index 756d075..2526718 100644
--- a/ppapi/native_client/src/untrusted/pnacl_irt_shim/BUILD.gn
+++ b/ppapi/native_client/src/untrusted/pnacl_irt_shim/BUILD.gn
@@ -14,10 +14,6 @@
     "shim_entry.c",
     "shim_ppapi.c",
   ]
-  deps = [
-    "//ppapi/c",
-    "//ppapi/proxy:proxy",
-  ]
 
   # Indicate that this variant of the shim library should not depend on
   # the unstable/private IRT hook interface.
@@ -45,8 +41,4 @@
     "irt_shim_ppapi.c",
     "pnacl_shim.c",
   ]
-  deps = [
-    "//ppapi/c",
-    "//ppapi/proxy:proxy",
-  ]
 }
diff --git a/printing/mojom/print.mojom b/printing/mojom/print.mojom
index 69130608..0e0b8ae 100644
--- a/printing/mojom/print.mojom
+++ b/printing/mojom/print.mojom
@@ -146,3 +146,12 @@
   kLocal,
   kCloud
 };
+
+[EnableIf=is_win]
+enum PrinterLanguageType {
+  kNone = 0,
+  kTextOnly,
+  kXps,
+  kPostscriptLevel2,
+  kPostscriptLevel3,
+};
diff --git a/printing/print_settings.cc b/printing/print_settings.cc
index bd3b3d9..b12f1f5 100644
--- a/printing/print_settings.cc
+++ b/printing/print_settings.cc
@@ -14,6 +14,10 @@
 #include <cups/cups.h>
 #endif
 
+#if defined(OS_WIN)
+#include "printing/mojom/print.mojom.h"
+#endif
+
 namespace printing {
 
 namespace {
@@ -274,7 +278,7 @@
   supports_alpha_blend_ = true;
 #if defined(OS_WIN)
   print_text_with_gdi_ = false;
-  printer_type_ = PrintSettings::PrinterType::TYPE_NONE;
+  printer_language_type_ = mojom::PrinterLanguageType::kNone;
 #endif
   is_modifiable_ = true;
   pages_per_sheet_ = 1;
diff --git a/printing/print_settings.h b/printing/print_settings.h
index 451632c..9592024 100644
--- a/printing/print_settings.h
+++ b/printing/print_settings.h
@@ -59,16 +59,6 @@
 
 class COMPONENT_EXPORT(PRINTING) PrintSettings {
  public:
-#if defined(OS_WIN)
-  enum PrinterType {
-    TYPE_NONE = 0,
-    TYPE_TEXTONLY,
-    TYPE_XPS,
-    TYPE_POSTSCRIPT_LEVEL2,
-    TYPE_POSTSCRIPT_LEVEL3
-  };
-#endif
-
   // Media properties requested by the user. Default instance represents
   // default media selection.
   struct RequestedMedia {
@@ -202,16 +192,22 @@
   void set_print_text_with_gdi(bool use_gdi) { print_text_with_gdi_ = use_gdi; }
   bool print_text_with_gdi() const { return print_text_with_gdi_; }
 
-  void set_printer_type(PrinterType type) { printer_type_ = type; }
-  bool printer_is_textonly() const {
-    return printer_type_ == PrinterType::TYPE_TEXTONLY;
+  void set_printer_language_type(mojom::PrinterLanguageType type) {
+    printer_language_type_ = type;
   }
-  bool printer_is_xps() const { return printer_type_ == PrinterType::TYPE_XPS; }
-  bool printer_is_ps2() const {
-    return printer_type_ == PrinterType::TYPE_POSTSCRIPT_LEVEL2;
+  bool printer_language_is_textonly() const {
+    return printer_language_type_ == mojom::PrinterLanguageType::kTextOnly;
   }
-  bool printer_is_ps3() const {
-    return printer_type_ == PrinterType::TYPE_POSTSCRIPT_LEVEL3;
+  bool printer_language_is_xps() const {
+    return printer_language_type_ == mojom::PrinterLanguageType::kXps;
+  }
+  bool printer_language_is_ps2() const {
+    return printer_language_type_ ==
+           mojom::PrinterLanguageType::kPostscriptLevel2;
+  }
+  bool printer_language_is_ps3() const {
+    return printer_language_type_ ==
+           mojom::PrinterLanguageType::kPostscriptLevel3;
   }
 #endif
 
@@ -243,13 +239,13 @@
   const std::string& pin_value() const { return pin_value_; }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-  // Cookie generator. It is used to initialize PrintedDocument with its
-  // associated PrintSettings, to be sure that each generated PrintedPage is
-  // correctly associated with its corresponding PrintedDocument.
+  // Cookie generator. It is used to initialize `PrintedDocument` with its
+  // associated `PrintSettings`, to be sure that each generated `PrintedPage`
+  // is correctly associated with its corresponding `PrintedDocument`.
   static int NewCookie();
 
  private:
-  // Multi-page printing. Each PageRange describes a from-to page combination.
+  // Multi-page printing. Each `PageRange` describes a from-to page combination.
   // This permits printing selected pages only.
   PageRanges ranges_;
 
@@ -311,7 +307,7 @@
   // True to print text with GDI.
   bool print_text_with_gdi_;
 
-  PrinterType printer_type_;
+  mojom::PrinterLanguageType printer_language_type_;
 #endif
 
   bool is_modifiable_;
diff --git a/printing/print_settings_initializer_win.cc b/printing/print_settings_initializer_win.cc
index 9c1dc4fc9..84a4a4f1 100644
--- a/printing/print_settings_initializer_win.cc
+++ b/printing/print_settings_initializer_win.cc
@@ -7,6 +7,7 @@
 #include <windows.h>
 
 #include "printing/backend/win_helper.h"
+#include "printing/mojom/print.mojom.h"
 #include "printing/print_settings.h"
 
 namespace printing {
@@ -151,25 +152,26 @@
   int level;
   if (IsPrinterPostScript(hdc, &level)) {
     if (level == 2) {
-      print_settings->set_printer_type(
-          PrintSettings::PrinterType::TYPE_POSTSCRIPT_LEVEL2);
+      print_settings->set_printer_language_type(
+          mojom::PrinterLanguageType::kPostscriptLevel2);
       return;
     }
     DCHECK_EQ(3, level);
-    print_settings->set_printer_type(
-        PrintSettings::PrinterType::TYPE_POSTSCRIPT_LEVEL3);
+    print_settings->set_printer_language_type(
+        mojom::PrinterLanguageType::kPostscriptLevel3);
     return;
   }
   // Detects the generic / text only driver.
   if (IsPrinterTextOnly(hdc)) {
-    print_settings->set_printer_type(PrintSettings::PrinterType::TYPE_TEXTONLY);
+    print_settings->set_printer_language_type(
+        mojom::PrinterLanguageType::kTextOnly);
     return;
   }
   if (IsPrinterXPS(hdc)) {
-    print_settings->set_printer_type(PrintSettings::PrinterType::TYPE_XPS);
+    print_settings->set_printer_language_type(mojom::PrinterLanguageType::kXps);
     return;
   }
-  print_settings->set_printer_type(PrintSettings::PrinterType::TYPE_NONE);
+  print_settings->set_printer_language_type(mojom::PrinterLanguageType::kNone);
 }
 
 }  // namespace printing
diff --git a/remoting/client/input/keycode_map.cc b/remoting/client/input/keycode_map.cc
index 4302a46..9dc665a 100644
--- a/remoting/client/input/keycode_map.cc
+++ b/remoting/client/input/keycode_map.cc
@@ -109,9 +109,9 @@
 }
 
 const KeycodeMap& GetKeycodeMapQwerty() {
-  static const base::NoDestructor<KeycodeMap> map(
+  static const KeycodeMap map(
       CreateKeycodeMapFromMapEntries(kKeycodeMapEntriesQwerty));
-  return *map;
+  return map;
 }
 
 }  // namespace
diff --git a/remoting/host/desktop_session_win.cc b/remoting/host/desktop_session_win.cc
index e1180f3..bedb335 100644
--- a/remoting/host/desktop_session_win.cc
+++ b/remoting/host/desktop_session_win.cc
@@ -72,14 +72,34 @@
 const int kDefaultRdpScreenWidth = 1280;
 const int kDefaultRdpScreenHeight = 768;
 
-// RDC 6.1 (W2K8) supports dimensions of up to 4096x2048.
-const int kMaxRdpScreenWidth = 4096;
-const int kMaxRdpScreenHeight = 2048;
-
 // The minimum effective screen dimensions supported by Windows are 800x600.
 const int kMinRdpScreenWidth = 800;
 const int kMinRdpScreenHeight = 600;
 
+// Win7 SP1 (and Vista) supports dimensions up to 4096x2048.
+const int kMaxRdpScreenWidthForWin7 = 4096;
+const int kMaxRdpScreenHeightForWin7 = 2048;
+
+// Win8+ supports dimensions up to 8192x8192.
+const int kMaxRdpScreenWidthForWin8AndLater = 8192;
+const int kMaxRdpScreenHeightForWin8AndLater = 8192;
+
+int GetMaxRdpScreenWidth() {
+  static int max_rdp_screen_width =
+      base::win::GetVersion() >= base::win::Version::WIN8
+          ? kMaxRdpScreenWidthForWin8AndLater
+          : kMaxRdpScreenWidthForWin7;
+  return max_rdp_screen_width;
+}
+
+int GetMaxRdpScreenHeight() {
+  static int max_rdp_screen_height =
+      base::win::GetVersion() >= base::win::Version::WIN8
+          ? kMaxRdpScreenHeightForWin8AndLater
+          : kMaxRdpScreenHeightForWin7;
+  return max_rdp_screen_height;
+}
+
 // Default dots per inch used by RDP is 96 DPI.
 const int kDefaultRdpDpi = 96;
 
@@ -106,8 +126,8 @@
 
 webrtc::DesktopSize GetBoundedRdpDesktopSize(int width, int height) {
   return webrtc::DesktopSize(
-      base::ClampToRange(width, kMinRdpScreenWidth, kMaxRdpScreenWidth),
-      base::ClampToRange(height, kMinRdpScreenHeight, kMaxRdpScreenHeight));
+      base::ClampToRange(width, kMinRdpScreenWidth, GetMaxRdpScreenWidth()),
+      base::ClampToRange(height, kMinRdpScreenHeight, GetMaxRdpScreenHeight()));
 }
 
 // DesktopSession implementation which attaches to the host's physical console.
diff --git a/remoting/host/security_key/BUILD.gn b/remoting/host/security_key/BUILD.gn
index 49bb2b3e..b9c55c5 100644
--- a/remoting/host/security_key/BUILD.gn
+++ b/remoting/host/security_key/BUILD.gn
@@ -37,7 +37,10 @@
     "//ipc",
     "//mojo/public/cpp/platform",
     "//mojo/public/cpp/system",
+    "//net:net",
+    "//net/traffic_annotation:traffic_annotation",
     "//remoting/proto",
+    "//remoting/protocol:protocol",
     "//third_party/webrtc_overrides:webrtc_component",
   ]
 
@@ -62,8 +65,11 @@
     "remote_security_key_main.h",
   ]
   deps = [
+    ":security_key",
     "//base:debugging_buildflags",
     "//mojo/core/embedder",
+    "//remoting/host:base",
+    "//remoting/host:common",
   ]
 }
 
@@ -101,8 +107,16 @@
 
   public_deps = [ ":test_support" ]
   deps = [
+    ":security_key",
     "//mojo/core/test:test_support",
+    "//net:net",
+    "//net:test_support",
+    "//net/traffic_annotation:test_support",
     "//remoting:test_support",
+    "//remoting/host:common",
+    "//remoting/host:test_support",
+    "//remoting/host/setup:common",
+    "//remoting/protocol:protocol",
   ]
 
   if (is_posix) {
@@ -129,6 +143,7 @@
   ]
 
   deps = [
+    ":security_key",
     "//ipc",
     "//remoting/proto",
     "//testing/gtest",
diff --git a/sandbox/policy/linux/bpf_libassistant_policy_linux.cc b/sandbox/policy/linux/bpf_libassistant_policy_linux.cc
index a00dd4fd..f235efa 100644
--- a/sandbox/policy/linux/bpf_libassistant_policy_linux.cc
+++ b/sandbox/policy/linux/bpf_libassistant_policy_linux.cc
@@ -24,16 +24,23 @@
 LibassistantProcessPolicy::~LibassistantProcessPolicy() = default;
 
 ResultExpr LibassistantProcessPolicy::EvaluateSyscall(int sysno) const {
-#if defined(__NR_sched_setscheduler)
-  if (sysno == __NR_sched_setscheduler)
-    return Allow();
+  switch (sysno) {
+#if defined(__NR_getcpu)
+    // Needed by arm devices.
+    case __NR_getcpu:
+      return Allow();
 #endif
+#if defined(__NR_sched_setscheduler)
+    case __NR_sched_setscheduler:
+      return Allow();
+#endif
+    default:
+      auto* sandbox_linux = SandboxLinux::GetInstance();
+      if (sandbox_linux->ShouldBrokerHandleSyscall(sysno))
+        return sandbox_linux->HandleViaBroker();
 
-  auto* sandbox_linux = SandboxLinux::GetInstance();
-  if (sandbox_linux->ShouldBrokerHandleSyscall(sysno))
-    return sandbox_linux->HandleViaBroker();
-
-  return BPFBasePolicy::EvaluateSyscall(sysno);
+      return BPFBasePolicy::EvaluateSyscall(sysno);
+  }
 }
 
 }  // namespace policy
diff --git a/services/device/public/cpp/hid/hid_blocklist.h b/services/device/public/cpp/hid/hid_blocklist.h
index e0d145f..40af590 100644
--- a/services/device/public/cpp/hid/hid_blocklist.h
+++ b/services/device/public/cpp/hid/hid_blocklist.h
@@ -5,13 +5,9 @@
 #ifndef SERVICES_DEVICE_PUBLIC_CPP_HID_HID_BLOCKLIST_H_
 #define SERVICES_DEVICE_PUBLIC_CPP_HID_HID_BLOCKLIST_H_
 
+#include "base/no_destructor.h"
 #include "services/device/public/mojom/hid.mojom.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 namespace device {
 
 class HidBlocklist final {
diff --git a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
index ed1a9c72..576df0c 100644
--- a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
+++ b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
@@ -220,10 +220,8 @@
 
 base::SequenceLocalStorageSlot<TracingSamplerProfiler>&
 GetSequenceLocalStorageProfilerSlot() {
-  static base::NoDestructor<
-      base::SequenceLocalStorageSlot<TracingSamplerProfiler>>
-      storage;
-  return *storage;
+  static base::SequenceLocalStorageSlot<TracingSamplerProfiler> storage;
+  return storage;
 }
 
 // Stores information about the StackFrame, to emit to the trace.
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index fd28d962..1ab125d 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -6824,7 +6824,7 @@
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 9
+          "shards": 15
         },
         "test": "content_browsertests",
         "test_id_prefix": "ninja://content/test:content_browsertests/"
@@ -14028,7 +14028,7 @@
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 9
+          "shards": 15
         },
         "test": "content_browsertests",
         "test_id_prefix": "ninja://content/test:content_browsertests/"
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index eb3f0bc2..aadf44e 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1216,11 +1216,17 @@
         'args': [
           '--test-launcher-filter-file=../../testing/buildbot/filters/android.lollipop_tablet_tester.content_browsertests.filter',
         ],
+        'swarming': {
+          'shards': 15,
+        },
       },
       'Marshmallow Tablet Tester': {
         'args': [
           '--test-launcher-filter-file=../../testing/buildbot/filters/android.marshmallow_tablet_tester.content_browsertests.filter',
         ],
+        'swarming': {
+          'shards': 15,
+        },
       },
       'WebRTC Chromium FYI Android Tests (dbg) (L Nexus5)': {
         'args': [
diff --git a/testing/scripts/representative_perf_test_data/representatives_frame_times_upper_limit.json b/testing/scripts/representative_perf_test_data/representatives_frame_times_upper_limit.json
index e958805..3b964e7d 100644
--- a/testing/scripts/representative_perf_test_data/representatives_frame_times_upper_limit.json
+++ b/testing/scripts/representative_perf_test_data/representatives_frame_times_upper_limit.json
@@ -144,8 +144,9 @@
     },
     "new_tilings": {
       "ci_095": 0.334,
-      "avg": 16.692,
-      "cpu_wall_time_ratio": 0.372
+      "avg": 19.224,
+      "cpu_wall_time_ratio": 0.372,
+      "_comment": "Return to 16.692 after crbug.com/1225374"
     },
     "chip_tune": {
       "ci_095": 0.355,
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index d243ff8..4a775e98 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2109,6 +2109,28 @@
             ]
         }
     ],
+    "CompositeAfterPaint": [
+        {
+            "platforms": [
+                "android",
+                "android_weblayer",
+                "android_webview",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "CompositeAfterPaint"
+                    ]
+                }
+            ]
+        }
+    ],
     "ContentCapture": [
         {
             "platforms": [
diff --git a/third_party/blink/common/browser_interface_broker_proxy.cc b/third_party/blink/common/browser_interface_broker_proxy.cc
index 65826e83..7d30a1d 100644
--- a/third_party/blink/common/browser_interface_broker_proxy.cc
+++ b/third_party/blink/common/browser_interface_broker_proxy.cc
@@ -5,7 +5,6 @@
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 
 #include "base/macros.h"
-#include "base/no_destructor.h"
 #include "base/threading/sequence_local_storage_slot.h"
 
 namespace blink {
@@ -62,17 +61,15 @@
 }
 
 BrowserInterfaceBrokerProxy& GetEmptyBrowserInterfaceBroker() {
-  static base::NoDestructor<
-      base::SequenceLocalStorageSlot<BrowserInterfaceBrokerProxy>>
-      proxy_slot;
-  if (!proxy_slot->GetValuePointer()) {
-    auto& proxy = proxy_slot->GetOrCreateValue();
+  static base::SequenceLocalStorageSlot<BrowserInterfaceBrokerProxy> proxy_slot;
+  if (!proxy_slot.GetValuePointer()) {
+    auto& proxy = proxy_slot.GetOrCreateValue();
     mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker> remote;
     ignore_result(remote.InitWithNewPipeAndPassReceiver());
     proxy.Bind(std::move(remote), base::ThreadTaskRunnerHandle::Get());
   }
 
-  return proxy_slot->GetOrCreateValue();
+  return proxy_slot.GetOrCreateValue();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/common/privacy_budget/identifiability_study_settings.cc b/third_party/blink/common/privacy_budget/identifiability_study_settings.cc
index bf93086c..b3da1c9 100644
--- a/third_party/blink/common/privacy_budget/identifiability_study_settings.cc
+++ b/third_party/blink/common/privacy_budget/identifiability_study_settings.cc
@@ -105,8 +105,7 @@
 }
 
 bool DecideSample(int sample_rate) {
-  static base::NoDestructor<base::SequenceLocalStorageSlot<std::mt19937_64>>
-      prng;
+  static base::SequenceLocalStorageSlot<std::mt19937_64> prng;
 
   if (sample_rate == 0)
     return false;
@@ -114,10 +113,10 @@
   if (sample_rate == 1)
     return true;
 
-  if (!prng->GetValuePointer())
-    prng->emplace(base::RandUint64());
+  if (!prng.GetValuePointer())
+    prng.emplace(base::RandUint64());
 
-  return RandGenerator(sample_rate, **prng) == 0;
+  return RandGenerator(sample_rate, *prng) == 0;
 }
 
 }  // namespace
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index 7691e1e..cd2ebc9 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -441,7 +441,7 @@
   // used for resources which have compress="gzip" in *.grd.
   virtual WebData GetDataResource(
       int resource_id,
-      ui::ScaleFactor scale_factor = ui::SCALE_FACTOR_NONE) {
+      ui::ResourceScaleFactor scale_factor = ui::SCALE_FACTOR_NONE) {
     return WebData();
   }
 
diff --git a/third_party/blink/public/web/web_remote_frame.h b/third_party/blink/public/web/web_remote_frame.h
index 2b7f665..c075dc1 100644
--- a/third_party/blink/public/web/web_remote_frame.h
+++ b/third_party/blink/public/web/web_remote_frame.h
@@ -53,15 +53,15 @@
       WebFrame* opener);
 
   // Also performs core initialization to associate the created remote frame
-  // with the provided <portal> element.
-  BLINK_EXPORT static WebRemoteFrame* CreateForPortal(
+  // with the provided <portal> or <fencedframe> element.
+  BLINK_EXPORT static WebRemoteFrame* CreateForPortalOrFencedFrame(
       mojom::TreeScopeType,
       WebRemoteFrameClient*,
       InterfaceRegistry*,
       AssociatedInterfaceProvider*,
       const RemoteFrameToken& frame_token,
       const base::UnguessableToken& devtools_frame_token,
-      const WebElement& portal_element);
+      const WebElement& frame_owner);
 
   // Specialized factory methods to allow the embedder to replicate the frame
   // tree between processes.
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index fd9c44f..3c99fb9 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -289,9 +289,9 @@
 };
 
 BackForwardCacheBufferLimitTracker& GetBackForwardCacheBufferLimitTracker() {
-  static base::NoDestructor<BackForwardCacheBufferLimitTracker>
+  static BackForwardCacheBufferLimitTracker
       back_forward_cache_buffer_limit_tracker;
-  return *back_forward_cache_buffer_limit_tracker;
+  return back_forward_cache_buffer_limit_tracker;
 }
 
 inline float ParentPageZoomFactor(LocalFrame* frame) {
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc
index ab1faaea..c8f071d 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
@@ -47,8 +47,11 @@
 
   HTMLFrameOwnerElement* owner = remote_frame_->DeprecatedLocalOwner();
   if (owner &&
-      owner->OwnerType() == mojom::blink::FrameOwnerElementType::kPortal)
+      (owner->OwnerType() == mojom::blink::FrameOwnerElementType::kPortal ||
+       owner->OwnerType() ==
+           mojom::blink::FrameOwnerElementType::kFencedframe)) {
     return owner->GetDocument().GetFrame()->View();
+  }
 
   // |is_attached_| is only set from AttachToLayout(), which ensures that the
   // parent is a local frame.
diff --git a/third_party/blink/renderer/core/frame/visual_viewport.cc b/third_party/blink/renderer/core/frame/visual_viewport.cc
index 05dc2e87..3338bdf 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport.cc
@@ -37,6 +37,7 @@
 #include "cc/input/main_thread_scrolling_reason.h"
 #include "cc/layers/solid_color_scrollbar_layer.h"
 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h"
+#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
@@ -297,7 +298,7 @@
   }
 
 #if defined(OS_ANDROID)
-  if (base::FeatureList::IsEnabled(::features::kElasticOverscroll) &&
+  if (Platform::Current()->IsElasticOverscrollEnabled() &&
       base::GetFieldTrialParamValueByFeature(
           ::features::kElasticOverscroll, ::features::kElasticOverscrollType) !=
           ::features::kElasticOverscrollTypeTransform) {
diff --git a/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc b/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc
index bd4421c..86ae1f30 100644
--- a/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc
@@ -26,6 +26,7 @@
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/web_frame_widget_impl.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
+#include "third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/html/portal/html_portal_element.h"
 #include "third_party/blink/renderer/core/html_names.h"
@@ -54,6 +55,7 @@
       frame_token);
 }
 
+// static
 WebRemoteFrame* WebRemoteFrame::CreateMainFrame(
     WebView* web_view,
     WebRemoteFrameClient* client,
@@ -67,17 +69,18 @@
       frame_token, devtools_frame_token, opener);
 }
 
-WebRemoteFrame* WebRemoteFrame::CreateForPortal(
+// static
+WebRemoteFrame* WebRemoteFrame::CreateForPortalOrFencedFrame(
     mojom::blink::TreeScopeType scope,
     WebRemoteFrameClient* client,
     InterfaceRegistry* interface_registry,
     AssociatedInterfaceProvider* associated_interface_provider,
     const RemoteFrameToken& frame_token,
     const base::UnguessableToken& devtools_frame_token,
-    const WebElement& portal_element) {
-  return WebRemoteFrameImpl::CreateForPortal(
+    const WebElement& frame_owner) {
+  return WebRemoteFrameImpl::CreateForPortalOrFencedFrame(
       scope, client, interface_registry, associated_interface_provider,
-      frame_token, devtools_frame_token, portal_element);
+      frame_token, devtools_frame_token, frame_owner);
 }
 
 // static
@@ -111,28 +114,34 @@
   return frame;
 }
 
-WebRemoteFrameImpl* WebRemoteFrameImpl::CreateForPortal(
+WebRemoteFrameImpl* WebRemoteFrameImpl::CreateForPortalOrFencedFrame(
     mojom::blink::TreeScopeType scope,
     WebRemoteFrameClient* client,
     InterfaceRegistry* interface_registry,
     AssociatedInterfaceProvider* associated_interface_provider,
     const RemoteFrameToken& frame_token,
     const base::UnguessableToken& devtools_frame_token,
-    const WebElement& portal_element) {
+    const WebElement& frame_owner) {
   auto* frame = MakeGarbageCollected<WebRemoteFrameImpl>(
       scope, client, interface_registry, associated_interface_provider,
       frame_token);
 
-  Element* element = portal_element;
-  DCHECK(element->HasTagName(html_names::kPortalTag));
-  DCHECK(
-      RuntimeEnabledFeatures::PortalsEnabled(element->GetExecutionContext()));
-  HTMLPortalElement* portal = static_cast<HTMLPortalElement*>(element);
-  LocalFrame* host_frame = portal->GetDocument().GetFrame();
-  frame->InitializeCoreFrame(*host_frame->GetPage(), portal, nullptr, nullptr,
-                             FrameInsertType::kInsertInConstructor, g_null_atom,
-                             &host_frame->window_agent_factory(),
-                             devtools_frame_token);
+  // We first convert this to a raw blink::Element*, and manually convert this
+  // to an HTMLElement*. That is the only way the IsA<> and To<> casts below
+  // will work.
+  Element* element = frame_owner;
+  DCHECK(IsA<HTMLPortalElement>(element) ||
+         IsA<HTMLFencedFrameElement>(element));
+  ExecutionContext* execution_context = element->GetExecutionContext();
+  DCHECK(RuntimeEnabledFeatures::PortalsEnabled(execution_context) ||
+         RuntimeEnabledFeatures::FencedFramesEnabled(execution_context));
+  HTMLFrameOwnerElement* frame_owner_element =
+      To<HTMLFrameOwnerElement>(element);
+  LocalFrame* host_frame = frame_owner_element->GetDocument().GetFrame();
+  frame->InitializeCoreFrame(
+      *host_frame->GetPage(), frame_owner_element, nullptr, nullptr,
+      FrameInsertType::kInsertInConstructor, g_null_atom,
+      &host_frame->window_agent_factory(), devtools_frame_token);
 
   return frame;
 }
@@ -241,9 +250,11 @@
           To<WebLocalFrameImpl>(parent)->LocalRoot()->FrameWidget();
     }
   } else if (owner && owner->IsLocal()) {
-    // Never gets to this point without |owner| being a portal element.
-    auto* owner_element = To<HTMLFrameOwnerElement>(owner);
-    DCHECK(owner_element->IsHTMLPortalElement());
+    // Never gets to this point unless |owner| is a <portal> or <fencedframe>
+    // element.
+    HTMLFrameOwnerElement* owner_element = To<HTMLFrameOwnerElement>(owner);
+    DCHECK(owner_element->IsHTMLPortalElement() ||
+           owner_element->IsHTMLFencedFrameElement());
     LocalFrame& local_frame =
         owner_element->GetDocument().GetFrame()->LocalFrameRoot();
     ancestor_widget = WebLocalFrameImpl::FromFrame(local_frame)->FrameWidget();
diff --git a/third_party/blink/renderer/core/frame/web_remote_frame_impl.h b/third_party/blink/renderer/core/frame/web_remote_frame_impl.h
index b224a36..e995c43 100644
--- a/third_party/blink/renderer/core/frame/web_remote_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_remote_frame_impl.h
@@ -39,14 +39,14 @@
       const RemoteFrameToken& frame_token,
       const base::UnguessableToken& devtools_frame_token,
       WebFrame* opener);
-  static WebRemoteFrameImpl* CreateForPortal(
+  static WebRemoteFrameImpl* CreateForPortalOrFencedFrame(
       mojom::blink::TreeScopeType,
       WebRemoteFrameClient*,
       InterfaceRegistry*,
       AssociatedInterfaceProvider*,
       const RemoteFrameToken& frame_token,
       const base::UnguessableToken& devtools_frame_token,
-      const WebElement& portal_element);
+      const WebElement& frame_owner);
 
   WebRemoteFrameImpl(mojom::blink::TreeScopeType,
                      WebRemoteFrameClient*,
diff --git a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h
index c37acd34..d4c8aeab 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h
+++ b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h
@@ -108,6 +108,11 @@
       return html_element->IsHTMLFencedFrameElement();
     return false;
   }
+  static bool AllowFrom(const Element& element) {
+    if (const HTMLElement* html_element = DynamicTo<HTMLElement>(element))
+      return html_element->IsHTMLFencedFrameElement();
+    return false;
+  }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/portal/html_portal_element.h b/third_party/blink/renderer/core/html/portal/html_portal_element.h
index f9262fd..d6ccd40c 100644
--- a/third_party/blink/renderer/core/html/portal/html_portal_element.h
+++ b/third_party/blink/renderer/core/html/portal/html_portal_element.h
@@ -172,6 +172,11 @@
       return html_element->IsHTMLPortalElement();
     return false;
   }
+  static bool AllowFrom(const Element& element) {
+    if (const HTMLElement* html_element = DynamicTo<HTMLElement>(element))
+      return html_element->IsHTMLPortalElement();
+    return false;
+  }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_media_element.cc b/third_party/blink/renderer/modules/accessibility/ax_media_element.cc
index 482f14b3..4715a48 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_media_element.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_media_element.cc
@@ -61,24 +61,6 @@
   return AXNodeObject::Restriction();
 }
 
-bool AccessibilityMediaElement::HasControls() const {
-  if (IsDetached())
-    return false;
-  if (!IsA<HTMLMediaElement>(GetNode()) || !GetNode()->isConnected()) {
-    NOTREACHED() << "Accessible media element not ready: " << GetNode()
-                 << "  isConnected? " << GetNode()->isConnected();
-    return false;
-  }
-  return To<HTMLMediaElement>(GetNode())->ShouldShowControls();
-}
-
-bool AccessibilityMediaElement::HasEmptySource() const {
-  if (IsDetached())
-    return false;
-  return To<HTMLMediaElement>(GetNode())->getNetworkState() ==
-         HTMLMediaElement::kNetworkEmpty;
-}
-
 bool AccessibilityMediaElement::IsUnplayable() const {
   if (IsDetached())
     return true;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_media_element.h b/third_party/blink/renderer/modules/accessibility/ax_media_element.h
index afa2cea7..e58ceb9 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_media_element.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_media_element.h
@@ -34,8 +34,6 @@
   AXRestriction Restriction() const override;
 
  protected:
-  bool HasControls() const;
-  bool HasEmptySource() const;
   bool IsUnplayable() const;
 
   DISALLOW_COPY_AND_ASSIGN(AccessibilityMediaElement);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 9424437..0d164be0 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -67,6 +67,7 @@
 #include "third_party/blink/renderer/core/html/html_table_row_element.h"
 #include "third_party/blink/renderer/core/html/html_table_section_element.h"
 #include "third_party/blink/renderer/core/html/html_title_element.h"
+#include "third_party/blink/renderer/core/html/media/html_media_element.h"
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
 #include "third_party/blink/renderer/core/input/context_menu_allowed_scope.h"
 #include "third_party/blink/renderer/core/input/event_handler.h"
@@ -2478,6 +2479,14 @@
       return true;
   }
 
+  // The ignored state of media controls can change without a layout update.
+  // Keep them in the tree at all times so that the serializer isn't
+  // accidentally working with unincluded nodes, which is not allowed.
+  if (node->IsInUserAgentShadowRoot() &&
+      IsA<HTMLMediaElement>(node->OwnerShadowHost())) {
+    return true;
+  }
+
   Element* element = GetElement();
   if (!element)
     return false;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index f3f23ef..82d3c9f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -2882,6 +2882,8 @@
     HandleUseMapAttributeChangedWithCleanLayout(element);
   } else if (attr_name == html_names::kNameAttr) {
     HandleNameAttributeChangedWithCleanLayout(element);
+  } else if (attr_name == html_names::kControlsAttr) {
+    ChildrenChangedWithCleanLayout(element);
   }
 
   if (!attr_name.LocalName().StartsWith("aria-"))
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler.idl b/third_party/blink/renderer/modules/scheduler/scheduler.idl
index 71f20574..755ec139 100644
--- a/third_party/blink/renderer/modules/scheduler/scheduler.idl
+++ b/third_party/blink/renderer/modules/scheduler/scheduler.idl
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Experimental Scheduling API Proposal:
-// https://docs.google.com/document/d/1Apz-SD-pOagGeyWxIpgOi0ARNkrCrELhPdm18eeu9tw
+// https://wicg.github.io/scheduling-apis/#sec-scheduler
+//
+// currentTaskSignal:
+// https://github.com/WICG/scheduling-apis/blob/main/explainers/post-task-propagation.md
 [
     Exposed=(Window,Worker),
     ImplementedAs=DOMScheduler,
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler_post_task_callback.idl b/third_party/blink/renderer/modules/scheduler/scheduler_post_task_callback.idl
index 7dc1b251..97c205c 100644
--- a/third_party/blink/renderer/modules/scheduler/scheduler_post_task_callback.idl
+++ b/third_party/blink/renderer/modules/scheduler/scheduler_post_task_callback.idl
@@ -2,6 +2,5 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Experimental Scheduling API Proposal:
-// https://docs.google.com/document/d/1Apz-SD-pOagGeyWxIpgOi0ARNkrCrELhPdm18eeu9tw
+// https://wicg.github.io/scheduling-apis/#sec-scheduler
 callback SchedulerPostTaskCallback = any ();
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl b/third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl
index 147b49e..e8459a0 100644
--- a/third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl
+++ b/third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Experimental Scheduling API Proposal:
-// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
+// https://wicg.github.io/scheduling-apis/#sec-task-priorities
 enum TaskPriority {
     "user-blocking",
     "user-visible",
     "background"
 };
 
+// https://wicg.github.io/scheduling-apis/#sec-scheduler
 dictionary SchedulerPostTaskOptions {
     AbortSignal signal;
     TaskPriority priority;
diff --git a/third_party/blink/renderer/modules/scheduler/task_controller.idl b/third_party/blink/renderer/modules/scheduler/task_controller.idl
index 15efbca..280744f 100644
--- a/third_party/blink/renderer/modules/scheduler/task_controller.idl
+++ b/third_party/blink/renderer/modules/scheduler/task_controller.idl
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Experimental Scheduling API Proposal:
-// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
-
+// https://wicg.github.io/scheduling-apis/#sec-task-controller
 [
     Exposed=(Window,Worker),
     ImplementedAs=DOMTaskController,
diff --git a/third_party/blink/renderer/modules/scheduler/task_signal.idl b/third_party/blink/renderer/modules/scheduler/task_signal.idl
index 731e725..1b29200 100644
--- a/third_party/blink/renderer/modules/scheduler/task_signal.idl
+++ b/third_party/blink/renderer/modules/scheduler/task_signal.idl
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Experimental Scheduling API Proposal:
-// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
-
+// https://wicg.github.io/scheduling-apis/#sec-task-signal
 [
     Exposed=(Window,Worker),
     ImplementedAs=DOMTaskSignal,
diff --git a/third_party/blink/renderer/modules/scheduler/window_or_worker_scheduler.idl b/third_party/blink/renderer/modules/scheduler/window_or_worker_scheduler.idl
index bd65043..5c72942 100644
--- a/third_party/blink/renderer/modules/scheduler/window_or_worker_scheduler.idl
+++ b/third_party/blink/renderer/modules/scheduler/window_or_worker_scheduler.idl
@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Experimental Scheduling API Proposal:
-// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
+// https://wicg.github.io/scheduling-apis/#sec-patches-html-windoworworkerglobalscope
 [
     ImplementedAs=DOMScheduler,
     Exposed=(Window,Worker)
diff --git a/third_party/blink/renderer/modules/xr/xr_image_tracking_result.cc b/third_party/blink/renderer/modules/xr/xr_image_tracking_result.cc
index 7e3355f..069d9a1 100644
--- a/third_party/blink/renderer/modules/xr/xr_image_tracking_result.cc
+++ b/third_party/blink/renderer/modules/xr/xr_image_tracking_result.cc
@@ -48,12 +48,7 @@
 
 device::mojom::blink::XRNativeOriginInformationPtr
 XRImageTrackingResult::NativeOrigin() const {
-  // TODO(https://crbug.com/1143575): We'll want these to correspond to an
-  // actual, independent space eventually, but at the moment it's sufficient for
-  // the ARCore implementation to have it be equivalent to the local reference
-  // space.
-  return device::mojom::blink::XRNativeOriginInformation::NewReferenceSpaceType(
-      device::mojom::XRReferenceSpaceType::kLocal);
+  return device::mojom::blink::XRNativeOriginInformation::NewImageIndex(index_);
 }
 
 void XRImageTrackingResult::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/platform/graphics/image.cc b/third_party/blink/renderer/platform/graphics/image.cc
index 4665d9d..16ee18e 100644
--- a/third_party/blink/renderer/platform/graphics/image.cc
+++ b/third_party/blink/renderer/platform/graphics/image.cc
@@ -95,8 +95,9 @@
   return image_decode_cache;
 }
 
-scoped_refptr<Image> Image::LoadPlatformResource(int resource_id,
-                                                 ui::ScaleFactor scale_factor) {
+scoped_refptr<Image> Image::LoadPlatformResource(
+    int resource_id,
+    ui::ResourceScaleFactor scale_factor) {
   const WebData& resource =
       Platform::Current()->GetDataResource(resource_id, scale_factor);
   if (resource.IsEmpty())
diff --git a/third_party/blink/renderer/platform/graphics/image.h b/third_party/blink/renderer/platform/graphics/image.h
index 0f3c56aa..4ef1b1a 100644
--- a/third_party/blink/renderer/platform/graphics/image.h
+++ b/third_party/blink/renderer/platform/graphics/image.h
@@ -79,7 +79,7 @@
 
   static scoped_refptr<Image> LoadPlatformResource(
       int resource_id,
-      ui::ScaleFactor scale_factor = ui::SCALE_FACTOR_100P);
+      ui::ResourceScaleFactor scale_factor = ui::SCALE_FACTOR_100P);
 
   static PaintImage ResizeAndOrientImage(
       const PaintImage&,
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc
index d232a430..c3c7e72 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc
@@ -93,10 +93,10 @@
 };
 
 DecoderCounter* GetDecoderCounter() {
-  static base::NoDestructor<DecoderCounter> s_counter;
+  static DecoderCounter s_counter;
   // Note that this will init only in the first call in the ctor, so it's still
   // single threaded.
-  return s_counter.get();
+  return &s_counter;
 }
 
 void FinishWait(base::WaitableEvent* waiter, bool* result_out, bool result) {
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_stream_adapter.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_stream_adapter.cc
index a4e347c..3a19c2c 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_stream_adapter.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_stream_adapter.cc
@@ -80,10 +80,10 @@
 
 // Number of RTCVideoDecoder instances right now that have started decoding.
 std::atomic_int* GetDecoderCounter() {
-  static base::NoDestructor<std::atomic_int> s_counter(0);
+  static std::atomic_int s_counter(0);
   // Note that this will init only in the first call in the ctor, so it's still
   // single threaded.
-  return s_counter.get();
+  return &s_counter;
 }
 
 void RecordInitializationLatency(base::TimeDelta latency) {
diff --git a/third_party/blink/renderer/platform/scheduler/common/features.cc b/third_party/blink/renderer/platform/scheduler/common/features.cc
index 525643e..9ef5952 100644
--- a/third_party/blink/renderer/platform/scheduler/common/features.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/features.cc
@@ -64,8 +64,6 @@
 // use the base::FeatureParams.
 
 base::TimeDelta GetIntensiveWakeUpThrottlingGracePeriod() {
-  DCHECK(IsIntensiveWakeUpThrottlingEnabled());
-
   // Controls the time that elapses after a page is backgrounded before the
   // throttling policy takes effect.
   static const base::FeatureParam<int>
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index 2cf8fb8..bbe6cf81 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -1370,13 +1370,17 @@
 std::unique_ptr<WebSchedulingTaskQueue>
 FrameSchedulerImpl::CreateWebSchedulingTaskQueue(
     WebSchedulingPriority priority) {
-  // Use QueueTraits here that are the same as postMessage, which is one current
-  // method for scheduling script.
-  scoped_refptr<MainThreadTaskQueue> task_queue =
+  scoped_refptr<MainThreadTaskQueue> immediate_task_queue =
       frame_task_queue_controller_->NewWebSchedulingTaskQueue(
-          PausableTaskQueueTraits(), priority);
+          DeferrableTaskQueueTraits(), priority);
+  scoped_refptr<MainThreadTaskQueue> delayed_task_queue =
+      frame_task_queue_controller_->NewWebSchedulingTaskQueue(
+          DeferrableTaskQueueTraits()
+              .SetCanBeThrottled(true)
+              .SetCanBeIntensivelyThrottled(true),
+          priority);
   return std::make_unique<MainThreadWebSchedulingTaskQueueImpl>(
-      task_queue->AsWeakPtr());
+      immediate_task_queue->AsWeakPtr(), delayed_task_queue->AsWeakPtr());
 }
 
 void FrameSchedulerImpl::OnWebSchedulingTaskQueuePriorityChanged(
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 9b3e078..1f97f072 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
@@ -16,6 +16,7 @@
 #include "base/metrics/field_trial_param_associator.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
 #include "base/task/sequence_manager/test/sequence_manager_for_test.h"
 #include "base/task/thread_pool.h"
 #include "base/test/bind.h"
@@ -23,6 +24,7 @@
 #include "base/test/scoped_command_line.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
+#include "base/time/time.h"
 #include "base/unguessable_token.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -646,6 +648,26 @@
     return GetParam().is_intensive_throttling_expected;
   }
 
+  // Get the TaskRunner from |frame_scheduler_| using the test's task type
+  // parameter.
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() const {
+    return GetTaskRunner(frame_scheduler_.get());
+  }
+
+  // Get the TaskRunner from the provided |frame_scheduler| using the test's
+  // task type parameter.
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
+      FrameSchedulerImpl* frame_scheduler) const {
+    const TaskType task_type = GetTaskType();
+    if (task_type == TaskType::kExperimentalWebScheduling) {
+      return frame_scheduler
+          ->CreateWebSchedulingTaskQueue(
+              WebSchedulingPriority::kUserVisiblePriority)
+          ->GetTaskRunner();
+    }
+    return frame_scheduler->GetTaskRunner(task_type);
+  }
+
   base::TimeDelta GetExpectedWakeUpInterval() const {
     if (IsIntensiveThrottlingExpected())
       return kIntensiveThrottledWakeUpInterval;
@@ -2761,7 +2783,8 @@
   // - 'V': UserVisible
   // - 'B': Background
   void PostWebSchedulingTestTasks(Vector<String>* run_order,
-                                  const String& task_descriptor) {
+                                  const String& task_descriptor,
+                                  base::TimeDelta delay = base::TimeDelta()) {
     std::istringstream stream(task_descriptor.Utf8());
     while (!stream.eof()) {
       std::string task;
@@ -2781,9 +2804,11 @@
           EXPECT_FALSE(true);
           return;
       }
-      web_scheduling_task_runners_[static_cast<int>(priority)]->PostTask(
-          FROM_HERE, base::BindOnce(&AppendToVectorTestTask, run_order,
-                                    String::FromUTF8(task)));
+      web_scheduling_task_runners_[static_cast<int>(priority)]->PostDelayedTask(
+          FROM_HERE,
+          base::BindOnce(&AppendToVectorTestTask, run_order,
+                         String::FromUTF8(task)),
+          delay);
     }
   }
 
@@ -2815,12 +2840,29 @@
               testing::ElementsAre("V1", "V2", "B1", "B2", "U1", "U2"));
 }
 
-// Verify that tasks posted with TaskType::kJavascriptTimerDelayed* run at the
-// expected time when throttled.
+TEST_F(WebSchedulingTaskQueueTest, DynamicTaskPriorityOrderDelayedTasks) {
+  Vector<String> run_order;
+
+  // We're relying on all of the delays to expire at the same time, in which
+  // case the tasks will run in the updated priority order.
+  PostWebSchedulingTestTasks(&run_order, "B1 B2 V1 V2 U1 U2",
+                             base::TimeDelta::FromMilliseconds(5));
+  task_queues_[static_cast<int>(WebSchedulingPriority::kUserBlockingPriority)]
+      ->SetPriority(WebSchedulingPriority::kBackgroundPriority);
+
+  task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(5));
+
+  EXPECT_THAT(run_order,
+              testing::ElementsAre("V1", "V2", "B1", "B2", "U1", "U2"));
+}
+
+// Verify that tasks posted with TaskType::kJavascriptTimerDelayed* and
+// delayed web scheduling tasks run at the expected time when throttled.
 TEST_F(FrameSchedulerImplTest, ThrottledJSTimerTasksRunTime) {
   constexpr TaskType kJavaScriptTimerTaskTypes[] = {
       TaskType::kJavascriptTimerDelayedLowNesting,
-      TaskType::kJavascriptTimerDelayedHighNesting};
+      TaskType::kJavascriptTimerDelayedHighNesting,
+      TaskType::kExperimentalWebScheduling};
 
   // Snap the time to a multiple of 1 second. Otherwise, the exact run time
   // of throttled tasks after hiding the page will vary.
@@ -2832,10 +2874,16 @@
 
   std::map<TaskType, std::vector<base::TimeTicks>> run_times;
 
-  // Post tasks with each Javascript Timer Task Type.
+  // Post tasks with each Javascript Timer Task Type and with a
+  // WebSchedulingTaskQueue.
   for (TaskType task_type : kJavaScriptTimerTaskTypes) {
     const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-        frame_scheduler_->GetTaskRunner(task_type);
+        task_type == TaskType::kExperimentalWebScheduling
+            ? frame_scheduler_
+                  ->CreateWebSchedulingTaskQueue(
+                      WebSchedulingPriority::kUserVisiblePriority)
+                  ->GetTaskRunner()
+            : frame_scheduler_->GetTaskRunner(task_type);
 
     // Note: Taking the address of an element in |run_times| is safe because
     // inserting elements in a map does not invalidate references.
@@ -2944,7 +2992,7 @@
 
   // Throttled TaskRunner to which tasks are posted in this test.
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(GetTaskType());
+      GetTaskRunner();
 
   // Snap the time to a multiple of
   // |kIntensiveThrottledWakeUpInterval|. Otherwise, the time at which
@@ -3122,7 +3170,7 @@
 
   // Throttled TaskRunner to which tasks are posted in this test.
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(GetTaskType());
+      GetTaskRunner();
 
   // Snap the time to a multiple of
   // |kIntensiveThrottledWakeUpInterval|. Otherwise, the time at which
@@ -3296,7 +3344,7 @@
        ManySameOriginFrames) {
   ASSERT_FALSE(frame_scheduler_->IsCrossOriginToMainFrame());
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(GetTaskType());
+      GetTaskRunner();
 
   // Create a FrameScheduler that is same-origin with the main frame, and an
   // associated throttled TaskRunner.
@@ -3306,7 +3354,7 @@
                            FrameScheduler::FrameType::kSubframe);
   ASSERT_FALSE(other_frame_scheduler->IsCrossOriginToMainFrame());
   const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
-      other_frame_scheduler->GetTaskRunner(GetTaskType());
+      GetTaskRunner(other_frame_scheduler.get());
 
   // Snap the time to a multiple of
   // |kIntensiveThrottledWakeUpInterval|. Otherwise, the time at which
@@ -3352,14 +3400,14 @@
   constexpr int kNumTasks = 3;
   // |task_runner| is throttled.
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(GetTaskType());
+      GetTaskRunner();
   // |other_task_runner| is throttled. It belongs to a different frame on the
   // same page.
   const auto other_frame_scheduler = CreateFrameScheduler(
       page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
       FrameScheduler::FrameType::kSubframe);
   const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
-      other_frame_scheduler->GetTaskRunner(GetTaskType());
+      GetTaskRunner(other_frame_scheduler.get());
 
   // Fast-forward the time to a multiple of
   // |kIntensiveThrottledWakeUpInterval|. Otherwise,
@@ -3477,7 +3525,7 @@
        FrameChangesOriginType) {
   EXPECT_FALSE(frame_scheduler_->IsCrossOriginToMainFrame());
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(GetTaskType());
+      GetTaskRunner();
 
   // Create a new FrameScheduler that remains cross-origin with the main frame
   // throughout the test.
@@ -3487,7 +3535,7 @@
                            FrameScheduler::FrameType::kSubframe);
   cross_origin_frame_scheduler->SetCrossOriginToMainFrame(true);
   const scoped_refptr<base::SingleThreadTaskRunner> cross_origin_task_runner =
-      cross_origin_frame_scheduler->GetTaskRunner(GetTaskType());
+      GetTaskRunner(cross_origin_frame_scheduler.get());
 
   // Snap the time to a multiple of
   // |kIntensiveThrottledWakeUpInterval|. Otherwise, the time at which
@@ -3579,6 +3627,9 @@
             /* is_intensive_throttling_expected=*/false},
         IntensiveWakeUpThrottlingTestParam{
             /* task_type=*/TaskType::kJavascriptTimerDelayedHighNesting,
+            /* is_intensive_throttling_expected=*/true},
+        IntensiveWakeUpThrottlingTestParam{
+            /* task_type=*/TaskType::kExperimentalWebScheduling,
             /* is_intensive_throttling_expected=*/true}),
     [](const testing::TestParamInfo<IntensiveWakeUpThrottlingTestParam>& info) {
       return TaskTypeNames::TaskTypeToString(info.param.task_type);
@@ -3841,6 +3892,36 @@
   }
 }
 
+// Verify that non-delayed kExperimentalWebScheduling tasks are not throttled.
+TEST_F(FrameSchedulerImplTest, ImmediateWebSchedulingTasksAreNotThrottled) {
+  std::vector<base::TimeTicks> run_times;
+
+  // Make sure we are *not* aligned to a 1 second boundary by aligning to a 1
+  // second boundary and moving past it a bit. If we were throttled, even
+  // non-delayed tasks will need to wait until the next aligned interval to run.
+  FastForwardToAlignedTime(base::TimeDelta::FromSeconds(1));
+  task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(1));
+
+  const base::TimeTicks start = base::TimeTicks::Now();
+
+  // Hide the page to start throttling timers.
+  page_scheduler_->SetPageVisible(false);
+
+  // Post a non-delayed task to a web scheduling task queue.
+  const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      frame_scheduler_
+          ->CreateWebSchedulingTaskQueue(
+              WebSchedulingPriority::kUserVisiblePriority)
+          ->GetTaskRunner();
+  task_runner->PostTask(FROM_HERE, base::BindOnce(&RecordRunTime, &run_times));
+
+  // Run any ready tasks, which includes our non-delayed non-throttled web
+  // scheduling task. If we are throttled, our task will not run.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(run_times, testing::ElementsAre(start));
+}
+
 }  // namespace frame_scheduler_impl_unittest
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_web_scheduling_task_queue_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_web_scheduling_task_queue_impl.cc
index 0474a99..e93f85f 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_web_scheduling_task_queue_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_web_scheduling_task_queue_impl.cc
@@ -10,16 +10,59 @@
 namespace blink {
 namespace scheduler {
 
+MainThreadWebSchedulingTaskQueueImpl::WebSchedulingTaskRunner::
+    WebSchedulingTaskRunner(
+        scoped_refptr<base::SingleThreadTaskRunner> immediate_task_runner,
+        scoped_refptr<base::SingleThreadTaskRunner> delayed_task_runner)
+    : immediate_task_runner_(std::move(immediate_task_runner)),
+      delayed_task_runner_(std::move(delayed_task_runner)) {}
+
+bool MainThreadWebSchedulingTaskQueueImpl::WebSchedulingTaskRunner::
+    PostDelayedTask(const base::Location& location,
+                    base::OnceClosure task,
+                    base::TimeDelta delay) {
+  return GetTaskRunnerForDelay(delay)->PostDelayedTask(location,
+                                                       std::move(task), delay);
+}
+
+bool MainThreadWebSchedulingTaskQueueImpl::WebSchedulingTaskRunner::
+    PostNonNestableDelayedTask(const base::Location& location,
+                               base::OnceClosure task,
+                               base::TimeDelta delay) {
+  return GetTaskRunnerForDelay(delay)->PostNonNestableDelayedTask(
+      location, std::move(task), delay);
+}
+
+bool MainThreadWebSchedulingTaskQueueImpl::WebSchedulingTaskRunner::
+    RunsTasksInCurrentSequence() const {
+  DCHECK_EQ(immediate_task_runner_->RunsTasksInCurrentSequence(),
+            delayed_task_runner_->RunsTasksInCurrentSequence());
+  return immediate_task_runner_->RunsTasksInCurrentSequence();
+}
+
+base::SingleThreadTaskRunner* MainThreadWebSchedulingTaskQueueImpl::
+    WebSchedulingTaskRunner::GetTaskRunnerForDelay(base::TimeDelta delay) {
+  return delay > base::TimeDelta() ? delayed_task_runner_.get()
+                                   : immediate_task_runner_.get();
+}
+
 MainThreadWebSchedulingTaskQueueImpl::MainThreadWebSchedulingTaskQueueImpl(
-    base::WeakPtr<MainThreadTaskQueue> task_queue)
-    : task_runner_(
-          task_queue->CreateTaskRunner(TaskType::kExperimentalWebScheduling)),
-      task_queue_(std::move(task_queue)) {}
+    base::WeakPtr<MainThreadTaskQueue> immediate_task_queue,
+    base::WeakPtr<MainThreadTaskQueue> delayed_task_queue)
+    : task_runner_(base::MakeRefCounted<WebSchedulingTaskRunner>(
+          immediate_task_queue->CreateTaskRunner(
+              TaskType::kExperimentalWebScheduling),
+          delayed_task_queue->CreateTaskRunner(
+              TaskType::kExperimentalWebScheduling))),
+      immediate_task_queue_(std::move(immediate_task_queue)),
+      delayed_task_queue_(std::move(delayed_task_queue)) {}
 
 void MainThreadWebSchedulingTaskQueueImpl::SetPriority(
     WebSchedulingPriority priority) {
-  if (task_queue_)
-    task_queue_->SetWebSchedulingPriority(priority);
+  if (immediate_task_queue_)
+    immediate_task_queue_->SetWebSchedulingPriority(priority);
+  if (delayed_task_queue_)
+    delayed_task_queue_->SetWebSchedulingPriority(priority);
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_web_scheduling_task_queue_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_web_scheduling_task_queue_impl.h
index 6b572faa..3f70d61 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_web_scheduling_task_queue_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_web_scheduling_task_queue_impl.h
@@ -21,7 +21,9 @@
 class PLATFORM_EXPORT MainThreadWebSchedulingTaskQueueImpl
     : public WebSchedulingTaskQueue {
  public:
-  MainThreadWebSchedulingTaskQueueImpl(base::WeakPtr<MainThreadTaskQueue>);
+  MainThreadWebSchedulingTaskQueueImpl(
+      base::WeakPtr<MainThreadTaskQueue> immediate_task_queue,
+      base::WeakPtr<MainThreadTaskQueue> delayed_task_queue);
   ~MainThreadWebSchedulingTaskQueueImpl() override = default;
 
   void SetPriority(WebSchedulingPriority) override;
@@ -29,8 +31,36 @@
   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() override;
 
  private:
-  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  base::WeakPtr<MainThreadTaskQueue> task_queue_;
+  // In order to throttle delayed tasks in the background, we manage two
+  // MainThreadTaskQueues and their associated TaskRunners---one for delayed
+  // tasks and one for non-delayed tasks (immediate). Rather than exposing this
+  // to the web scheduling layer, we implement a simple custom TaskRunner that
+  // handles picking the appropriate underlying TaskRunner based on the delay
+  // value and return that in GetTaskRunner().
+  class WebSchedulingTaskRunner : public base::SingleThreadTaskRunner {
+   public:
+    WebSchedulingTaskRunner(
+        scoped_refptr<base::SingleThreadTaskRunner> immediate_task_runner,
+        scoped_refptr<base::SingleThreadTaskRunner> delayed_task_runner);
+
+    bool PostDelayedTask(const base::Location& location,
+                         base::OnceClosure task,
+                         base::TimeDelta delay) override;
+    bool PostNonNestableDelayedTask(const base::Location& from_here,
+                                    base::OnceClosure task,
+                                    base::TimeDelta delay) override;
+    bool RunsTasksInCurrentSequence() const override;
+
+   private:
+    base::SingleThreadTaskRunner* GetTaskRunnerForDelay(base::TimeDelta delay);
+
+    const scoped_refptr<base::SingleThreadTaskRunner> immediate_task_runner_;
+    const scoped_refptr<base::SingleThreadTaskRunner> delayed_task_runner_;
+  };
+
+  scoped_refptr<WebSchedulingTaskRunner> task_runner_;
+  base::WeakPtr<MainThreadTaskQueue> immediate_task_queue_;
+  base::WeakPtr<MainThreadTaskQueue> delayed_task_queue_;
 };
 
 }  // namespace scheduler
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
index fd20e904..c4417cd6 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -725,10 +725,8 @@
   for (WakeUpBudgetPool* pool : AllWakeUpBudgetPools())
     pool->SetWakeUpDuration(kThrottledWakeUpDuration);
 
-  if (IsIntensiveWakeUpThrottlingEnabled()) {
-    same_origin_intensive_wake_up_budget_pool_
-        ->AllowLowerAlignmentIfNoRecentWakeUp(kDefaultThrottledWakeUpInterval);
-  }
+  same_origin_intensive_wake_up_budget_pool_
+      ->AllowLowerAlignmentIfNoRecentWakeUp(kDefaultThrottledWakeUpInterval);
 
   UpdateWakeUpBudgetPools(lazy_now);
 }
@@ -772,11 +770,9 @@
           FROM_HERE, do_throttle_cpu_time_callback_.GetCallback(),
           kThrottlingDelayAfterBackgrounding);
     }
-    if (IsIntensiveWakeUpThrottlingEnabled()) {
-      main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
-          FROM_HERE, do_intensively_throttle_wake_ups_callback_.GetCallback(),
-          GetIntensiveWakeUpThrottlingGracePeriod());
-    }
+    main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
+        FROM_HERE, do_intensively_throttle_wake_ups_callback_.GetCallback(),
+        GetIntensiveWakeUpThrottlingGracePeriod());
   }
   if (notification_policy == NotificationPolicy::kNotifyFrames)
     NotifyFrames();
@@ -793,8 +789,6 @@
 }
 
 void PageSchedulerImpl::DoIntensivelyThrottleWakeUps() {
-  DCHECK(IsIntensiveWakeUpThrottlingEnabled());
-
   do_intensively_throttle_wake_ups_callback_.Cancel();
   are_wake_ups_intensively_throttled_ = true;
 
diff --git a/third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h b/third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h
index 049ecb25..04e618d 100644
--- a/third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h
+++ b/third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h
@@ -10,8 +10,7 @@
 
 namespace blink {
 
-// Priorities for the experimental scheduling API (see
-// https://github.com/WICG/main-thread-scheduling).
+// https://wicg.github.io/scheduling-apis/#sec-task-priorities
 enum class WebSchedulingPriority {
   kUserBlockingPriority = 0,
   kUserVisiblePriority = 1,
diff --git a/third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h b/third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h
index 0f580027..50d783a 100644
--- a/third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h
+++ b/third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h
@@ -12,9 +12,9 @@
 
 namespace blink {
 
-// This class is used by the experimental Scheduling API to submit tasks to the
-// platform's scheduler through prioritized task queues (see
-// https://github.com/WICG/main-thread-scheduling).
+// This class is used by the Prioritized Task Scheduling API to submit tasks to
+// the platform's scheduler through prioritized task queues (see
+// https://wicg.github.io/scheduling-apis/).
 class PLATFORM_EXPORT WebSchedulingTaskQueue {
  public:
   virtual ~WebSchedulingTaskQueue() = default;
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support.cc b/third_party/blink/renderer/platform/testing/testing_platform_support.cc
index 12ed1318..0b6ebef 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support.cc
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support.cc
@@ -109,8 +109,9 @@
   return WebString::FromUTF8("en-US");
 }
 
-WebData TestingPlatformSupport::GetDataResource(int resource_id,
-                                                ui::ScaleFactor scale_factor) {
+WebData TestingPlatformSupport::GetDataResource(
+    int resource_id,
+    ui::ResourceScaleFactor scale_factor) {
   return old_platform_
              ? old_platform_->GetDataResource(resource_id, scale_factor)
              : WebData();
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support.h b/third_party/blink/renderer/platform/testing/testing_platform_support.h
index c60029c..cf6ed4f 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support.h
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support.h
@@ -63,7 +63,7 @@
   // Platform:
   WebString DefaultLocale() override;
   WebData GetDataResource(int resource_id,
-                          ui::ScaleFactor scale_factor) override;
+                          ui::ResourceScaleFactor scale_factor) override;
   WebData UncompressDataResource(int resource_id) override;
   ThreadSafeBrowserInterfaceBrokerProxy* GetBrowserInterfaceBroker() override;
   bool IsThreadedAnimationEnabled() override;
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/driver.py b/third_party/blink/tools/blinkpy/web_tests/port/driver.py
index 233462d..eeb5171a 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/driver.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/driver.py
@@ -530,8 +530,18 @@
         cmd.extend(self._port.additional_driver_flags())
         if self._port.get_option('enable_leak_detection'):
             cmd.append('--enable-leak-detection')
-
         cmd.extend(per_test_args)
+
+        # The following code temporarily disables CompositeAfterPaint in web
+        # tests unless it is explicitly enabled. CompositeAfterPaint is enabled
+        # via fieldtrial_testing_config.json which would make web tests run
+        # with CompositeAfterPaint. This is disabled in order to stage the
+        # enabling of the feature because of the number of rebaselines needed.
+        # TODO(pdr): Remove this code and run web tests with
+        # CompositeAfterPaint.
+        if '--enable-blink-features=CompositeAfterPaint' not in cmd:
+            cmd.append('--disable-blink-features=CompositeAfterPaint')
+
         cmd = coalesce_repeated_switches(cmd)
         cmd.append('-')
         return cmd
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 470143e..2b2b59c7 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -121,6 +121,10 @@
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-alignment-implies-size-change-035.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-alignment-implies-size-change-036.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-baseline-004.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-1.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-2.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-3.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-4.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-001.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-002.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-003.html [ Failure ]
diff --git a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
index 56948e9..23ecbe2c 100644
--- a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
+++ b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
@@ -6,7 +6,6 @@
 # crbug.com/1138028 tracks the removal of these failure expectations
 
 # Crashes
-crbug.com/1012242 external/wpt/webvtt/rendering/cues-with-video/processing-model/disable_controls_reposition.html [ CRASH ]
 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-display-none-editable.html [ CRASH ]
 crbug.com/926685 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-010.html [ CRASH ]
 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-017.html [ CRASH ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 637e1a22..9179a5f3 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3926,9 +3926,6 @@
 
 # [css-grid]
 crbug.com/1045599 external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-safe-001.html [ Failure ]
-crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4.html [ Failure ]
-crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6.html [ Failure ]
-crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-004.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-003.html [ Failure ]
 crbug.com/759665 external/wpt/css/css-grid/animation/grid-template-columns-001.html [ Failure ]
@@ -3959,6 +3956,10 @@
 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-alignment-implies-size-change-035.html [ Failure ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-alignment-implies-size-change-036.html [ Failure ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-baseline-004.html [ Failure ]
+virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-1.html [ Failure ]
+virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-2.html [ Failure ]
+virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-3.html [ Failure ]
+virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-4.html [ Failure ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-001.html [ Failure ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-002.html [ Failure ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-003.html [ Failure ]
@@ -4008,18 +4009,6 @@
 virtual/layout-ng-grid/fast/css-grid-layout/grid-track-sizing-with-orthogonal-flows.html [ Failure ]
 virtual/layout-ng-grid/fast/css-grid-layout/maximize-tracks-definite-indefinite-height.html [ Failure ]
 
-# Bad stretching of svgs without aspect-ratio.
-crbug.com/1114013 external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5.html [ Failure ]
-crbug.com/1114013 external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8.html [ Failure ]
-crbug.com/1114013 external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-9.html [ Failure ]
-crbug.com/1114013 external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-10.html [ Failure ]
-
-# Open discussion of SVG aspect ratios at https://github.com/w3c/csswg-drafts/issues/6286
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7.html [ Failure ]
-
 # The 'last baseline' keyword is not implemented yet
 crbug.com/885175 external/wpt/css/css-grid/alignment/grid-item-self-baseline-001.html [ Skip ]
 crbug.com/885175 external/wpt/css/css-grid/alignment/grid-self-alignment-baseline-with-grid-001.html [ Skip ]
@@ -7080,3 +7069,6 @@
 # Now also flakily timing-out.
 crbug.com/1223773 external/wpt/webrtc/simulcast/getStats.https.html [ Failure Pass Timeout ]
 crbug.com/1193920 virtual/threaded-prefer-compositing/fast/scrolling/events/overscroll-event-fired-to-scrolled-element.html [ Failure Pass Timeout ]
+
+# Sheriff 2021-07-02
+crbug.com/1226112 http/tests/text-autosizing/narrow-iframe.html [ Failure Pass ]
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-1-ref.html
similarity index 73%
copy from third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html
copy to third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-1-ref.html
index 0d60375..1120e45f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-1-ref.html
@@ -1,6 +1,6 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>Reference: stretching works for replaced items with no aspect ratio</title>
+<title>Reference: stretching works for replaced items with a fallback aspect ratio</title>
 <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
 <link rel="author" title="Mozilla" href="https://mozilla.org">
 <style>
@@ -19,9 +19,6 @@
 
   img {
     display: block;
-    width: 300px;
-    height: 150px;
-    background: blue;
   }
 
   .justify {
@@ -35,17 +32,17 @@
   <img class="align justify">
 </div>
 <div>
-  <img class="align" style="width:20px">
+  <img class="align">
 </div>
 <div>
-  <img class="justify" style="height:0px">
+  <img class="justify">
 </div>
 <div>
-  <img style="width:20px; height:0px">
+  <img style="width:10px; height:20px">
 </div>
 
 <script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"></svg>'
+var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 100"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   var img = imgs[i];
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-10.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-1.html
similarity index 71%
rename from third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-10.html
rename to third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-1.html
index 6d8d7e8..909406f5 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-10.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-1.html
@@ -1,11 +1,12 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>stretching works for replaced items with no aspect ratio</title>
+<title>stretching works for replaced items with a fallback aspect ratio</title>
 <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
 <link rel="author" title="Mozilla" href="https://mozilla.org">
 <link rel="help" href="https://drafts.csswg.org/css-grid">
 <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281">
-<link rel="match" href="grid-item-no-aspect-ratio-stretch-8-ref.html">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6286#issuecomment-866986544">
+<link rel="match" href="grid-item-aspect-ratio-stretch-1-ref.html">
 <style>
   body {
     line-height: 0;
@@ -13,6 +14,7 @@
 
   div {
     display: inline-grid;
+    grid-template: 100% / 100%;
     height: 250px;
     width: 350px;
     background: grey;
@@ -41,7 +43,7 @@
 </div>
 
 <script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="0px" height="0px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
+var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 50 100"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   var img = imgs[i];
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-2-ref.html
similarity index 73%
copy from third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html
copy to third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-2-ref.html
index 0d60375..84d7950 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-2-ref.html
@@ -1,6 +1,6 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>Reference: stretching works for replaced items with no aspect ratio</title>
+<title>Reference: stretching works for replaced items with a fallback aspect ratio</title>
 <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
 <link rel="author" title="Mozilla" href="https://mozilla.org">
 <style>
@@ -19,9 +19,6 @@
 
   img {
     display: block;
-    width: 300px;
-    height: 150px;
-    background: blue;
   }
 
   .justify {
@@ -35,17 +32,17 @@
   <img class="align justify">
 </div>
 <div>
-  <img class="align" style="width:20px">
+  <img class="align">
 </div>
 <div>
-  <img class="justify" style="height:0px">
+  <img class="justify">
 </div>
 <div>
-  <img style="width:20px; height:0px">
+  <img style="width:20px; height:40px">
 </div>
 
 <script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"></svg>'
+var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 100"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   var img = imgs[i];
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-10.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-2.html
similarity index 71%
copy from third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-10.html
copy to third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-2.html
index 6d8d7e8..f5b7330 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-10.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-2.html
@@ -1,11 +1,12 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>stretching works for replaced items with no aspect ratio</title>
+<title>stretching works for replaced items with a fallback aspect ratio</title>
 <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
 <link rel="author" title="Mozilla" href="https://mozilla.org">
 <link rel="help" href="https://drafts.csswg.org/css-grid">
 <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281">
-<link rel="match" href="grid-item-no-aspect-ratio-stretch-8-ref.html">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6286#issuecomment-866986544">
+<link rel="match" href="grid-item-aspect-ratio-stretch-2-ref.html">
 <style>
   body {
     line-height: 0;
@@ -13,6 +14,7 @@
 
   div {
     display: inline-grid;
+    grid-template: 100% / 100%;
     height: 250px;
     width: 350px;
     background: grey;
@@ -41,7 +43,7 @@
 </div>
 
 <script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="0px" height="0px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
+var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="20px" viewBox="0 0 50 100"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   var img = imgs[i];
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-3-ref.html
similarity index 75%
copy from third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html
copy to third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-3-ref.html
index 0d60375..b85803e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-3-ref.html
@@ -1,6 +1,6 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>Reference: stretching works for replaced items with no aspect ratio</title>
+<title>Reference: stretching works for replaced items with a fallback aspect ratio</title>
 <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
 <link rel="author" title="Mozilla" href="https://mozilla.org">
 <style>
@@ -19,8 +19,6 @@
 
   img {
     display: block;
-    width: 300px;
-    height: 150px;
     background: blue;
   }
 
@@ -35,17 +33,17 @@
   <img class="align justify">
 </div>
 <div>
-  <img class="align" style="width:20px">
+  <img class="align">
 </div>
 <div>
-  <img class="justify" style="height:0px">
+  <img class="justify">
 </div>
 <div>
-  <img style="width:20px; height:0px">
+  <img style="width:0px; height:20px">
 </div>
 
 <script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"></svg>'
+var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 100"></svg>'
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   var img = imgs[i];
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-3.html
similarity index 73%
copy from third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7.html
copy to third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-3.html
index 29dec490..4320254 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-3.html
@@ -1,11 +1,12 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>stretching works for replaced items with no aspect ratio</title>
+<title>stretching works for replaced items with a fallback aspect ratio</title>
 <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
 <link rel="author" title="Mozilla" href="https://mozilla.org">
 <link rel="help" href="https://drafts.csswg.org/css-grid">
 <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281">
-<link rel="match" href="grid-item-no-aspect-ratio-stretch-7-ref.html">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6286#issuecomment-866986544">
+<link rel="match" href="grid-item-aspect-ratio-stretch-3-ref.html">
 <style>
   body {
     line-height: 0;
@@ -13,6 +14,7 @@
 
   div {
     display: inline-grid;
+    grid-template: 100% / 100%;
     height: 250px;
     width: 350px;
     background: grey;
@@ -42,7 +44,7 @@
 </div>
 
 <script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="0px" viewBox="0 0 50 100"></svg>'
+var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="0px" height="20px" viewBox="0 0 50 100"></svg>'
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   var img = imgs[i];
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-4-ref.html
similarity index 78%
rename from third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-4-ref.html
index 0d60375..8d7fc87 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-4-ref.html
@@ -1,6 +1,6 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>Reference: stretching works for replaced items with no aspect ratio</title>
+<title>Reference: stretching works for replaced items with a fallback aspect ratio</title>
 <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
 <link rel="author" title="Mozilla" href="https://mozilla.org">
 <style>
@@ -19,8 +19,6 @@
 
   img {
     display: block;
-    width: 300px;
-    height: 150px;
     background: blue;
   }
 
@@ -35,17 +33,17 @@
   <img class="align justify">
 </div>
 <div>
-  <img class="align" style="width:20px">
+  <img class="align">
 </div>
 <div>
-  <img class="justify" style="height:0px">
+  <img class="justify">
 </div>
 <div>
   <img style="width:20px; height:0px">
 </div>
 
 <script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"></svg>'
+var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 100"></svg>'
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   var img = imgs[i];
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-4.html
similarity index 78%
rename from third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7.html
rename to third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-4.html
index 29dec490..f689cce 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-aspect-ratio-stretch-4.html
@@ -1,11 +1,12 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>stretching works for replaced items with no aspect ratio</title>
+<title>stretching works for replaced items with a fallback aspect ratio</title>
 <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
 <link rel="author" title="Mozilla" href="https://mozilla.org">
 <link rel="help" href="https://drafts.csswg.org/css-grid">
 <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281">
-<link rel="match" href="grid-item-no-aspect-ratio-stretch-7-ref.html">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6286#issuecomment-866986544">
+<link rel="match" href="grid-item-aspect-ratio-stretch-4-ref.html">
 <style>
   body {
     line-height: 0;
@@ -13,6 +14,7 @@
 
   div {
     display: inline-grid;
+    grid-template: 100% / 100%;
     height: 250px;
     width: 350px;
     background: grey;
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4-ref.html
index 9edb8aa..4a543b4a 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4-ref.html
@@ -19,8 +19,8 @@
 
   img {
     display: block;
-    width: 300px;
-    height: 150px;
+    width: 0px;
+    height: 20px;
   }
 
   .justify {
@@ -34,17 +34,17 @@
   <img class="align justify">
 </div>
 <div>
-  <img class="align" style="width:10px">
+  <img class="align">
 </div>
 <div>
-  <img class="justify" style="height:20px">
+  <img class="justify">
 </div>
 <div>
-  <img style="width:10px; height:20px">
+  <img>
 </div>
 
 <script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 100"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
+var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   var img = imgs[i];
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4.html
index 1cf2849..a53c0fd 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4.html
@@ -13,6 +13,7 @@
 
   div {
     display: inline-grid;
+    grid-template: 100% / 100%;
     height: 250px;
     width: 350px;
     background: grey;
@@ -41,7 +42,7 @@
 </div>
 
 <script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 50 100"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
+var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="0px" height="20px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   var img = imgs[i];
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5-ref.html
index 6a34465..98f88fe 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5-ref.html
@@ -19,8 +19,8 @@
 
   img {
     display: block;
-    width: 300px;
-    height: 150px;
+    width: 20px;
+    height: 0px;
   }
 
   .justify {
@@ -34,17 +34,17 @@
   <img class="align justify">
 </div>
 <div>
-  <img class="align" style="width:20px">
+  <img class="align">
 </div>
 <div>
-  <img class="justify" style="width:350px; height:40px">
+  <img class="justify">
 </div>
 <div>
-  <img style="width:20px; height:40px">
+  <img>
 </div>
 
 <script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 100"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
+var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   var img = imgs[i];
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5.html
index 3000e9d..c28022c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5.html
@@ -13,6 +13,7 @@
 
   div {
     display: inline-grid;
+    grid-template: 100% / 100%;
     height: 250px;
     width: 350px;
     background: grey;
@@ -41,7 +42,7 @@
 </div>
 
 <script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="20px" viewBox="0 0 50 100"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
+var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="0px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   var img = imgs[i];
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6-ref.html
index 33f472e..8e989d1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6-ref.html
@@ -19,9 +19,8 @@
 
   img {
     display: block;
-    width: 300px;
-    height: 150px;
-    background: blue;
+    width: 0px;
+    height: 0px;
   }
 
   .justify {
@@ -35,17 +34,17 @@
   <img class="align justify">
 </div>
 <div>
-  <img class="align" style="width:0px">
+  <img class="align">
 </div>
 <div>
-  <img class="justify" style="height:20px">
+  <img class="justify">
 </div>
 <div>
-  <img style="width:0px; height:20px">
+  <img>
 </div>
 
 <script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"></svg>'
+var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   var img = imgs[i];
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6.html
index 655040eb..b4f172c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6.html
@@ -19,7 +19,6 @@
     margin: 10px;
     vertical-align: top;
   }
-  img { background: blue; }
 
   .justify {
     justify-self: stretch;
@@ -42,7 +41,7 @@
 </div>
 
 <script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="0px" height="20px" viewBox="0 0 50 100"></svg>'
+var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="0px" height="0px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   var img = imgs[i];
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8-ref.html
deleted file mode 100644
index 09dd273..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8-ref.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>Reference: stretching works for replaced items with no aspect ratio</title>
-<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
-<link rel="author" title="Mozilla" href="https://mozilla.org">
-<style>
-  body {
-    line-height: 0;
-  }
-
-  div {
-    display: inline-block;
-    height: 250px;
-    width: 350px;
-    background: grey;
-    margin: 10px;
-    vertical-align: top;
-  }
-</style>
-<div>
-</div>
-<div>
-</div>
-<div>
-</div>
-<div>
-</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8.html
deleted file mode 100644
index 933ddd05..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>stretching works for replaced items with no aspect ratio</title>
-<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
-<link rel="author" title="Mozilla" href="https://mozilla.org">
-<link rel="help" href="https://drafts.csswg.org/css-grid">
-<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281">
-<link rel="match" href="grid-item-no-aspect-ratio-stretch-8-ref.html">
-<style>
-  body {
-    line-height: 0;
-  }
-
-  div {
-    display: inline-grid;
-    height: 250px;
-    width: 350px;
-    background: grey;
-    margin: 10px;
-    vertical-align: top;
-  }
-
-  .justify {
-    justify-self: stretch;
-  }
-  .align {
-    align-self: stretch;
-  }
-</style>
-<div>
-  <img class="align justify">
-</div>
-<div>
-  <img class="align">
-</div>
-<div>
-  <img class="justify">
-</div>
-<div>
-  <img>
-</div>
-
-<script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="0px" height="20px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
-var imgs = document.querySelectorAll('img');
-for (var i = 0; i < imgs.length; ++i) {
-  var img = imgs[i];
-  img.src = url;
-}
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-9.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-9.html
deleted file mode 100644
index 367adae..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-9.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>stretching works for replaced items with no aspect ratio</title>
-<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
-<link rel="author" title="Mozilla" href="https://mozilla.org">
-<link rel="help" href="https://drafts.csswg.org/css-grid">
-<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281">
-<link rel="match" href="grid-item-no-aspect-ratio-stretch-8-ref.html">
-<style>
-  body {
-    line-height: 0;
-  }
-
-  div {
-    display: inline-grid;
-    height: 250px;
-    width: 350px;
-    background: grey;
-    margin: 10px;
-    vertical-align: top;
-  }
-
-  .justify {
-    justify-self: stretch;
-  }
-  .align {
-    align-self: stretch;
-  }
-</style>
-<div>
-  <img class="align justify">
-</div>
-<div>
-  <img class="align">
-</div>
-<div>
-  <img class="justify">
-</div>
-<div>
-  <img>
-</div>
-
-<script>
-var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="0px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>'
-var imgs = document.querySelectorAll('img');
-for (var i = 0; i < imgs.length; ++i) {
-  var img = imgs[i];
-  img.src = url;
-}
-</script>
diff --git a/third_party/blink/web_tests/wpt_internal/js-self-profiling/time-domain.window.js b/third_party/blink/web_tests/wpt_internal/js-self-profiling/time-domain.window.js
new file mode 100644
index 0000000..4967684
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/js-self-profiling/time-domain.window.js
@@ -0,0 +1,16 @@
+// META: script=resources/profile-utils.js
+
+promise_test(async () => {
+  const start = performance.now();
+
+  const profiler = new Profiler({ sampleInterval: 10 });
+  ProfileUtils.forceSample();
+  const trace = await profiler.stop();
+
+  const end = performance.now();
+
+  assert_greater_than(trace.samples.length, 0);
+  for (const sample of trace.samples) {
+    assert_between_inclusive(sample.timestamp, start, end);
+  }
+}, 'sample timestamps use the current high-resolution time');
diff --git a/tools/clang/blink_gc_plugin/RecordInfo.cpp b/tools/clang/blink_gc_plugin/RecordInfo.cpp
index d16a828..23c70665 100644
--- a/tools/clang/blink_gc_plugin/RecordInfo.cpp
+++ b/tools/clang/blink_gc_plugin/RecordInfo.cpp
@@ -2,8 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "Config.h"
 #include "RecordInfo.h"
+
+#include <string>
+
+#include "Config.h"
 #include "clang/Sema/Sema.h"
 
 using namespace clang;
@@ -241,9 +244,24 @@
               .first->second;
 }
 
+bool RecordInfo::HasTypeAlias(std::string marker_name) const {
+  for (Decl* decl : record_->decls()) {
+    TypeAliasDecl* alias = dyn_cast<TypeAliasDecl>(decl);
+    if (!alias)
+      continue;
+    if (alias->getName() == marker_name)
+      return true;
+  }
+  return false;
+}
+
 bool RecordInfo::IsStackAllocated() {
   if (is_stack_allocated_ == kNotComputed) {
     is_stack_allocated_ = kFalse;
+    if (HasTypeAlias("IsStackAllocatedTypeMarker")) {
+      is_stack_allocated_ = kTrue;
+      return is_stack_allocated_;
+    }
     for (Bases::iterator it = GetBases().begin();
          it != GetBases().end();
          ++it) {
diff --git a/tools/clang/blink_gc_plugin/RecordInfo.h b/tools/clang/blink_gc_plugin/RecordInfo.h
index 20ef058..d6ef638 100644
--- a/tools/clang/blink_gc_plugin/RecordInfo.h
+++ b/tools/clang/blink_gc_plugin/RecordInfo.h
@@ -134,6 +134,8 @@
 
   bool HasOptionalFinalizer();
 
+  bool HasTypeAlias(std::string marker_name) const;
+
   RecordCache* cache_;
   clang::CXXRecordDecl* record_;
   const std::string name_;
diff --git a/tools/clang/blink_gc_plugin/tests/heap/stubs.h b/tools/clang/blink_gc_plugin/tests/heap/stubs.h
index 2e7dfc6..7c7c88d 100644
--- a/tools/clang/blink_gc_plugin/tests/heap/stubs.h
+++ b/tools/clang/blink_gc_plugin/tests/heap/stubs.h
@@ -366,10 +366,13 @@
   void* operator new(size_t) = delete; \
   void* operator new(size_t, void*) = delete;
 
-#define STACK_ALLOCATED()                            \
- private:                                            \
-  __attribute__((annotate("blink_stack_allocated"))) \
-  void* operator new(size_t) = delete;               \
+#define STACK_ALLOCATED()                                                \
+ public:                                                                 \
+  using IsStackAllocatedTypeMarker [[maybe_unused]] = int;               \
+                                                                         \
+ private:                                                                \
+  __attribute__((annotate("blink_stack_allocated"))) void* operator new( \
+      size_t) = delete;                                                  \
   void* operator new(size_t, void*) = delete;
 
 #define DISALLOW_NEW_EXCEPT_PLACEMENT_NEW() \
diff --git a/tools/clang/blink_gc_plugin/tests/stack_allocated.txt b/tools/clang/blink_gc_plugin/tests/stack_allocated.txt
index 8c678aca..f6742df0 100644
--- a/tools/clang/blink_gc_plugin/tests/stack_allocated.txt
+++ b/tools/clang/blink_gc_plugin/tests/stack_allocated.txt
@@ -23,8 +23,8 @@
 ./stack_allocated.h:44:3: warning: [blink-gc] Garbage collected class 'DerivedHeapObject2' is not permitted to override its new operator.
   STACK_ALLOCATED();
   ^
-./heap/stubs.h:371:3: note: expanded from macro 'STACK_ALLOCATED'
-  __attribute__((annotate("blink_stack_allocated"))) \
+./heap/stubs.h:374:3: note: expanded from macro 'STACK_ALLOCATED'
+  __attribute__((annotate("blink_stack_allocated"))) void* operator new( \
   ^
 In file included from stack_allocated.cpp:5:
 ./stack_allocated.h:24:5: warning: [blink-gc] Class 'StackObject' has untraced or not traceable fields.
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 29f4ff0..a56fe29 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -34,18 +34,11 @@
       'win64-chrome': 'official_goma_x64',
     },
     'chrome.pgo': {
+      'android-arm32-pgo': 'official_goma_android_arm32_pgo',
       'linux-pgo': 'official_goma_linux_pgo',
-      'linux-pgo-beta': 'official_goma_linux_pgo',
-      'linux-pgo-stable': 'official_goma_linux_pgo',
       'mac-pgo': 'official_goma_mac_pgo',
-      'mac-pgo-beta': 'official_goma_mac_pgo',
-      'mac-pgo-stable': 'official_goma_mac_pgo',
       'win32-pgo': 'official_goma_x86_pgo',
-      'win32-pgo-beta': 'official_goma_x86_pgo',
-      'win32-pgo-stable': 'official_goma_x86_pgo',
       'win64-pgo': 'official_goma_x64_pgo',
-      'win64-pgo-beta': 'official_goma_x64_pgo',
-      'win64-pgo-stable': 'official_goma_x64_pgo',
     },
     # Take care when changing any of these builders to ensure that you do not
     # include a configuration with 'chrome_with_codecs' since these builders
@@ -909,6 +902,7 @@
     },
 
     'tryserver.chrome.pgo': {
+      'android-arm32-pgo': 'official_goma_android_arm32_pgo',
       'linux-pgo': 'official_goma_linux_pgo',
       'mac-pgo': 'official_goma_mac_pgo',
       'win32-pgo': 'official_goma_x86_pgo',
@@ -1171,6 +1165,7 @@
       'win10_chromium_x64_rel_ng_exp': 'release_trybot',
       'win10_chromium_x64_rel_ng_rts': 'gpu_tests_release_trybot_resource_allowlisting_code_coverage',
       'win10_chromium_x64_20h2_fyi_rel_ng': 'gpu_tests_release_trybot_resource_allowlisting_code_coverage',
+      'win10-rel-orchestrator': 'gpu_tests_release_trybot_resource_allowlisting_code_coverage',
       'win-annotator-rel': 'release_trybot',
       'win-asan': 'asan_clang_fuzzer_static_v8_heap_minimal_symbols_release',
       'win-celab-try-rel': 'release_bot_minimal_symbols',
@@ -2445,6 +2440,10 @@
       'official', 'goma',
     ],
 
+    'official_goma_android_arm32_pgo': [
+      'official', 'goma', 'android_official', 'arm', 'pgo_phase_1',
+    ],
+
     'official_goma_mac': [
       'official', 'goma', 'no_widevine_cdm_host_verification',
     ],
@@ -2853,6 +2852,15 @@
       'gn_args': 'is_java_debug=true',
     },
 
+    'android_official': {
+      'mixins': ['android', 'android_official_webview_target', 'android_without_codecs', 'clank_custom_args', 'enable_remoting', 'enable_webview_bundles', 'ffmpeg_branding_chrome', 'official'],
+      'gn_args': 'use_signing_keys=true'
+    },
+
+    'android_official_webview_target': {
+      'gn_args': 'system_webview_apk_target="//clank/android_webview:system_webview_google_apk"',
+    },
+
     'android_without_codecs': {
       'gn_args': 'target_os="android"',
     },
@@ -3003,6 +3011,10 @@
       'gn_args': 'is_clang=true',
     },
 
+    'clank_custom_args': {
+      'args_file': '//clank/build/custom_args.gn',
+    },
+
     # Settings used by the codesearch builders to generate cross-references.
     'codesearch': {
       'gn_args': 'clang_use_chrome_plugins=false enable_kythe_annotations=true',
@@ -3123,6 +3135,10 @@
       'gn_args': 'skip_archive_compression=false',
     },
 
+    'enable_remoting': {
+      'gn_args': 'enable_remoting=true',
+    },
+
     'enable_v8_oilpan': {
       'gn_args': 'enable_blink_heap_use_v8_oilpan=true',
     },
@@ -3139,6 +3155,10 @@
       'gn_args': 'enable_vulkan=true',
     },
 
+    'enable_webview_bundles': {
+      'gn_args': 'enable_webview_bundles = true'
+    },
+
     'eve': {
       'args_file': '//build/args/chromeos/eve.gni',
     },
diff --git a/tools/mb/mb_config_expectations/chrome.pgo.json b/tools/mb/mb_config_expectations/chrome.pgo.json
index eba01ee..6cb7c02 100644
--- a/tools/mb/mb_config_expectations/chrome.pgo.json
+++ b/tools/mb/mb_config_expectations/chrome.pgo.json
@@ -1,4 +1,22 @@
 {
+  "android-arm32-pgo": {
+    "args_file": "//clank/build/custom_args.gn",
+    "gn_args": {
+      "chrome_pgo_phase": 1,
+      "enable_remoting": true,
+      "enable_webview_bundles": true,
+      "ffmpeg_branding": "Chrome",
+      "is_chrome_branded": true,
+      "is_official_build": true,
+      "proprietary_codecs": true,
+      "strip_absolute_paths_from_debug_symbols": true,
+      "system_webview_apk_target": "//clank/android_webview:system_webview_google_apk",
+      "target_cpu": "arm",
+      "target_os": "android",
+      "use_goma": true,
+      "use_signing_keys": true
+    }
+  },
   "linux-pgo": {
     "gn_args": {
       "chrome_pgo_phase": 1,
@@ -10,28 +28,6 @@
       "use_goma": true
     }
   },
-  "linux-pgo-beta": {
-    "gn_args": {
-      "chrome_pgo_phase": 1,
-      "is_chrome_branded": true,
-      "is_component_build": false,
-      "is_official_build": true,
-      "strip_absolute_paths_from_debug_symbols": true,
-      "symbol_level": 0,
-      "use_goma": true
-    }
-  },
-  "linux-pgo-stable": {
-    "gn_args": {
-      "chrome_pgo_phase": 1,
-      "is_chrome_branded": true,
-      "is_component_build": false,
-      "is_official_build": true,
-      "strip_absolute_paths_from_debug_symbols": true,
-      "symbol_level": 0,
-      "use_goma": true
-    }
-  },
   "mac-pgo": {
     "gn_args": {
       "chrome_pgo_phase": 1,
@@ -46,34 +42,6 @@
       "use_goma": true
     }
   },
-  "mac-pgo-beta": {
-    "gn_args": {
-      "chrome_pgo_phase": 1,
-      "enable_keystone_registration_framework": false,
-      "enable_widevine_cdm_host_verification": false,
-      "ignore_missing_widevine_signing_cert": true,
-      "is_chrome_branded": true,
-      "is_component_build": false,
-      "is_official_build": true,
-      "strip_absolute_paths_from_debug_symbols": true,
-      "symbol_level": 0,
-      "use_goma": true
-    }
-  },
-  "mac-pgo-stable": {
-    "gn_args": {
-      "chrome_pgo_phase": 1,
-      "enable_keystone_registration_framework": false,
-      "enable_widevine_cdm_host_verification": false,
-      "ignore_missing_widevine_signing_cert": true,
-      "is_chrome_branded": true,
-      "is_component_build": false,
-      "is_official_build": true,
-      "strip_absolute_paths_from_debug_symbols": true,
-      "symbol_level": 0,
-      "use_goma": true
-    }
-  },
   "win32-pgo": {
     "gn_args": {
       "chrome_pgo_phase": 1,
@@ -87,32 +55,6 @@
       "use_goma": true
     }
   },
-  "win32-pgo-beta": {
-    "gn_args": {
-      "chrome_pgo_phase": 1,
-      "enable_resource_allowlist_generation": false,
-      "is_chrome_branded": true,
-      "is_component_build": false,
-      "is_official_build": true,
-      "strip_absolute_paths_from_debug_symbols": true,
-      "symbol_level": 0,
-      "target_cpu": "x86",
-      "use_goma": true
-    }
-  },
-  "win32-pgo-stable": {
-    "gn_args": {
-      "chrome_pgo_phase": 1,
-      "enable_resource_allowlist_generation": false,
-      "is_chrome_branded": true,
-      "is_component_build": false,
-      "is_official_build": true,
-      "strip_absolute_paths_from_debug_symbols": true,
-      "symbol_level": 0,
-      "target_cpu": "x86",
-      "use_goma": true
-    }
-  },
   "win64-pgo": {
     "gn_args": {
       "chrome_pgo_phase": 1,
@@ -125,31 +67,5 @@
       "target_cpu": "x64",
       "use_goma": true
     }
-  },
-  "win64-pgo-beta": {
-    "gn_args": {
-      "chrome_pgo_phase": 1,
-      "enable_resource_allowlist_generation": false,
-      "is_chrome_branded": true,
-      "is_component_build": false,
-      "is_official_build": true,
-      "strip_absolute_paths_from_debug_symbols": true,
-      "symbol_level": 0,
-      "target_cpu": "x64",
-      "use_goma": true
-    }
-  },
-  "win64-pgo-stable": {
-    "gn_args": {
-      "chrome_pgo_phase": 1,
-      "enable_resource_allowlist_generation": false,
-      "is_chrome_branded": true,
-      "is_component_build": false,
-      "is_official_build": true,
-      "strip_absolute_paths_from_debug_symbols": true,
-      "symbol_level": 0,
-      "target_cpu": "x64",
-      "use_goma": true
-    }
   }
 }
\ No newline at end of file
diff --git a/tools/mb/mb_config_expectations/tryserver.chrome.pgo.json b/tools/mb/mb_config_expectations/tryserver.chrome.pgo.json
index 1d89704..6cb7c02 100644
--- a/tools/mb/mb_config_expectations/tryserver.chrome.pgo.json
+++ b/tools/mb/mb_config_expectations/tryserver.chrome.pgo.json
@@ -1,4 +1,22 @@
 {
+  "android-arm32-pgo": {
+    "args_file": "//clank/build/custom_args.gn",
+    "gn_args": {
+      "chrome_pgo_phase": 1,
+      "enable_remoting": true,
+      "enable_webview_bundles": true,
+      "ffmpeg_branding": "Chrome",
+      "is_chrome_branded": true,
+      "is_official_build": true,
+      "proprietary_codecs": true,
+      "strip_absolute_paths_from_debug_symbols": true,
+      "system_webview_apk_target": "//clank/android_webview:system_webview_google_apk",
+      "target_cpu": "arm",
+      "target_os": "android",
+      "use_goma": true,
+      "use_signing_keys": true
+    }
+  },
   "linux-pgo": {
     "gn_args": {
       "chrome_pgo_phase": 1,
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.win.json b/tools/mb/mb_config_expectations/tryserver.chromium.win.json
index 38a6577..2a11a8a 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.win.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.win.json
@@ -253,6 +253,21 @@
       "use_libfuzzer": true
     }
   },
+  "win10-rel-orchestrator": {
+    "gn_args": {
+      "blink_enable_generated_code_formatting": false,
+      "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt",
+      "dcheck_always_on": true,
+      "enable_resource_allowlist_generation": false,
+      "ffmpeg_branding": "Chrome",
+      "is_component_build": false,
+      "is_debug": false,
+      "proprietary_codecs": true,
+      "symbol_level": 0,
+      "use_clang_coverage": true,
+      "use_goma": true
+    }
+  },
   "win10_chromium_inverse_fieldtrials_x64_fyi_rel_ng": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 958b39b1..44d2ffa 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -46350,6 +46350,7 @@
   <int value="-1826649921" label="ContextualSuggestionsButton:disabled"/>
   <int value="-1826309726" label="ArcCustomTabsExperiment:disabled"/>
   <int value="-1823193038" label="QuietNotificationPrompts:disabled"/>
+  <int value="-1823073826" label="TabGroupsSave:disabled"/>
   <int value="-1822825246" label="DragFromShelfToHomeOrOverview:enabled"/>
   <int value="-1821058653" label="enable-delay-agnostic-aec"/>
   <int value="-1818947212" label="OutOfBlinkCors:disabled"/>
@@ -46394,6 +46395,7 @@
   <int value="-1778993296" label="ContextualSearchMlTapSuppression:disabled"/>
   <int value="-1778202807" label="DelayAsyncScriptExecution:enabled"/>
   <int value="-1776351704" label="DesktopPWAsOmniboxInstall:disabled"/>
+  <int value="-1775842908" label="EnableOAuthIpp:disabled"/>
   <int value="-1774818943" label="VrWebInputEditing:enabled"/>
   <int value="-1772942854" label="LongPressBackForHistory:enabled"/>
   <int value="-1772905637"
@@ -46972,6 +46974,7 @@
   <int value="-1319688939" label="ignore-gpu-blacklist"/>
   <int value="-1318914924" label="OverflowIconsForMediaControls:enabled"/>
   <int value="-1316769004" label="CrossOriginOpenerPolicyReporting:disabled"/>
+  <int value="-1314757884" label="TabGroupsSave:enabled"/>
   <int value="-1314603238" label="ChromeHomePullToRefreshIphAtTop:enabled"/>
   <int value="-1313810940" label="StrictOriginIsolation:disabled"/>
   <int value="-1311575452"
@@ -47219,6 +47222,7 @@
   <int value="-1134412904" label="PrivacySandboxSettings:disabled"/>
   <int value="-1134307340" label="stop-loading-in-background:enabled"/>
   <int value="-1132704128" label="AndroidPaymentAppsFilter:disabled"/>
+  <int value="-1128981647" label="EnableOAuthIpp:enabled"/>
   <int value="-1128912963" label="MediaControlsExpandGesture:disabled"/>
   <int value="-1128221789"
       label="AutofillEnableSurfacingServerCardNickname:enabled"/>
@@ -48938,6 +48942,8 @@
   <int value="359896413" label="SplitPartiallyOccludedQuads:disabled"/>
   <int value="360391863" label="NTPOfflineBadge:enabled"/>
   <int value="360599302" label="enable-gpu-rasterization"/>
+  <int value="362623880"
+      label="HoldingSpaceInProgressDownloadsIntegration:disabled"/>
   <int value="362644448" label="memlog-in-process"/>
   <int value="362853088" label="AmbientColor:disabled"/>
   <int value="363997248" label="UseXpsForPrinting:enabled"/>
@@ -49150,6 +49156,8 @@
   <int value="529235584" label="PhoneHub:enabled"/>
   <int value="530828403" label="AllowStartingServiceManagerOnly:disabled"/>
   <int value="533064367" label="WebRtcHideLocalIpsWithMdns:disabled"/>
+  <int value="533115840"
+      label="HoldingSpaceInProgressDownloadsIntegration:enabled"/>
   <int value="535131384" label="OmniboxTailSuggestions:enabled"/>
   <int value="535194142" label="TemporaryHoldingSpacePreviews:enabled"/>
   <int value="535976218" label="enable-plugin-power-saver"/>
@@ -61350,6 +61358,44 @@
   <int value="17" label="FlingToCloseTabletTouch"/>
 </enum>
 
+<enum name="OverviewEndAction">
+  <int value="0"
+      label="SplitView related, e.g, both snapped, window activation"/>
+  <int value="1" label="Dragging window from shelf in tablet mode"/>
+  <int value="2" label="Exiting overview to home launcher"/>
+  <int value="3" label="Click or tap outside of the windows in overview"/>
+  <int value="4" label="Window activated in overview"/>
+  <int value="5" label="Last window in overview being removed"/>
+  <int value="6" label="Display added while in overivew"/>
+  <int value="7" label="Pressing shortcut F5"/>
+  <int value="8" label="Pressing key Escape or Back"/>
+  <int value="9" label="Desk activation"/>
+  <int value="10" label="Pressing overview button"/>
+  <int value="11" label="Long pressing overview button in split view mode"/>
+  <int value="12" label="Three fingers swiping down on the trackpad"/>
+  <int value="13" label="Enabled Docked Magnifier"/>
+  <int value="14" label="Switching user happens with multi users login"/>
+  <int value="15" label="Started window cycle"/>
+  <int value="16" label="Opened app list while in clamshell mode"/>
+  <int value="17" label="Shelf alignment changed"/>
+  <int value="18" label="Shutting down"/>
+  <int value="19" label="Exiting overview through dev tools"/>
+  <int value="20" label="Exiting overview in tests"/>
+</enum>
+
+<enum name="OverviewStartAction">
+  <int value="0" label="SplitView related, e.g, one window snapped"/>
+  <int value="1" label="Pressing shortcut F5"/>
+  <int value="2" label="Dragging window from shelf in tablet mode"/>
+  <int value="3" label="Going from home launcher to overview"/>
+  <int value="4" label="Pressing overview button"/>
+  <int value="5" label="Long pressing overview button in split view mode"/>
+  <int value="6" label="Going from Bento bar to overview"/>
+  <int value="7" label="Three fingers swiping up on the trackpad"/>
+  <int value="8" label="Entering overview through dev tools"/>
+  <int value="9" label="Entering overview in tests"/>
+</enum>
+
 <enum name="P2PLookupResult">
   <int value="0" label="Found"/>
   <int value="1" label="Not Found"/>
@@ -86438,6 +86484,7 @@
   <int value="2" label="Failed (no versions)"/>
   <int value="3" label="Failed (no file descriptors)"/>
   <int value="4" label="Failed (error opening file descriptors)"/>
+  <int value="5" label="Failed (Component Updater SafeMode enabled)"/>
 </enum>
 
 <enum name="WebViewDrawAndSubmissionType">
diff --git a/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml b/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml
index 52bdeac..711f931 100644
--- a/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml
@@ -291,6 +291,30 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Accessibility.CrosDictation.ListeningDuration.NetworkRecognition"
+    units="ms" expires_after="2021-12-06">
+  <owner>katie@chromium.org</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    Duration that the network speech recognition service was listening for
+    dictation. Recorded each time a user toggles dictation on until dictation is
+    stopped, either by the user action, error, or timeout.
+  </summary>
+</histogram>
+
+<histogram
+    name="Accessibility.CrosDictation.ListeningDuration.OnDeviceRecognition"
+    units="ms" expires_after="2021-12-06">
+  <owner>katie@chromium.org</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    Duration that the on-device speech recognition service was listening for
+    dictation. Recorded each time a user toggles dictation on until dictation is
+    stopped, either by the user action, error, or timeout.
+  </summary>
+</histogram>
+
 <histogram name="Accessibility.CrosDictation.ToggleDictationMethod"
     enum="CrosDictationToggleDictationMethod" expires_after="2021-11-21">
   <owner>anastasi@google.com</owner>
@@ -518,7 +542,7 @@
 </histogram>
 
 <histogram name="Accessibility.CrosStickyKeys" enum="BooleanEnabled"
-    expires_after="M93">
+    expires_after="M96">
   <owner>dmazzoni@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
   <owner>tengs@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/android/histograms.xml b/tools/metrics/histograms/histograms_xml/android/histograms.xml
index b66e4cec..47306c8 100644
--- a/tools/metrics/histograms/histograms_xml/android/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/android/histograms.xml
@@ -2892,6 +2892,17 @@
   </summary>
 </histogram>
 
+<histogram name="Android.WebView.ComponentUpdater.SafeModeActionExecuted"
+    enum="Boolean" expires_after="2022-06-30">
+  <owner>nator@chromium.org</owner>
+  <owner>src/android_webview/OWNERS</owner>
+  <summary>
+    Whether a SafeMode action was executed by Component Updater services. This
+    is logged whenever a nonembedded Component Updater service checks whether
+    SafeMode is enabled.
+  </summary>
+</histogram>
+
 <histogram name="Android.WebView.ComponentUpdater.UnexpectedExit"
     enum="Boolean" expires_after="2021-10-08">
   <owner>nator@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/ash/histograms.xml b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
index 5ce777d..3b0943b 100644
--- a/tools/metrics/histograms/histograms_xml/ash/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
@@ -734,6 +734,17 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.Desks.BentoBarEnabled" enum="Boolean"
+    expires_after="2022-05-19">
+  <owner>minch@chromium.org</owner>
+  <owner>janetmac@chromium.org</owner>
+  <summary>
+    Emits true if a user clicked `Show deskbar` inside the context menu of bento
+    bar or desks bar in overview mode, false if `Hide deskbar` inside the same
+    context menu being clicked.
+  </summary>
+</histogram>
+
 <histogram name="Ash.Desks.ConsecutiveDailyVisits" units="days"
     expires_after="2022-02-19">
   <owner>chinsenj@chromium.org</owner>
@@ -1631,6 +1642,16 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.Overview.EndAction" enum="OverviewEndAction"
+    expires_after="2022-05-19">
+  <owner>minch@chromium.org</owner>
+  <owner>janetmac@chromium.org</owner>
+  <summary>
+    Emitted when exiting overview mode. Recording the reasons for ending
+    overview mode. E.g, pressing the overview button at status area.
+  </summary>
+</histogram>
+
 <histogram name="Ash.Overview.Items" units="units" expires_after="2022-04-08">
   <owner>chinsenj@chromium.org</owner>
   <owner>nupurjain@chromium.org</owner>
@@ -1698,6 +1719,16 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.Overview.StartAction" enum="OverviewStartAction"
+    expires_after="2022-05-19">
+  <owner>minch@chromium.org</owner>
+  <owner>janetmac@chromium.org</owner>
+  <summary>
+    Emitted when entering overview mode. Recording the reasons for starting
+    overview mode. E.g, pressing the overview button at status area.
+  </summary>
+</histogram>
+
 <histogram name="Ash.Overview.TimeBetweenActiveWindowChanges" units="seconds"
     expires_after="2022-04-08">
   <owner>chinsenj@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index e491294..56d7c5e 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -11660,6 +11660,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="NewTabPageProviders" separator=".">
+  <obsolete>
+    Removed in 06 2021.
+  </obsolete>
   <suffix name="allowlist"
       label="Installed allowlist entry point suggestions."/>
   <suffix name="client" label="Suggestions coming from the client."/>
@@ -11735,7 +11738,11 @@
       Deprecated NTP replaced by WebUI NTP M91.
     </obsolete>
   </suffix>
-  <suffix name="MostLikely" label="Loaded server-side suggestions."/>
+  <suffix name="MostLikely" label="Loaded server-side suggestions.">
+    <obsolete>
+      Removed in 06 2021.
+    </obsolete>
+  </suffix>
   <suffix name="MostVisited" label="Loaded client-side suggestions."/>
   <suffix name="NewTab" label="NTP loaded on a new tab."/>
   <suffix name="Startup" label="NTP loaded during browser startup."/>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 5bae464..90485154 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,12 +5,12 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/win/1de765c9f44ba03cf19433a4e4773c70dff7fdc2/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "483154c0035fe858cc81c6bbdee5952f926f6129",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/d27cd04c1f2558bc29856c1357d79f56f92e759b/trace_processor_shell"
+            "hash": "35f8312274ca0f4e183d62a2553c1fce1126ad84",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/b0345c864e4a2ddeeb3b5084fb6fc7b856957ac8/trace_processor_shell"
         },
         "linux": {
-            "hash": "74346658e719c8b2669a1a35bb89b4e2ac7cc794",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/1de765c9f44ba03cf19433a4e4773c70dff7fdc2/trace_processor_shell"
+            "hash": "08053337518f6063718f9ef61292a17475ec550d",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/b0345c864e4a2ddeeb3b5084fb6fc7b856957ac8/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accelerated_widget_mac/ca_transaction_observer.h b/ui/accelerated_widget_mac/ca_transaction_observer.h
index 3155897c..44de148 100644
--- a/ui/accelerated_widget_mac/ca_transaction_observer.h
+++ b/ui/accelerated_widget_mac/ca_transaction_observer.h
@@ -9,16 +9,12 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/observer_list.h"
 #include "base/time/time.h"
 
 #include "ui/accelerated_widget_mac/accelerated_widget_mac_export.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 namespace ui {
 
 // CATransactionCoordinator is an interface to undocumented macOS APIs which
diff --git a/ui/accessibility/ax_active_popup.cc b/ui/accessibility/ax_active_popup.cc
index 40ef023..366baf1 100644
--- a/ui/accessibility/ax_active_popup.cc
+++ b/ui/accessibility/ax_active_popup.cc
@@ -5,7 +5,6 @@
 #include "ui/accessibility/ax_active_popup.h"
 
 #include "base/macros.h"
-#include "base/no_destructor.h"
 
 namespace ui {
 
@@ -17,8 +16,8 @@
   // currently active autofill popup. This singleton is used for communicating
   // the live status of the autofill popup between web contents and Views. The
   // assumption here is that only one autofill popup can exist at a time.
-  static base::NoDestructor<absl::optional<AXNodeID>> active_popup_ax_unique_id;
-  return *active_popup_ax_unique_id;
+  static absl::optional<AXNodeID> active_popup_ax_unique_id;
+  return active_popup_ax_unique_id;
 }
 
 }  // namespace
diff --git a/ui/accessibility/ax_tree_id.cc b/ui/accessibility/ax_tree_id.cc
index 15c4f3e..c1050f4 100644
--- a/ui/accessibility/ax_tree_id.cc
+++ b/ui/accessibility/ax_tree_id.cc
@@ -8,7 +8,6 @@
 #include <iostream>
 
 #include "base/check.h"
-#include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "base/util/values/values_util.h"
 #include "base/values.h"
@@ -108,9 +107,8 @@
 }
 
 const AXTreeID& AXTreeIDUnknown() {
-  static const base::NoDestructor<AXTreeID> ax_tree_id_unknown(
-      ax::mojom::AXTreeIDType::kUnknown);
-  return *ax_tree_id_unknown;
+  static const AXTreeID ax_tree_id_unknown(ax::mojom::AXTreeIDType::kUnknown);
+  return ax_tree_id_unknown;
 }
 
 }  // namespace ui
diff --git a/ui/accessibility/ax_tree_id.h b/ui/accessibility/ax_tree_id.h
index 9bd07f9..a1da6d9 100644
--- a/ui/accessibility/ax_tree_id.h
+++ b/ui/accessibility/ax_tree_id.h
@@ -7,7 +7,6 @@
 
 #include <string>
 
-#include "base/no_destructor.h"
 #include "base/unguessable_token.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/accessibility/ax_base_export.h"
@@ -66,7 +65,7 @@
   explicit AXTreeID(const std::string& string);
 
   friend struct mojo::UnionTraits<ax::mojom::AXTreeIDDataView, ui::AXTreeID>;
-  friend class base::NoDestructor<AXTreeID>;
+  friend AX_BASE_EXPORT const AXTreeID& AXTreeIDUnknown();
   friend void swap(AXTreeID& first, AXTreeID& second);
 
   ax::mojom::AXTreeIDType type_;
@@ -82,7 +81,7 @@
                                         const AXTreeID& value);
 
 // The value to use when an AXTreeID is unknown.
-AX_BASE_EXPORT extern const AXTreeID& AXTreeIDUnknown();
+AX_BASE_EXPORT const AXTreeID& AXTreeIDUnknown();
 
 }  // namespace ui
 
diff --git a/ui/android/java/res/values/color_palette.xml b/ui/android/java/res/values/color_palette.xml
index 94cdbb9..8ef7222 100644
--- a/ui/android/java/res/values/color_palette.xml
+++ b/ui/android/java/res/values/color_palette.xml
@@ -7,7 +7,8 @@
 <resources xmlns:tools="http://schemas.android.com/tools">
     <!-- 2021 color palette -->
     <color name="modern_blue_100">#D3E3FD</color>
-    <color name="modern_blue_200">#A8C7FA</color>
+    <!--TODO: Replace with baseline_primary_200 -->
+    <color name="modern_blue_200">@color/baseline_primary_200</color>
     <color name="modern_blue_600">#0B57D0</color>
     <color name="modern_grey_100_alpha_12">#1EE3E3E3</color>
     <color name="modern_grey_900_alpha_12">#1E1F1F1F</color>
@@ -15,6 +16,13 @@
     <color name="neutral_variant_100">#E1E3E1</color>
     <color name="neutral_variant_400">#8E918F</color>
 
+    <color name="baseline_neutral_100">#E3E3E3</color>
+    <color name="baseline_neutral_variant_200">#C4C7C5</color>
+    <color name="baseline_primary_200">#A8C7FA</color>
+    <color name="baseline_primary_800">#062E6F</color>
+
+    <color name="baseline_neutral_900_with_neutral_200_alpha_12_with_primary_200_alpha_2">#353637</color>
+
     <!-- Modern color palette -->
     <color name="modern_white">@android:color/white</color>
     <color name="modern_blue_300">#8AB4F8</color>
diff --git a/ui/aura/window_tree_host.cc b/ui/aura/window_tree_host.cc
index 28bf20d5..3ccf7461 100644
--- a/ui/aura/window_tree_host.cc
+++ b/ui/aura/window_tree_host.cc
@@ -344,6 +344,31 @@
   return nullptr;
 }
 
+void WindowTreeHost::LockMouse(Window* window) {
+  Window* root_window = window->GetRootWindow();
+  DCHECK(root_window);
+
+  auto* cursor_client = client::GetCursorClient(root_window);
+  if (cursor_client) {
+    cursor_client->HideCursor();
+    cursor_client->LockCursor();
+  }
+}
+
+void WindowTreeHost::UnlockMouse(Window* window) {
+  Window* root_window = window->GetRootWindow();
+  DCHECK(root_window);
+
+  if (window->HasCapture())
+    window->ReleaseCapture();
+
+  auto* cursor_client = client::GetCursorClient(root_window);
+  if (cursor_client) {
+    cursor_client->UnlockCursor();
+    cursor_client->ShowCursor();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // WindowTreeHost, protected:
 
diff --git a/ui/aura/window_tree_host.h b/ui/aura/window_tree_host.h
index 6d78efa..dbfdcb0 100644
--- a/ui/aura/window_tree_host.h
+++ b/ui/aura/window_tree_host.h
@@ -253,6 +253,9 @@
   virtual std::unique_ptr<ScopedEnableUnadjustedMouseEvents>
   RequestUnadjustedMovement();
 
+  virtual void LockMouse(Window* window);
+  virtual void UnlockMouse(Window* window);
+
   bool holding_pointer_moves() const { return holding_pointer_moves_; }
 
  protected:
diff --git a/ui/aura/window_tree_host_platform.cc b/ui/aura/window_tree_host_platform.cc
index 74b8040..95ffa4fe 100644
--- a/ui/aura/window_tree_host_platform.cc
+++ b/ui/aura/window_tree_host_platform.cc
@@ -203,6 +203,11 @@
   NOTIMPLEMENTED();
 }
 
+void WindowTreeHostPlatform::LockMouse(Window* window) {
+  window->SetCapture();
+  WindowTreeHost::LockMouse(window);
+}
+
 void WindowTreeHostPlatform::OnBoundsChanged(const BoundsChange& change) {
   // It's possible this function may be called recursively. Only notify
   // observers on initial entry. This way observers can safely assume that
diff --git a/ui/aura/window_tree_host_platform.h b/ui/aura/window_tree_host_platform.h
index 7e436c9..2969ac7 100644
--- a/ui/aura/window_tree_host_platform.h
+++ b/ui/aura/window_tree_host_platform.h
@@ -48,6 +48,7 @@
   void MoveCursorToScreenLocationInPixels(
       const gfx::Point& location_in_pixels) override;
   void OnCursorVisibilityChangedNative(bool show) override;
+  void LockMouse(Window* window) override;
 
   ui::PlatformWindow* platform_window() { return platform_window_.get(); }
   const ui::PlatformWindow* platform_window() const {
diff --git a/ui/base/idle/idle_internal.cc b/ui/base/idle/idle_internal.cc
index 968d800..5a730a49 100644
--- a/ui/base/idle/idle_internal.cc
+++ b/ui/base/idle/idle_internal.cc
@@ -4,13 +4,11 @@
 
 #include "ui/base/idle/idle_internal.h"
 
-#include "base/no_destructor.h"
-
 namespace ui {
 
 absl::optional<IdleState>& IdleStateForTesting() {
-  static base::NoDestructor<absl::optional<IdleState>> idle_state;
-  return *idle_state;
+  static absl::optional<IdleState> idle_state;
+  return idle_state;
 }
 
 }  // namespace ui
diff --git a/ui/base/ime/chromeos/component_extension_ime_manager.cc b/ui/base/ime/chromeos/component_extension_ime_manager.cc
index b2abfe2..78950bd 100644
--- a/ui/base/ime/chromeos/component_extension_ime_manager.cc
+++ b/ui/base/ime/chromeos/component_extension_ime_manager.cc
@@ -9,7 +9,6 @@
 
 #include "base/command_line.h"
 #include "base/strings/string_util.h"
-#include "base/trace_event/trace_event.h"
 #include "ui/base/ime/chromeos/extension_ime_util.h"
 
 namespace chromeos {
@@ -76,21 +75,11 @@
 
 bool ComponentExtensionIMEManager::LoadComponentExtensionIME(
     Profile* profile,
-    const std::string& input_method_id,
-    std::set<std::string>* extension_loaded) {
-  TRACE_EVENT0("ime",
-               "ComponentExtensionIMEManager::LoadComponentExtensionIME");
+    const std::string& input_method_id) {
   ComponentExtensionIME ime;
   if (FindEngineEntry(input_method_id, &ime)) {
-    bool will_load = extension_loaded == nullptr;
-    if (!will_load &&
-        extension_loaded->find(ime.id) == extension_loaded->end()) {
-      extension_loaded->insert(ime.id);
-      will_load = true;
-    }
-    if (will_load)
-      delegate_->Load(profile, ime.id, ime.manifest, ime.path);
-    return will_load;
+    delegate_->Load(profile, ime.id, ime.manifest, ime.path);
+    return true;
   }
   return false;
 }
diff --git a/ui/base/ime/chromeos/component_extension_ime_manager.h b/ui/base/ime/chromeos/component_extension_ime_manager.h
index 65d4164..77d646a9 100644
--- a/ui/base/ime/chromeos/component_extension_ime_manager.h
+++ b/ui/base/ime/chromeos/component_extension_ime_manager.h
@@ -53,16 +53,11 @@
       std::unique_ptr<ComponentExtensionIMEManagerDelegate> delegate);
   virtual ~ComponentExtensionIMEManager();
 
-  // Loads the IME component extension for |input_method_id| if the extension Id
-  // is not in the |extension_loaded|. This function returns true once an
-  // corresponding IME extension will be loaded. This function is safe to call
-  // multiple times. Returns false if the corresponding component extension is
-  // already loaded or there is not any IME extension found for the
-  // |input_method_id|.
-  bool LoadComponentExtensionIME(
-      Profile* profile,
-      const std::string& input_method_id,
-      std::set<std::string>* extension_loaded = nullptr);
+  // Loads |input_method_id| component extension IME. This function returns true
+  // on success. This function is safe to call multiple times. Returns false if
+  // already corresponding component extension is loaded.
+  bool LoadComponentExtensionIME(Profile* profile,
+                                 const std::string& input_method_id);
 
   // Returns true if |input_method_id| is allowlisted component extension input
   // method.
diff --git a/ui/gfx/android/android_surface_control_compat.cc b/ui/gfx/android/android_surface_control_compat.cc
index d840a04..95ec1237 100644
--- a/ui/gfx/android/android_surface_control_compat.cc
+++ b/ui/gfx/android/android_surface_control_compat.cc
@@ -15,7 +15,6 @@
 #include "base/hash/md5_constexpr.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/system/sys_info.h"
 #include "base/trace_event/trace_event.h"
@@ -133,8 +132,8 @@
 struct SurfaceControlMethods {
  public:
   static const SurfaceControlMethods& Get() {
-    static const base::NoDestructor<SurfaceControlMethods> instance;
-    return *instance;
+    static const SurfaceControlMethods instance;
+    return instance;
   }
 
   SurfaceControlMethods() {
diff --git a/ui/gfx/font_render_params_mac.cc b/ui/gfx/font_render_params_mac.cc
index 071e786..03bf988c 100644
--- a/ui/gfx/font_render_params_mac.cc
+++ b/ui/gfx/font_render_params_mac.cc
@@ -5,7 +5,6 @@
 #include "ui/gfx/font_render_params.h"
 
 #include "base/macros.h"
-#include "base/no_destructor.h"
 #include "base/notreached.h"
 
 namespace gfx {
@@ -32,8 +31,8 @@
   if (family_out)
     NOTIMPLEMENTED();
   // TODO: Query the OS for font render settings instead of returning defaults.
-  static const base::NoDestructor<gfx::FontRenderParams> params(LoadDefaults());
-  return *params;
+  static const gfx::FontRenderParams params(LoadDefaults());
+  return params;
 }
 
 float GetFontRenderParamsDeviceScaleFactor() {
diff --git a/ui/gfx/font_render_params_skia.cc b/ui/gfx/font_render_params_skia.cc
index 01c9024..7e8edf7 100644
--- a/ui/gfx/font_render_params_skia.cc
+++ b/ui/gfx/font_render_params_skia.cc
@@ -5,7 +5,6 @@
 #include "ui/gfx/font_render_params.h"
 
 #include "base/macros.h"
-#include "base/no_destructor.h"
 #include "base/notreached.h"
 
 namespace gfx {
@@ -40,8 +39,8 @@
   if (family_out)
     NOTIMPLEMENTED();
   // Customized font rendering settings are not supported, only defaults.
-  static const base::NoDestructor<gfx::FontRenderParams> params(LoadDefaults());
-  return *params;
+  static const gfx::FontRenderParams params(LoadDefaults());
+  return params;
 }
 
 float GetFontRenderParamsDeviceScaleFactor() {
diff --git a/ui/gfx/mojom/swap_result.mojom b/ui/gfx/mojom/swap_result.mojom
index b73d4e43..f206fee 100644
--- a/ui/gfx/mojom/swap_result.mojom
+++ b/ui/gfx/mojom/swap_result.mojom
@@ -12,5 +12,6 @@
 enum SwapResult {
   ACK,
   FAILED,
+  SKIPPED,
   NAK_RECREATE_BUFFERS,
 };
diff --git a/ui/gfx/mojom/swap_result_mojom_traits.h b/ui/gfx/mojom/swap_result_mojom_traits.h
index ec531dc..245a5a3 100644
--- a/ui/gfx/mojom/swap_result_mojom_traits.h
+++ b/ui/gfx/mojom/swap_result_mojom_traits.h
@@ -19,6 +19,8 @@
         return gfx::mojom::SwapResult::ACK;
       case gfx::SwapResult::SWAP_FAILED:
         return gfx::mojom::SwapResult::FAILED;
+      case gfx::SwapResult::SWAP_SKIPPED:
+        return gfx::mojom::SwapResult::SKIPPED;
       case gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS:
         return gfx::mojom::SwapResult::NAK_RECREATE_BUFFERS;
     }
@@ -34,6 +36,9 @@
       case gfx::mojom::SwapResult::FAILED:
         *out = gfx::SwapResult::SWAP_FAILED;
         return true;
+      case gfx::mojom::SwapResult::SKIPPED:
+        *out = gfx::SwapResult::SWAP_SKIPPED;
+        return true;
       case gfx::mojom::SwapResult::NAK_RECREATE_BUFFERS:
         *out = gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS;
         return true;
diff --git a/ui/gfx/swap_result.h b/ui/gfx/swap_result.h
index 8d6bbc0a..39a62d5 100644
--- a/ui/gfx/swap_result.h
+++ b/ui/gfx/swap_result.h
@@ -18,6 +18,15 @@
 enum class SwapResult {
   SWAP_ACK,
   SWAP_FAILED,
+  // Typically, the Viz thread should decide whether to skip a swap based off
+  // the damage. In rare cases, however, the GPU main thread might skip the
+  // swap after the Viz thread requests it (e.g. the Viz thread might not know
+  // that the buffers are not fully initialized yet). For the purposes of
+  // metrics bookkeeping, we label this scenario as SWAP_SKIPPED and treat it
+  // much like we do a SWAP_FAILED (e.g. failed PresentationFeedback).
+  // TODO(https://crbug.com/1226090): Consider more explicit handling of
+  // SWAP_SKIPPED.
+  SWAP_SKIPPED,
   SWAP_NAK_RECREATE_BUFFERS,
   SWAP_RESULT_LAST = SWAP_NAK_RECREATE_BUFFERS,
 };
diff --git a/ui/gfx/win/rendering_window_manager.h b/ui/gfx/win/rendering_window_manager.h
index 0357d9d..53b2c93 100644
--- a/ui/gfx/win/rendering_window_manager.h
+++ b/ui/gfx/win/rendering_window_manager.h
@@ -9,11 +9,10 @@
 
 #include "base/containers/flat_map.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/no_destructor.h"
 #include "ui/gfx/gfx_export.h"
 
 namespace base {
-template <typename T>
-class NoDestructor;
 class SingleThreadTaskRunner;
 }
 
diff --git a/ui/gl/dcomp_surface_registry.cc b/ui/gl/dcomp_surface_registry.cc
index 2446013..899e47d 100644
--- a/ui/gl/dcomp_surface_registry.cc
+++ b/ui/gl/dcomp_surface_registry.cc
@@ -4,8 +4,6 @@
 
 #include "ui/gl/dcomp_surface_registry.h"
 
-#include "base/no_destructor.h"
-
 namespace gl {
 
 DCOMPSurfaceRegistry* DCOMPSurfaceRegistry::GetInstance() {
diff --git a/ui/gl/dcomp_surface_registry.h b/ui/gl/dcomp_surface_registry.h
index 308ba05..cf77519 100644
--- a/ui/gl/dcomp_surface_registry.h
+++ b/ui/gl/dcomp_surface_registry.h
@@ -6,15 +6,11 @@
 #define UI_GL_DCOMP_SURFACE_REGISTRY_H_
 
 #include "base/containers/flat_map.h"
+#include "base/no_destructor.h"
 #include "base/unguessable_token.h"
 #include "base/win/scoped_handle.h"
 #include "ui/gl/gl_export.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 namespace gl {
 
 // A registry in the GPU process for mapping an `UnguessableToken` to a
diff --git a/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc b/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc
index 49d848c..cf684a6 100644
--- a/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc
+++ b/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc
@@ -282,6 +282,7 @@
     case gfx::SwapResult::SWAP_ACK:
       SkiaGlRenderer::PostRenderFrameTask(std::move(result));
       break;
+    case gfx::SwapResult::SWAP_SKIPPED:
     case gfx::SwapResult::SWAP_FAILED:
       LOG(FATAL) << "Failed to swap buffers";
       break;
diff --git a/ui/ozone/demo/surfaceless_gl_renderer.cc b/ui/ozone/demo/surfaceless_gl_renderer.cc
index 94b7623..35150df8 100644
--- a/ui/ozone/demo/surfaceless_gl_renderer.cc
+++ b/ui/ozone/demo/surfaceless_gl_renderer.cc
@@ -304,6 +304,7 @@
           FROM_HERE, base::BindOnce(&SurfacelessGlRenderer::RenderFrame,
                                     weak_ptr_factory_.GetWeakPtr()));
       break;
+    case gfx::SwapResult::SWAP_SKIPPED:
     case gfx::SwapResult::SWAP_FAILED:
       LOG(FATAL) << "Failed to swap buffers";
       break;
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index 19c06510..7439ee7 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -282,25 +282,23 @@
   }
 
   const PlatformRuntimeProperties& GetPlatformRuntimeProperties() override {
-    static base::NoDestructor<OzonePlatform::PlatformRuntimeProperties>
-        properties;
+    static OzonePlatform::PlatformRuntimeProperties properties;
     if (connection_) {
-      properties->supports_server_side_window_decorations =
+      properties.supports_server_side_window_decorations =
           (connection_->xdg_decoration_manager_v1() != nullptr);
     }
-    return *properties;
+    return properties;
   }
 
   const InitializedHostProperties& GetInitializedHostProperties() override {
-    static base::NoDestructor<OzonePlatform::InitializedHostProperties>
-        properties;
+    static OzonePlatform::InitializedHostProperties properties;
     static bool initialized = false;
     if (!initialized) {
-      properties->supports_overlays =
+      properties.supports_overlays =
           ui::IsWaylandOverlayDelegationEnabled() && connection_->viewporter();
       initialized = true;
     }
-    return *properties;
+    return properties;
   }
 
   void AddInterfaces(mojo::BinderMap* binders) override {
diff --git a/ui/ozone/public/ozone_platform.cc b/ui/ozone/public/ozone_platform.cc
index bc110794..6a968f0 100644
--- a/ui/ozone/public/ozone_platform.cc
+++ b/ui/ozone/public/ozone_platform.cc
@@ -149,9 +149,8 @@
 
 const OzonePlatform::PlatformRuntimeProperties&
 OzonePlatform::GetPlatformRuntimeProperties() {
-  static const base::NoDestructor<OzonePlatform::PlatformRuntimeProperties>
-      properties;
-  return *properties;
+  static const OzonePlatform::PlatformRuntimeProperties properties;
+  return properties;
 }
 
 const OzonePlatform::InitializedHostProperties&
diff --git a/ui/views/accessibility/ax_aura_obj_cache.h b/ui/views/accessibility/ax_aura_obj_cache.h
index 646c739..bff702d 100644
--- a/ui/views/accessibility/ax_aura_obj_cache.h
+++ b/ui/views/accessibility/ax_aura_obj_cache.h
@@ -12,6 +12,7 @@
 #include <set>
 #include <vector>
 
+#include "base/no_destructor.h"
 #include "ui/accessibility/ax_enums.mojom-forward.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/aura/client/focus_change_observer.h"
@@ -21,11 +22,6 @@
 class Window;
 }  // namespace aura
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}  // namespace base
-
 namespace views {
 class AXAuraObjWrapper;
 class AXVirtualView;
diff --git a/ui/views/metadata/view_factory.h b/ui/views/metadata/view_factory.h
index 16de551..9dbf329 100644
--- a/ui/views/metadata/view_factory.h
+++ b/ui/views/metadata/view_factory.h
@@ -27,7 +27,8 @@
   BaseViewBuilderT& operator=(BaseViewBuilderT&&) = default;
   ~BaseViewBuilderT() override = default;
 
-  Builder& CopyAddressTo(ViewClass_** view_address) {
+  template <typename View>
+  Builder& CopyAddressTo(View** view_address) {
     *view_address = view_ ? view_.get() : root_view_;
     return *static_cast<Builder*>(this);
   }
diff --git a/ui/views/metadata/view_factory_unittest.cc b/ui/views/metadata/view_factory_unittest.cc
index 8d0f1d96..8432d9b 100644
--- a/ui/views/metadata/view_factory_unittest.cc
+++ b/ui/views/metadata/view_factory_unittest.cc
@@ -12,6 +12,7 @@
 #include "ui/views/border.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/scroll_view.h"
+#include "ui/views/layout/fill_layout.h"
 #include "ui/views/test/widget_test.h"
 #include "ui/views/view.h"
 #include "ui/views/view_class_properties.h"
@@ -24,6 +25,9 @@
   views::LabelButton* button = nullptr;
   views::LabelButton* scroll_button = nullptr;
   views::ScrollView* scroll_view = nullptr;
+  views::View* view_with_layout_manager = nullptr;
+  auto layout_manager = std::make_unique<views::FillLayout>();
+  auto* layout_manager_ptr = layout_manager.get();
   auto view = views::Builder<views::View>()
                   .CopyAddressTo(&parent)
                   .SetEnabled(false)
@@ -49,7 +53,10 @@
                            .SetContents(views::Builder<views::LabelButton>()
                                             .CopyAddressTo(&scroll_button)
                                             .SetText(u"ScrollTest"))
-                           .SetHeader(views::Builder<views::View>().SetID(2))})
+                           .SetHeader(views::Builder<views::View>().SetID(2)),
+                       views::Builder<views::LabelButton>()
+                           .CopyAddressTo(&view_with_layout_manager)
+                           .SetLayoutManager(std::move(layout_manager))})
                   .Build();
   ASSERT_TRUE(view.get());
   EXPECT_NE(parent, nullptr);
@@ -65,6 +72,9 @@
   EXPECT_NE(scroll_button, nullptr);
   EXPECT_EQ(scroll_button->GetText(), u"ScrollTest");
   EXPECT_EQ(scroll_button, scroll_view->contents());
+  EXPECT_NE(view_with_layout_manager, nullptr);
+  EXPECT_TRUE(views::IsViewClass<views::LabelButton>(view_with_layout_manager));
+  EXPECT_EQ(view_with_layout_manager->GetLayoutManager(), layout_manager_ptr);
 }
 
 TEST_F(ViewFactoryTest, TestViewBuilderOwnerships) {
diff --git a/ui/views/view.h b/ui/views/view.h
index df28d6bf..2c7cbb2 100644
--- a/ui/views/view.h
+++ b/ui/views/view.h
@@ -2145,6 +2145,17 @@
 };
 
 BEGIN_VIEW_BUILDER(VIEWS_EXPORT, View, BaseView)
+template <typename LayoutManager>
+BuilderT& SetLayoutManager(std::unique_ptr<LayoutManager> layout_manager) {
+  auto setter = std::make_unique<::views::internal::PropertySetter<
+      ViewClass_, std::unique_ptr<LayoutManager>,
+      decltype((static_cast<LayoutManager* (
+                    ViewClass_::*)(std::unique_ptr<LayoutManager>)>(
+          &ViewClass_::SetLayoutManager))),
+      &ViewClass_::SetLayoutManager>>(std::move(layout_manager));
+  ::views::internal::ViewBuilderCore::AddPropertySetter(std::move(setter));
+  return *static_cast<BuilderT*>(this);
+}
 VIEW_BUILDER_PROPERTY(std::unique_ptr<Background>, Background)
 VIEW_BUILDER_PROPERTY(std::unique_ptr<Border>, Border)
 VIEW_BUILDER_PROPERTY(gfx::Rect, BoundsRect)
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
index 25a925b..48a3540 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -749,6 +749,16 @@
                                            app_icon);
 }
 
+const gfx::ImageSkia* DesktopNativeWidgetAura::GetWindowIcon() {
+  NOTIMPLEMENTED_LOG_ONCE();
+  return nullptr;
+}
+
+const gfx::ImageSkia* DesktopNativeWidgetAura::GetWindowAppIcon() {
+  NOTIMPLEMENTED_LOG_ONCE();
+  return nullptr;
+}
+
 void DesktopNativeWidgetAura::InitModalType(ui::ModalType modal_type) {
   // 99% of the time, we should not be asked to create a
   // DesktopNativeWidgetAura that is modal. We only support window modal
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
index 4be3ddfb..9d847b51 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
@@ -138,6 +138,8 @@
   bool SetWindowTitle(const std::u16string& title) override;
   void SetWindowIcons(const gfx::ImageSkia& window_icon,
                       const gfx::ImageSkia& app_icon) override;
+  const gfx::ImageSkia* GetWindowIcon() override;
+  const gfx::ImageSkia* GetWindowAppIcon() override;
   void InitModalType(ui::ModalType modal_type) override;
   gfx::Rect GetWindowBoundsInScreen() const override;
   gfx::Rect GetClientAreaBoundsInScreen() const override;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index f6c41d83..4d986b92 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -66,6 +66,14 @@
 
 namespace {
 
+// While the mouse is locked we want the invisible mouse to stay within the
+// confines of the screen so we keep it in a capture region the size of the
+// screen.  However, on windows when the mouse hits the edge of the screen some
+// events trigger and cause strange issues to occur. To stop those events from
+// occurring we add a small border around the edge of the capture region.
+// This constant controls how many pixels wide that border is.
+const int kMouseCaptureRegionBorder = 5;
+
 gfx::Size GetExpandedWindowSize(bool is_translucent, gfx::Size size) {
   if (!is_translucent || !ui::win::IsAeroGlassEnabled())
     return size;
@@ -80,6 +88,24 @@
   rect->Inset(0, 0, vector.x(), vector.y());
 }
 
+// Updates the cursor clip region. Used for mouse locking.
+void UpdateMouseLockRegion(aura::Window* window, bool locked) {
+  if (!locked) {
+    ::ClipCursor(nullptr);
+    return;
+  }
+
+  RECT window_rect =
+      display::Screen::GetScreen()
+          ->DIPToScreenRectInWindow(window, window->GetBoundsInScreen())
+          .ToRECT();
+  window_rect.left += kMouseCaptureRegionBorder;
+  window_rect.right -= kMouseCaptureRegionBorder;
+  window_rect.top += kMouseCaptureRegionBorder;
+  window_rect.bottom -= kMouseCaptureRegionBorder;
+  ::ClipCursor(&window_rect);
+}
+
 }  // namespace
 
 DEFINE_UI_CLASS_PROPERTY_KEY(aura::Window*, kContentWindowForRootWindow, NULL)
@@ -663,6 +689,16 @@
   return message_handler_->RegisterUnadjustedMouseEvent();
 }
 
+void DesktopWindowTreeHostWin::LockMouse(aura::Window* window) {
+  UpdateMouseLockRegion(window, true /*locked*/);
+  WindowTreeHost::LockMouse(window);
+}
+
+void DesktopWindowTreeHostWin::UnlockMouse(aura::Window* window) {
+  UpdateMouseLockRegion(window, false /*locked*/);
+  WindowTreeHost::UnlockMouse(window);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // DesktopWindowTreeHostWin, wm::AnimationHost implementation:
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
index 07078cb..20bcac8 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
@@ -152,6 +152,8 @@
       const gfx::Point& location_in_pixels) override;
   std::unique_ptr<aura::ScopedEnableUnadjustedMouseEvents>
   RequestUnadjustedMovement() override;
+  void LockMouse(aura::Window* window) override;
+  void UnlockMouse(aura::Window* window) override;
 
   // Overridden from aura::client::AnimationHost
   void SetHostTransitionOffsets(
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc
index c5fb6a6..a8b4b0f 100644
--- a/ui/views/widget/native_widget_aura.cc
+++ b/ui/views/widget/native_widget_aura.cc
@@ -456,6 +456,14 @@
   AssignIconToAuraWindow(window_, window_icon, app_icon);
 }
 
+const gfx::ImageSkia* NativeWidgetAura::GetWindowIcon() {
+  return window_->GetProperty(aura::client::kWindowIconKey);
+}
+
+const gfx::ImageSkia* NativeWidgetAura::GetWindowAppIcon() {
+  return window_->GetProperty(aura::client::kAppIconKey);
+}
+
 void NativeWidgetAura::InitModalType(ui::ModalType modal_type) {
   if (modal_type != ui::MODAL_TYPE_NONE)
     window_->SetProperty(aura::client::kModalKey, modal_type);
diff --git a/ui/views/widget/native_widget_aura.h b/ui/views/widget/native_widget_aura.h
index 7779011..648b11d 100644
--- a/ui/views/widget/native_widget_aura.h
+++ b/ui/views/widget/native_widget_aura.h
@@ -97,6 +97,8 @@
   bool SetWindowTitle(const std::u16string& title) override;
   void SetWindowIcons(const gfx::ImageSkia& window_icon,
                       const gfx::ImageSkia& app_icon) override;
+  const gfx::ImageSkia* GetWindowIcon() override;
+  const gfx::ImageSkia* GetWindowAppIcon() override;
   void InitModalType(ui::ModalType modal_type) override;
   gfx::Rect GetWindowBoundsInScreen() const override;
   gfx::Rect GetClientAreaBoundsInScreen() const override;
diff --git a/ui/views/widget/native_widget_mac.h b/ui/views/widget/native_widget_mac.h
index b43425e..0b9c37a 100644
--- a/ui/views/widget/native_widget_mac.h
+++ b/ui/views/widget/native_widget_mac.h
@@ -130,6 +130,8 @@
   bool SetWindowTitle(const std::u16string& title) override;
   void SetWindowIcons(const gfx::ImageSkia& window_icon,
                       const gfx::ImageSkia& app_icon) override;
+  const gfx::ImageSkia* GetWindowIcon() override;
+  const gfx::ImageSkia* GetWindowAppIcon() override;
   void InitModalType(ui::ModalType modal_type) override;
   gfx::Rect GetWindowBoundsInScreen() const override;
   gfx::Rect GetClientAreaBoundsInScreen() const override;
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index 668a7705..9451441 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -413,6 +413,15 @@
   // Everything happens upon show.
 }
 
+const gfx::ImageSkia* NativeWidgetMac::GetWindowIcon() {
+  NOTIMPLEMENTED_LOG_ONCE();
+  return nullptr;
+}
+const gfx::ImageSkia* NativeWidgetMac::GetWindowAppIcon() {
+  NOTIMPLEMENTED_LOG_ONCE();
+  return nullptr;
+}
+
 gfx::Rect NativeWidgetMac::GetWindowBoundsInScreen() const {
   return ns_window_host_ ? ns_window_host_->GetWindowBoundsInScreen()
                          : gfx::Rect();
diff --git a/ui/views/widget/native_widget_private.h b/ui/views/widget/native_widget_private.h
index 8897a13..bb8c522 100644
--- a/ui/views/widget/native_widget_private.h
+++ b/ui/views/widget/native_widget_private.h
@@ -160,6 +160,8 @@
   // app switching UI.
   virtual void SetWindowIcons(const gfx::ImageSkia& window_icon,
                               const gfx::ImageSkia& app_icon) = 0;
+  virtual const gfx::ImageSkia* GetWindowIcon() = 0;
+  virtual const gfx::ImageSkia* GetWindowAppIcon() = 0;
 
   // Initializes the modal type of the window to |modal_type|. Called from
   // NativeWidgetDelegate::OnNativeWidgetCreated() before the widget is
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index fc606d7..a742d7d 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -956,11 +956,32 @@
 void Widget::UpdateWindowIcon() {
   if (non_client_view_)
     non_client_view_->UpdateWindowIcon();
-  native_widget_->SetWindowIcons(
-      GetImageSkiaFromImageModel(widget_delegate_->GetWindowIcon(),
-                                 GetNativeTheme()),
-      GetImageSkiaFromImageModel(widget_delegate_->GetWindowAppIcon(),
-                                 GetNativeTheme()));
+
+  gfx::ImageSkia window_icon = GetImageSkiaFromImageModel(
+      widget_delegate_->GetWindowIcon(), GetNativeTheme());
+
+  // In general, icon information is read from a |widget_delegate_| and then
+  // passed to |native_widget_|. On ChromeOS, for lacros-chrome to support the
+  // initial window state as minimized state, a valid icon is added to
+  // |native_widget_| earlier stage of widget initialization. See
+  // https://crbug.com/1189981. As only lacros-chrome on ChromeOS supports this
+  // behavior other overrides of |native_widget_| will always have no icon
+  // information. This is also true for |app_icon| referred below.
+  if (window_icon.isNull()) {
+    const gfx::ImageSkia* icon = native_widget_->GetWindowIcon();
+    if (icon && !icon->isNull())
+      window_icon = *icon;
+  }
+
+  gfx::ImageSkia app_icon = GetImageSkiaFromImageModel(
+      widget_delegate_->GetWindowAppIcon(), GetNativeTheme());
+  if (app_icon.isNull()) {
+    const gfx::ImageSkia* icon = native_widget_->GetWindowAppIcon();
+    if (icon && !icon->isNull())
+      app_icon = *icon;
+  }
+
+  native_widget_->SetWindowIcons(window_icon, app_icon);
 }
 
 FocusTraversable* Widget::GetFocusTraversable() {
diff --git a/url/url_idna_icu.cc b/url/url_idna_icu.cc
index 48e53a9..c5fcc6f 100644
--- a/url/url_idna_icu.cc
+++ b/url/url_idna_icu.cc
@@ -11,7 +11,6 @@
 #include <ostream>
 
 #include "base/check_op.h"
-#include "base/no_destructor.h"
 #include "third_party/icu/source/common/unicode/uidna.h"
 #include "third_party/icu/source/common/unicode/utypes.h"
 #include "url/url_canon_icu.h"
@@ -19,10 +18,8 @@
 
 namespace url {
 
-namespace {
-
-// A wrapper to use base::NoDestructor with ICU's UIDNA, a C pointer to
-// a UTS46/IDNA 2008 handling object opened with uidna_openUTS46().
+// Use UIDNA, a C pointer to a UTS46/IDNA 2008 handling object opened with
+// uidna_openUTS46().
 //
 // We use UTS46 with BiDiCheck to migrate from IDNA 2003 (with unassigned
 // code points allowed) to IDNA 2008 with
@@ -42,12 +39,12 @@
 // http://goo.gl/3XBhqw ).
 // See http://http://unicode.org/reports/tr46/ and references therein
 // for more details.
-struct UIDNAWrapper {
-  UIDNAWrapper() {
+UIDNA* GetUIDNA() {
+  static UIDNA* uidna = [] {
     UErrorCode err = U_ZERO_ERROR;
     // TODO(jungshik): Change options as different parties (browsers,
     // registrars, search engines) converge toward a consensus.
-    value = uidna_openUTS46(UIDNA_CHECK_BIDI, &err);
+    UIDNA* value = uidna_openUTS46(UIDNA_CHECK_BIDI, &err);
     if (U_FAILURE(err)) {
       CHECK(false) << "failed to open UTS46 data with error: "
                    << u_errorName(err)
@@ -56,16 +53,9 @@
                    << "tables for libicu. See https://crbug.com/778929.";
       value = nullptr;
     }
-  }
-
-  UIDNA* value;
-};
-
-}  // namespace
-
-UIDNA* GetUIDNA() {
-  static base::NoDestructor<UIDNAWrapper> uidna_wrapper;
-  return uidna_wrapper->value;
+    return value;
+  }();
+  return uidna;
 }
 
 // Converts the Unicode input representing a hostname to ASCII using IDN rules.
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestServiceTest.java b/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestServiceTest.java
index 1697d55..4d700aa 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestServiceTest.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestServiceTest.java
@@ -80,6 +80,10 @@
 
         ShadowPaymentFeatureList.setFeatureEnabled(
                 PaymentFeatureList.WEB_PAYMENTS_SINGLE_APP_UI_SKIP, true);
+        ShadowPaymentFeatureList.setFeatureEnabled(
+                PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION, true);
+        ShadowPaymentFeatureList.setFeatureEnabled(
+                PaymentFeatureList.WEB_PAYMENTS_EXPERIMENTAL_FEATURES, true);
         PaymentRequestService.resetShowingPaymentRequestForTest();
         PaymentAppService.getInstance().resetForTest();
 
diff --git a/weblayer/browser/media/local_presentation_manager_factory.h b/weblayer/browser/media/local_presentation_manager_factory.h
index 6002bb3..a426f44 100644
--- a/weblayer/browser/media/local_presentation_manager_factory.h
+++ b/weblayer/browser/media/local_presentation_manager_factory.h
@@ -5,13 +5,9 @@
 #ifndef WEBLAYER_BROWSER_MEDIA_LOCAL_PRESENTATION_MANAGER_FACTORY_H_
 #define WEBLAYER_BROWSER_MEDIA_LOCAL_PRESENTATION_MANAGER_FACTORY_H_
 
+#include "base/no_destructor.h"
 #include "components/media_router/browser/presentation/local_presentation_manager_factory.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}
-
 namespace content {
 class BrowserContext;
 }
diff --git a/weblayer/browser/media/media_router_factory.h b/weblayer/browser/media/media_router_factory.h
index f66bd5d..030a29f 100644
--- a/weblayer/browser/media/media_router_factory.h
+++ b/weblayer/browser/media/media_router_factory.h
@@ -5,13 +5,9 @@
 #ifndef WEBLAYER_BROWSER_MEDIA_MEDIA_ROUTER_FACTORY_H_
 #define WEBLAYER_BROWSER_MEDIA_MEDIA_ROUTER_FACTORY_H_
 
+#include "base/no_destructor.h"
 #include "components/media_router/browser/media_router_factory.h"
 
-namespace base {
-template <typename T>
-class NoDestructor;
-}
-
 namespace content {
 class BrowserContext;
 }
diff --git a/weblayer/shell/app/shell_main_params.cc b/weblayer/shell/app/shell_main_params.cc
index db4805e..0d6de8d 100644
--- a/weblayer/shell/app/shell_main_params.cc
+++ b/weblayer/shell/app/shell_main_params.cc
@@ -99,9 +99,9 @@
 }  // namespace
 
 MainParams CreateMainParams() {
-  static const base::NoDestructor<MainDelegateImpl> weblayer_delegate;
+  static MainDelegateImpl weblayer_delegate;
   MainParams params;
-  params.delegate = const_cast<MainDelegateImpl*>(&(*weblayer_delegate));
+  params.delegate = &weblayer_delegate;
 
   base::PathService::Get(base::DIR_EXE, &params.log_filename);
   params.log_filename = params.log_filename.AppendASCII("weblayer_shell.log");